如何在Bash参数中保留引号?

时间:2009-11-03 16:53:43

标签: bash quotes

我有一个Bash脚本,我希望在传递的参数中保留引号。

示例:

./test.sh this is "some test"

然后我想使用这些参数,并重用它们,包括整个参数列表的引号和引号。

我尝试使用\"$@\",但删除了列表中的引号。

我如何做到这一点?

14 个答案:

答案 0 :(得分:76)

使用"$@"会将参数替换为列表,而不会在空格上重新划分它们(在调用shell脚本时它们被拆分一次),如果你只是想重新调整,这通常就是你想要的 - 将参数传递给另一个程序。

你想做什么,以什么方式不起作用?

答案 1 :(得分:38)

只有当你是剧本的唯一用户时,Yuku的答案才有效,而如果你主要对打印字符串感兴趣,并希望他们能够使用Dennis Williamson没有报价引号。

这里有一个版本,如果你想将所有参数作为一个大的引号字符串参数传递给-cbash的{​​{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)

有两种安全的方法可以做到这一点:

1。 Shell parameter expansion${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'

2。 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 -cemit_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 "$@"

然后,您将看到并测试,调用具有不同字符串的脚本时发生了什么