将命令行参数从bash脚本传递到逐字编程,并进行转义

时间:2015-02-27 16:04:47

标签: bash unix sh

所以,如果设置了--xyz标志,我有一个需要运行X的bash脚本,否则为Y.它还需要将所有命令行参数传递给它运行的程序。我的问题在于,我不能让它完全按照它们出现的方式传递参数,而是要么逃避不需要转义的东西,要么剥离应该转义的东西的转义。

这是我的测试脚本:

#!/bin/sh
echo "$@"
args=$@
BASEDIR=$(dirname $0)
while test $# -gt 0
do
    case "$1" in
        --xyz) echo ${args/--xyz/}
            exit $?
            ;;
    esac
    shift
done

echo "$args"
printf %s "$args"
echo
echo "$(printf %s "$args")"
echo "$(printf %q "$args")"

然后我这样测试:

[jearls@earlzpremb X]$ ./test.sh -c Foo\ Bar
-c Foo Bar
-c Foo Bar
-c Foo Bar
-c Foo Bar
-c\ Foo\ Bar
[jearls@earlzpremb X]$ echo -c Foo\ Bar
-c Foo Bar
[jearls@earlzpremb X]$ echo "-c Foo\ Bar"
-c Foo\ Bar

我需要它像最后一个命令。 Foo \ Bar保留了空间的逃逸,但是在-c之后没有逃离空间。

我无法控制传递给此脚本的参数,因此我无法进行双重转义或类似的操作。

我怎样才能达到传递命令行参数的目标,基本上是逐字逐句地转义到另一个程序?

2 个答案:

答案 0 :(得分:4)

您可以(轻松地)将参数 list 转换为字符串,使其可以转换回列表。幸运的是,你很少需要这样做。

我不清楚你的要求是什么,但这是一个简单的脚本,它似乎展示了你期望的行为:

# Function to execute if --xyz in argument list
X() { local -i i=0
      for arg in "$@"; do echo X$((++i)): "$arg"; done
}
# Function to call otherwise
Y() { local -i i=0
      for arg in "$@"; do echo Y$((++i)): "$arg"; done
}

# Select one or the other
x_or_y() {
  local arg
  # Scan arguments for --xyz (Not very precise)
  for arg in "$@"; do
    case "$arg" in
      --xyz) X "$@";
             return $?;;
      --)    break;;
    esac
  done
  # No --xyz found
  Y "$@"
}

# Demonstrate that arguments are passed verbatim:
$ x_or_y something "a b c" 'a backslash\inside'
Y1: something
Y2: a b c
Y3: a backslash\inside

$ x_or_y something --xyz "a b c" 'a backslash\inside'
X1: something
X2: --xyz
X3: a b c
X4: a backslash\inside

使用bash,如果需要删除--xyz参数,则可以使用数组。 (你也可以使用set --,但我认为数组更清晰了):

x_or_y() {
  local -i i=1
  local arg
  local todo=Y
  local args=()
  # Scan arguments for --xyz (Not very precise)
  for arg in "$@"; do
    ((++i))
    case "$arg" in
      --xyz) todo=X; break;;
      --)    args+=("$arg"); break;;
      *)     args+=("$arg");;
    esac
  done
  # Call with the args scanned and the rest of the args:
  "$todo" "${args[@]}" "${@:i}"
}

编辑:正如Etan Reisner所说,没有数组就可以做到这一点。该数组是一种有用的技术,但对于这个问题,您可以使用子字符串表达式来实现:

x_or_y() {
  local -i i
  local -i skip=0
  local todo=Y
  # Scan arguments for --xyz (Not very precise)
  for ((i=1; i<=$#; ++i)); do
    case "${!i}" in
      --xyz) todo=X; skip=1; break;;
      --)    break;;
    esac
  done
  # Call with the args scanned and the rest of the args:
  "$todo" "${@:1:i-1}" "${@:i+skip}"
}

答案 1 :(得分:1)

如果您需要从参数列表中删除--xyz参数,则以下内容应该有效(尽管我不知道这是否适用于/bin/sh)。

$ cat test.sh
#!/bin/sh

c() {
    printf 'argc: %s\n' "$#";
    printf 'argv: %s\n' "$@"
}

counter=1
for arg; do
    case $arg in
        --xyz)
            echo "Found $arg at position $counter"
            break
        ;;
    esac
    ((counter++))
done

echo "${@::$counter}" "${@:$((counter + 1))}"
c "${@::$counter}" "${@:$((counter + 1))}"
$ ./test -c Foo\ Bar -a Blah\ Quux
-c Foo Bar -a Blah Quux
argc: 4
argv: -c
argv: Foo Bar
argv: -a
argv: Blah Quux
$ ./test.sh -c Foo\ Bar --xyz -a Blah\ Quux
Found --xyz at position 3
-c Foo Bar -a Blah Quux
argc: 4
argv: -c
argv: Foo Bar
argv: -a
argv: Blah Quux