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

zla*_*ack 69 bash quotes

我有一个Bash脚本,我想在传递的参数中保留引号.

例:

./test.sh this is "some test"
Run Code Online (Sandbox Code Playgroud)

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

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

我该如何做到这一点?

Chr*_*odd 82

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

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

  • 我认为他的意思是引号是参数字符串的一部分,而不是bash语法的一部分. (4认同)
  • 这是最受欢迎的答案.虽然它本身是正确的,但它并没有真正解决这个问题. (4认同)
  • @qwertzguy不正确.在变量进入参数列表之前,将删除双引号. (2认同)
  • @TorstenBronger:由于这个问题完全不清楚,这就是我对OP真正想要的东西的最佳猜测. (2认同)
  • @Hofi:这些都是因为你需要正确引用`echo` - `xargs`和``$ @"`是无关紧要的.按照我的建议尝试`echo -ne"nospace\x0yes space\x0"` (2认同)

unh*_*mer 39

只有当你是剧本的唯一用户时,Yuku的回答才有效,而如果你主要对打印字符串感兴趣,并且期望它们没有引号引用,那么Dennis Williamson是很好的.

如果您想将所有参数作为一个大的quoted-string参数传递给or 的-c参数,则可以使用以下版本:bashsu

#!/bin/bash
C=''
for i in "$@"; do 
    i="${i//\\/\\\\}"
    C="$C \"${i//\"/\\\"}\""
done
bash -c "$C"
Run Code Online (Sandbox Code Playgroud)

