1

我想实现一个 bash 函数,将其参数作为命令运行,同时(可能是可选的)在之前打印命令。想想安装脚本或测试运行脚本。

只是使用

function run () {
    echo "Running $@"
    "$@"
}

不允许我将调用与run foo arg1 arg2and区分开来run foo "arg1 arg2",因此我需要正确转义参数。

到目前为止我最好的镜头是

function run () {
    echo -n "Running"
    printf " %q" "$@"
    echo
    "$@"
}

哪个有效:

$ run echo "one_argument" "second argument" argument\"with\'quotes
Running echo one_argument second\ argument argument\"with\'quotes
one_argument second argument argument"with'quotes

但不是很优雅。我怎样才能实现输出

$ run echo "one_argument" "second argument" argument\"with\'quotes
Running echo one_argument "second argument" "argument\"with'quotes"
one_argument second argument argument"with'quotes

即我怎样才能printf在需要引用的参数周围加上引号,并在其中正确地转义引号,以便输出可以正确地复制'n'粘贴?

4

2 回答 2

3

这将引用所有内容:

run() {
    printf "Running:"
    for arg; do 
        printf ' "%s"' "${arg//\"/\\\"}"
    done
    echo
    "$@"
}
run echo "one_argument" "second argument" argument\"with\'quotes
Running: "echo" "one_argument" "second argument" "argument\"with'quotes"
one_argument second argument argument"with'quotes

此版本仅引用包含双引号或空格的参数:

run() {
    local fmt arg
    printf "Running:"
    for arg; do
        [[ $arg == *[\"[:space:]]* ]] && fmt=' "%s"' || fmt=" %s" 
        printf "$fmt" "${arg//\"/\\\"}"
    done
    echo
    "$@"
}
run echo "one_argument" "second argument" argument\"with\'quotes
Running: echo one_argument "second argument" "argument\"with'quotes"
one_argument second argument argument"with'quotes
于 2013-07-14T10:53:21.830 回答
2

我不认为你想要什么有一个优雅的解决方案,因为在任何命令看到它之前就"$@"已经处理了。bash您必须手动重新构建命令行:

#!/bin/bash

function run() {
  echo -n "Running:"
  for arg in "$@"; do
    arg="$(sed 's/"/\\&/g' <<<$arg)"
    [[ $arg =~ [[:space:]\\\'] ]] && arg=\"arg\"
    echo -n " $arg"
  done
  echo ""

  "$@"
}

run "$@"

输出:

$ ./test.sh echo arg1 "arg 2" "arg3\"with'other\'\nstuff"
Running: echo arg1 "arg 2" "arg3\"with'other\'\nstuff"
arg1 arg 2 arg3"with'other\'\nstuff

请注意,在某些极端情况下,您将无法获得准确的输入命令行。当您传递bash在传递它们之前扩展的参数时会发生这种情况,例如:

$ ./test.sh echo foo'bar'baz
Running: echo foobarbaz
foobarbaz
$ ./test.sh echo "foo\\bar"
Running: echo "foo\bar"
foobar
于 2013-07-14T09:55:59.020 回答