如何将bash脚本参数传递给子Shell


13

我有一个包装器脚本,可以完成一些工作,然后将原始参数传递给另一个工具:

#!/bin/bash
# ...
other_tool -a -b "$@"

除非“其他工具”在子shell中运行,否则此方法工作正常:

#!/bin/bash
# ...
bash -c "other_tool -a -b $@"

如果我这样调用包装器脚本:

wrapper.sh -x "blah blup"

然后,仅将第一个原始参数(-x)传递给“ other_tool”。实际上,我不创建子外壳,而是将原始参数传递给Android手机上的外壳,这不会有任何区别:

#!/bin/bash
# ...
adb sh -c "other_tool -a -b $@"

Answers:


14

Bash的printf命令具有将引号/转义/不管什么字符串的功能,因此,只要父级和子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}" "$@")"


谢谢!这对我来说非常有效。
DribblzAroundU82 '19

当没有传递任何参数时(它传递一个空参数而不是无参数),这将无法正常工作。
siegi

1
@siegi好收获;我为此添加了一种解决方法。
Gordon Davisson

4

这些解决方案都无法正常工作。只需传递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[*]}

1
如果您尝试将$ @作为最终引用的字符串参数(例如'/ bin / foo'“ $ @”'--opt')的一部分进行连接,并发现它并没有按您期望的那样输出,它也很有用。
jrg 2014年

1
哦,天哪。一直在努力解决这个问题,似乎没有任何效果。这解决了。考虑到这个问题很多,如果以某种方式内置bash来调用两次转义,那将是很好的。
MooGoo

2

更改$@$*。我做了一个小型的本地测试,它对我而言有效。

#!/bin/sh
bash -c "echo $*"
bash -c "echo $@"

另存为test.sh使其可执行

$ ./test.sh foo bar
foo bar
foo

如您所见,$*和之间有微妙的区别$@。参见例如http://ss64.com/bash/syntax-parameters.html


对于注释中的后续问题:您需要转义例如空格“两次”,以将带有分隔符的字符串作为组合参数传递给字符串,例如,将其test.sh修改为wc包装器:

#!/bin/sh
bash -c "wc $*"

这有效:

$ touch test\ file
$ ./test.sh -l "test\ file"
0 test file

但:

$ ./test.sh -l "test file"
wc: test: No such file or directory
wc: file: No such file or directory
0 total

不幸的是,这也不起作用。如果您这样调用包装器:wrapper.sh -x "blah blup"那么子外壳将获得三个参数(-x,blah,blup),而不是两个参数(-x,“ blah blup”)
拉尔夫·霍利

如果要保留空格分隔符(即),则需要“两次转义”该参数"Blah\ blup"。试试看,看看是否可行。
丹尼尔·安德森

2
看起来似乎可行,但是,这需要wrapper.sh的调用者添加转义符,而我正在寻找一种透明的解决方案。
拉尔夫·霍利2012年

2

之所以失败,是因为您将数组(位置参数)强制转换为字符串。"$@"之所以神奇,是因为它为您提供了每个单独的参数,并将其作为正确引用的字符串。添加其他文本打破了魔咒:"blah $@"只是一个字符串。

这可以使您更接近:

cmd="other_tool -a -b"
for parm in "$@"; do cmd+=" '$parm'"; done
adb sh -c "$cmd"

当然,任何包含单引号的参数都会引起麻烦。


2

好的,更多说明:

$ 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

1

我尝试了许多不同的解决方案,例如Greg(又名GreyCat)的Wiki上的BashFAQ / 096是一个很好的资源,包括背景信息和替代方法。总的来说,我发现以下两项是最易读的(在工作中):


从Bash 4.4开始(据我从NEWS得知),可以像这样使用参数扩展 @Q

adb sh -c "other_tool -a -b ${*@Q}"

请注意,我$*在这里使用而不是$@因为您希望"other_tool -a -b ${*@Q}"成为一个单个字符串,而不是每个传递的参数一个字符串。

如果要对bash数组变量执行相同操作,则需要语法${ARRAY[*]@Q}(在引号内)。


如果您没有可用的Bash 4.4或更高版本,或者不确定,这是我的首选解决方案:

function escapeBashArgs() {
    local arg separator=""
    for arg
    do
        printf "%s%q" "$separator" "$arg"
        separator=" "
    done
}

adb sh -c "other_tool -a -b $(escapeBashArgs "$@")"

请注意,此处您需要使用"$@"而不是$@"$*"或,$*因为您不希望在参数内进行单词拆分,因此无法使用不带引号的变体,并且您希望保留参数的数量,因此"$*"无法使用,因为它将连接在一起一个字符串的所有参数。然后,该函数以单个字符串返回所有参数。

如果您不关心第一个参数前面的额外空间,则可以将printf格式字符串更改为" %q"并删除该separator变量。或者,您可以使用Gordon Davissons answer中的单线


该解决方案适用于我可能提出的所有情况,尤其是:

  • 无参数:escapeBashArgs
  • 空参数:escapeBashArgs "" ""'' ''
  • 仅带有空格的参数:escapeBashArgs " " " "' ' ' '\ \ \ \ \此网站渲染器占用了最后一个空格
  • 具有特殊间距和换行符的参数:escapeBashArgs "a b" c\ d "arg with newline"'a b' 'c d' $'arg with\nnewline'a\ \ \ \ \ \ b c\ d $'arg with\nnewline'换行符位于with和之间newline,其他位置是由于该站点上的换行而引起的
  • 具有特殊字符的参数:escapeBashArgs '$"'\''({:})''$"'\''({:})'\$\"\'\(\{:\}\)
  • 来自jcayzacs的示例答案escapeBashArgs x/\ \ \"b\"/aaaaa/\'xxx\ yyyy\'/zz\"offf\"'x/ "b"/aaaaa/'\''xxx yyyy'\''/zz"offf"'x/\ \ \"b\"/aaaaa/\'xxx\ yyyy\'/zz\"offf\"

(使用GNU bash 5.0.3(1)-发行版进行了测试。)


-2
#! /bin/bash
sh -c 'other_tool -a -b "$@"' -- "$@"

3
欢迎来到超级用户!我们正在寻找能提供一些解释和上下文的长答案。不要仅仅给出一个答案。解释为什么你的答案是正确的,最好是引用。不包含说明的答案可能会被删除。
G-Man说'恢复莫妮卡'
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.