所以,所有参数都会在它们周围引用一个引号(如果它之前不存在,则为无害),但是我们也会逃避任何转义,然后转义已经存在于参数中的任何引号(语法${var//from/to}执行全局子串替换) .

你当然只能引用已经有空格的东西,但这里没关系.像这样的脚本的一个实用程序是能够拥有一组预定义的环境变量(或者,使用su,将某些东西作为特定用户运行,而不会出现双重引用的所有内容).


更新:我最近有理由以POSIX方式执行此操作,最小分叉,导致此脚本(最后一个printf输出用于调用脚本的命令行,您应该能够复制粘贴以便调用它与等效参数):

#!/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"
Run Code Online (Sandbox Code Playgroud)

我切换到''自弹也解释之类的东西$,并!!""-quotes.

  • ℹ️ 与:`"${@@Q}"` 相同 (2认同)

Pau*_*ce. 33

如果可以安全地假设包含空格的参数必须已经(并且应该)引用,那么您可以像这样添加它们:

#!/bin/bash
whitespace="[[:space:]]"
for i in "$@"
do
    if [[ $i =~ $whitespace ]]
    then
        i=\"$i\"
    fi
    echo "$i"
done
Run Code Online (Sandbox Code Playgroud)

这是一个示例运行:

$ ./argtest abc def "ghi jkl" $'mno\tpqr' $'stu\nvwx'
abc
def
"ghi jkl"
"mno    pqr"
"stu
vwx"
Run Code Online (Sandbox Code Playgroud)

您还可以使用- 和- - 在双引号或单引号内插入文字制表符和换行符,而不是在其中使用转义符.CtrlV TabCtrlV CtrlJ$'...'

关于在Bash中插入字符的注意事项:如果您在Bash中使用Vi键绑定(set -o vi)(Emacs是默认值 - set -o emacs),则需要处于插入模式才能插入字符.在Emacs模式下,您始终处于插入模式.

  • @TonyBarganski:在将参数传递给接收参数的程序之前,它们会删除引号。无法判断参数上是否有_outer_引号。 (2认同)

Tom*_*ale 25

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

1. Shell参数扩展:${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'
Run Code Online (Sandbox Code Playgroud)

2. printf %q "$quote-me"

printf支持内部引用.该手册的入门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
$
Run Code Online (Sandbox Code Playgroud)

请注意,如果向人类显示引用文本,第二种方式会更清晰.

相关:对于bash,POSIX sh和zsh:使用单引号而不是反斜杠引用字符串

  • 我发现 `${*@Q}` 对于将所有命令行参数传递给后续 shell 调用非常有用,在我的例子中,由于对更改组的 `sg` 调用。感谢您的回答! (4认同)
  • 第一种方法`expand-q`有一个错误:`$ {i @ Q}`应该是`$ {i:Q}`否则它会失败,并带有`bash:$ {i @ Q}:替代错误` (2认同)
  • @arielf 什么版本的`bash`?它的工作原理如“$BASH_VERSION 4.4.19(1)-release”所示。你正在做的可能是 `${parameter:offset}` 的副作用,其中 `offset` 是 `0`,(可能是因为 `Q` 的 `atoi()` 给出了 `0`)。 (2认同)
  • 那可能是原因。我使用的是Ubuntu 16.04.4(LTS)。该系统上最新的bash是`4.3.48(1)-release` (2认同)

Gar*_*ver 8

就像汤姆黑尔说的那样,一种方法是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}"
Run Code Online (Sandbox Code Playgroud)

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}"
Run Code Online (Sandbox Code Playgroud)

receiver.sh

#!/bin/bash
for arg in "$@"; do
  echo "$arg"
done
Run Code Online (Sandbox Code Playgroud)

用法示例:

$ ./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
Run Code Online (Sandbox Code Playgroud)


isa*_*ndi 8

我需要这个将所有参数转发给另一个解释器.最适合我的是:

bash -c "$(printf ' %q' "$@")"
Run Code Online (Sandbox Code Playgroud)

示例(当命名为forward.sh时):

$ ./forward.sh echo "3 4"
3 4
$ ./forward.sh bash -c "bash -c 'echo 3'"
3
Run Code Online (Sandbox Code Playgroud)

(当然我使用的实际脚本更复杂,涉及我的情况nohup和重定向等,但这是关键部分.)


Joh*_*udd 6

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.                               
Run Code Online (Sandbox Code Playgroud)


Fra*_*ace 6

只需使用:

"${@}"
Run Code Online (Sandbox Code Playgroud)

例如:

# 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
Run Code Online (Sandbox Code Playgroud)


Gus*_*ves 5

我的问题是相似的,并且我在这里使用了混合想法。

我们有一台带有PHP脚本的服务器,可以发送电子邮件。然后,我们有第二台服务器,它通过SSH连接到第一台服务器并执行它。

这两个服务器上的脚本名称相同,并且实际上都是通过bash脚本执行的。

在服务器1(本地)bash脚本上,我们只有:

/usr/bin/php /usr/local/myscript/myscript.php "$@"
Run Code Online (Sandbox Code Playgroud)

它位于/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[@]}"
Run Code Online (Sandbox Code Playgroud)

请注意,我"${CSMOPTS[@]}"习惯将options数组传递给远程服务器。

感谢您在此处发布的eveyone!它真的帮助了我!:)


Ran*_*ku' -18

只需在字符串周围使用单引号和双引号即可:

./test.sh this is '"some test"'
Run Code Online (Sandbox Code Playgroud)

所以单引号里面的双引号也被解释为字符串。

但我建议将整个字符串放在单引号之间:

 ./test.sh 'this is "some test" '
Run Code Online (Sandbox Code Playgroud)

为了了解 shell 正在做什么或者解释脚本中的参数,您可以编写一个像这样的小脚本:

#!/bin/bash

echo $@
echo "$@"
Run Code Online (Sandbox Code Playgroud)

然后您将看到并测试使用不同字符串调用脚本时会发生什么

  • 这怎么能成为公认的答案呢?这要求脚本的调用者知道他需要双引号,尽管这可以在脚本中处理而不改变调用者构建参数列表的方式 (60认同)
  • 如果您无法控制“./test.sh”,那么这是正确的答案,但如果您正在编写“./test.sh”,那么[Chris Dodd的答案](http://stackoverflow.com/ a/1669548/185101)是更好的解决方案。 (4认同)
  • 这里的人搞错了。这是如何被接受的答案。它将责任传递给调用者。 (2认同)