我有一个包装脚本,它可以执行一些工作,然后将原始参数传递给另一个工具:
#!/bin/bash
# ...
other_tool -a -b "$@"
除非“其他工具”在子 shell 中运行,否则这可以正常工作:
#!/bin/bash
# ...
bash -c "other_tool -a -b $@"
如果我像这样调用包装器脚本:
wrapper.sh -x "blah blup"
然后,只有第一个原始参数 (-x) 被传递给“other_tool”。实际上,我没有创建子 shell,而是将原始参数传递给 Android 手机上的 shell,这应该没有什么区别:
#!/bin/bash
# ...
adb sh -c "other_tool -a -b $@"
答案1
Bash 的printf
命令具有对字符串进行引用/转义/其他操作的功能,因此只要父 shell 和子 shell 实际上都是 bash,就可以这样工作:
[编辑:正如 siegi 在评论中指出的那样,如果你以显而易见的方式执行此操作,则在没有提供任何参数时会出现问题,它表现得好像实际上只有一个空参数。我在下面添加了一个解决方法,用 包装格式字符串${1+}
,其中仅包含格式字符串如果第一个参数已定义。这有点像临时解决办法,但确实有效。]
#!/bin/bash
quoted_args="$(printf "${1+ %q}" "$@")" # Note: this will have a leading space before the first arg
# echo "Quoted args:$quoted_args" # Uncomment this to see what it's doing
bash -c "other_tool -a -b$quoted_args"
请注意,您也可以在一行中完成此操作:bash -c "other_tool -a -b$(printf "${1+ %q}" "$@")"
答案2
这些解决方案都不太管用。只需将 x/\ \ \"b\"/aaaaa/\'xxx\ yyyy\'/zz\"offf\" 作为参数传递,它们就会失败。
这是一个处理所有情况的简单包装器。请注意它如何对每个参数进行两次转义。
#!/usr/bin/env bash
declare -a ARGS
COUNT=$#
for ((INDEX=0; INDEX<COUNT; ++INDEX))
do
ARG="$(printf "%q" "$1")"
ARGS[INDEX]="$(printf "%q" "$ARG")"
shift
done
ls -l ${ARGS[*]}
答案3
它失败了,因为你正在将一个数组(位置参数)强制转换为一个字符串。"$@"
这很神奇,因为它将每个单独的参数作为正确引用的字符串提供给你。添加额外的文本会破坏这种魔力:"blah $@"
只是一个字符串。
这可能会让你更接近:
cmd="other_tool -a -b"
for parm in "$@"; do cmd+=" '$parm'"; done
adb sh -c "$cmd"
当然,任何包含单引号的参数都会引起麻烦。
答案4
好的,更多解释:
$ cat /tmp/test.sh
#! /bin/bash
echo '$@='"$@"
set -v # "debug"
sh -c 'echo $@' "$@" # gremlins steal the first option
sh -c 'echo $@' -- "$@" # We defend the option with two knifes
$ bash -e /tmp/test.sh first second third
$@=first second third
sh -c 'echo $@' "$@" # gremlins steal the first option
second third
sh -c 'echo $@' -- "$@" # We defend the option with two knifes
first second third