我有一个Bash脚本,我希望在传递的参数中保留引号。
示例:
./test.sh this is "some test"
然后我想使用这些参数,并重用它们,包括整个参数列表的引号和引号。
我尝试使用\"$@\"
,但删除了列表中的引号。
我如何做到这一点?
答案 0 :(得分:76)
使用"$@"
会将参数替换为列表,而不会在空格上重新划分它们(在调用shell脚本时它们被拆分一次),如果你只是想重新调整,这通常就是你想要的 - 将参数传递给另一个程序。
你想做什么,以什么方式不起作用?
答案 1 :(得分:38)
这里有一个版本,如果你想将所有参数作为一个大的引号字符串参数传递给-c
或bash
的{{1}}参数,则可以使用这个版本:
su
所以,所有参数都会在它们周围引用一个引号(如果之前没有它就会无害,为此目的),但我们也逃避任何转义,然后转义任何引号已经在一个参数中(语法#!/bin/bash
C=''
for i in "$@"; do
i="${i//\\/\\\\}"
C="$C \"${i//\"/\\\"}\""
done
bash -c "$C"
进行全局子串替换)。
你当然只能引用已经有空格的东西,但这里不重要。像这样的脚本的一个实用程序是能够拥有一组预定义的环境变量(或者,使用su,以某个用户的身份运行东西,而不会引发一堆双引号)。
更新:我最近有理由以POSIX方式执行此操作,最小分叉,导致此脚本(最后一个printf输出用于调用脚本的命令行,您应该能够复制粘贴到命令用等效参数调用它:)
${var//from/to}
我切换到#!/bin/sh
C=''
for i in "$@"; do
case "$i" in
*\'*)
i=`printf "%s" "$i" | sed "s/'/'\"'\"'/g"`
;;
*) : ;;
esac
C="$C '$i'"
done
printf "$0%s\n" "$C"
,因为shell还会在''
中解释$
和!!
之类的内容。
答案 2 :(得分:32)
如果可以安全地假设包含空格的参数必须已经(并且应该)引用,那么您可以像这样添加它们:
#!/bin/bash
whitespace="[[:space:]]"
for i in "$@"
do
if [[ $i =~ $whitespace ]]
then
i=\"$i\"
fi
echo "$i"
done
以下是一个示例运行:
$ ./argtest abc def "ghi jkl" $'mno\tpqr' $'stu\nvwx'
abc
def
"ghi jkl"
"mno pqr"
"stu
vwx"
您还可以使用 Ctrl - V Tab 和 Ctrl <插入文字标签和换行符/ kbd> - V Ctrl - J 在双引号或单引号内,而不是在$'...'
内使用转义符。
关于在Bash中插入字符的说明:如果您在Bash中使用Vi键绑定(set -o vi
)(Emacs是默认值 - set -o emacs
),那么'' ll需要在插入模式中才能插入字符。在Emacs模式下,您始终处于插入模式。
答案 3 :(得分:21)
有两种安全的方法可以做到这一点:
${variable@Q}:
通过${variable@Q}
扩展变量时:
扩展是一个字符串,它是以可以重复用作输入的格式引用的参数值。
示例:
$ expand-q() { for i; do echo ${i@Q}; done; } # Same as for `i in "$@"`...
$ expand-q word "two words" 'new
> line' "single'quote" 'double"quote'
word
'two words'
$'new\nline'
'single'\''quote'
'double"quote'
printf %q "$quote-me"
printf
支持内部引用。 manual's entry for printf
说:
%q
使printf以可以作为shell输入重用的格式输出相应的参数。
示例:
$ cat test.sh
#!/bin/bash
printf "%q\n" "$@"
$
$ ./test.sh this is "some test" 'new
>line' "single'quote" 'double"quote'
this
is
some\ test
$'new\nline'
single\'quote
double\"quote
$
注意如果向人类显示引用文本,第二种方式会更清晰。
相关:对于bash,POSIX sh和zsh:Quote string with single quotes rather than backslashes
答案 4 :(得分:7)
我需要将所有参数转发给另一个解释器。 最适合我的是:
bash -c "$(printf ' %q' "$@")"
示例(当命名为forward.sh时):
$ ./forward.sh echo "3 4"
3 4
$ ./forward.sh bash -c "bash -c 'echo 3'"
3
(当然我使用的实际脚本更复杂,涉及我的案例nohup和重定向等,但这是关键部分。)
答案 5 :(得分:6)
就像Tom Hale所说,一种方法是printf
使用%q
引用 - 逃避。
例如:
<强> send_all_args.sh 强>
#!/bin/bash
if [ "$#" -lt 1 ]; then
quoted_args=""
else
quoted_args="$(printf " %q" "${@}")"
fi
bash -c "$( dirname "${BASH_SOURCE[0]}" )/receiver.sh${quoted_args}"
<强> send_fewer_args.sh 强>
#!/bin/bash
if [ "$#" -lt 2 ]; then
quoted_last_args=""
else
quoted_last_args="$(printf " %q" "${@:2}")"
fi
bash -c "$( dirname "${BASH_SOURCE[0]}" )/receiver.sh${quoted_last_args}"
<强> receiver.sh 强>
#!/bin/bash
for arg in "$@"; do
echo "$arg"
done
使用示例:
$ ./send_all_args.sh
$ ./send_all_args.sh a b
a
b
$ ./send_all_args.sh "a' b" 'c "e '
a' b
c "e
$ ./send_fewer_args.sh
$ ./send_fewer_args.sh a
$ ./send_fewer_args.sh a b
b
$ ./send_fewer_args.sh "a' b" 'c "e '
c "e
$ ./send_fewer_args.sh "a' b" 'c "e ' 'f " g'
c "e
f " g
答案 6 :(得分:5)
我的问题很相似,我在这里发布了混合的想法。
我们有一个带有发送电子邮件的PHP脚本的服务器。然后我们有第二台服务器通过SSH连接到第一台服务器并执行它。
两个服务器上的脚本名称相同,并且两者都是通过bash脚本实际执行的。
在服务器1(本地)bash脚本上我们只有:
/usr/bin/php /usr/local/myscript/myscript.php "$@"
它驻留在/usr/local/bin/myscript
上,由远程服务器调用。即使对于带空格的参数,它也能正常工作。
但是在远程服务器上我们不能使用相同的逻辑,因为第一台服务器不会收到来自"$@"
的引号。我使用了JohnMudd和Dennis Williamson的想法,用引号重新创建了选项和参数数组。我喜欢仅在项目中包含空格时添加转义引号的想法。
因此远程脚本以:
运行CSMOPTS=()
whitespace="[[:space:]]"
for i in "$@"
do
if [[ $i =~ $whitespace ]]
then
CSMOPTS+=(\"$i\")
else
CSMOPTS+=($i)
fi
done
/usr/bin/ssh "$USER@$SERVER" "/usr/local/bin/myscript ${CSMOPTS[@]}"
请注意,我使用"${CSMOPTS[@]}"
将options数组传递给远程服务器。
感谢发布在这里的eveyone!它真的帮助了我! :)
答案 7 :(得分:4)
更改了unhammer的示例以使用数组。
printargs() { printf "'%s' " "$@"; echo; }; # http://superuser.com/a/361133/126847
C=()
for i in "$@"; do
C+=("$i") # Need quotes here to append as a single array element.
done
printargs "${C[@]}" # Pass array to a program as a list of arguments.
答案 8 :(得分:3)
引号由bash解释,不存储在命令行参数或变量值中。
如果你想使用带引号的参数,你必须在每次使用它们时引用它们:
val="$3"
echo "Hello World" > "$val"
答案 9 :(得分:1)
只需使用:
"${@}"
例如:
# cat t2.sh
for I in "${@}"
do
echo "Param: $I"
done
# cat t1.sh
./t2.sh "${@}"
# ./t1.sh "This is a test" "This is another line" a b "and also c"
Param: This is a test
Param: This is another line
Param: a
Param: b
Param: and also c
答案 10 :(得分:0)
是的,似乎不可能保留引号,但对于我正在处理它的问题并非必要。
我有一个bash函数,它会递归搜索文件夹,grep搜索一个字符串,问题是传递一个包含空格的字符串,例如&#34;找到这个字符串&#34;。将此传递给bash脚本然后将基本参数$ n并将其传递给grep,这使得grep相信这些是不同的参数。我解决这个问题的方法是,当你引用bash来调用函数时,它将引号中的项分组为一个参数。我只需要用引号装饰该参数并将其传递给grep命令。
如果您知道在bash中接收的参数需要下一步的引用,您可以使用引号进行装饰。
答案 11 :(得分:0)
正如Gary S. Weaver在其源代码技巧中所示,诀窍是使用参数“ -c”调用bash,然后引用下一个。
例如
bash -c "<your program> <parameters>"
或
docker exec -it <my docker> bash -c "$SCRIPT $quoted_args"
答案 12 :(得分:0)
如果您需要将所有参数从另一种编程语言传递给bash (例如,如果您想执行bash -c
或emit_bash_code | bash
),请使用以下命令:
'\''
转义所有带有单引号的字符。 abc'def
的自变量将因此转换为'abc'\''def'
。字符'\''
的解释如下:已经存在的引用以第一个第一引号终止,然后转义的单引号\'
出现,然后开始新的引用。
答案 13 :(得分:-10)
只需在带有双引号的字符串周围使用单引号:
./test.sh this is '"some test"'
因此单引号内的双引号也被解释为字符串。
但我建议将整个字符串放在单引号之间:
./test.sh 'this is "some test" '
为了理解shell正在做什么或者解释脚本中的参数,你可以写一个像这样的小脚本:
#!/bin/bash
echo $@
echo "$@"
然后,您将看到并测试,调用具有不同字符串的脚本时发生了什么