Linux 在 bash 参数中保留引号

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10835933/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 06:35:53  来源:igfitidea点击:

Preserve Quotes in bash arguments

linuxbash

提问by aus

I am making a bash script that will print and pass complex arguments to another external program.

我正在制作一个 bash 脚本,它将打印复杂的参数并将其传递给另一个外部程序。

./script -m root@hostname,root@hostname -o -q -- 'uptime ; uname -a'

How do I print the raw arguments as such:

如何打印原始参数:

-m root@hostname,root@hostname -o -q -- 'uptime ; uname -a'

Using $@and $*removes the single quotes around uptime ; uname -awhich could cause undesired results. My script does not need to parse each argument. I just need to print / log the argument string and pass them to another program exactly how they are given.

使用$@$*删除uptime ; uname -a可能会导致意外结果的单引号。我的脚本不需要解析每个参数。我只需要打印/记录参数字符串并将它们完全按照给出的方式传递给另一个程序。

I know I can escape the quotes with something like "'uptime ; uname -a'"but I cannot guarantee the user will do that.

我知道我可以用类似的东西来转义引号, "'uptime ; uname -a'"但我不能保证用户会这样做。

采纳答案by Gordon Davisson

The quotes are removed before the arguments are passed to your script, so it's too late to preserve them. What you can do is preserve their effect when passing the arguments to the inner command, and reconstruct an equivalent quoted/escaped version of the arguments for printing.

在将参数传递给脚本之前删除引号,因此保留它们为时已晚。您可以做的是在将参数传递给内部命令时保留它们的效果,并重建参数的等效引用/转义版本以进行打印。

For passing the arguments to the inner command "$@"-- with the double-quotes, $@ preserves the original word breaks, meaning that the inner command receives exactly the same argument list that your script did.

为了将参数传递给内部命令"$@"——使用双引号,$@ 保留了原来的分词符,这意味着内部命令接收的参数列表与您的脚本接收的参数列表完全相同。

For printing, you can use the %q format in bash's printf command to reconstruct the quoting. Note that this won't always reconstruct the original quoting, but will construct an equivalentquoted/escaped string. For example, if you passed the argument 'uptime ; uname -a'it might print uptime\ \;\ uname\ -aor "uptime ; uname -a"or any other equivalent (see @William Pursell's answer for similar examples).

对于打印,您可以在 bash 的 printf 命令中使用 %q 格式来重建引用。请注意,这不会总是重建原始引用,但会构造等效的引用/转义字符串。例如,如果您传递了参数,'uptime ; uname -a'它可能会打印uptime\ \;\ uname\ -a"uptime ; uname -a"任何其他等效项(有关类似示例,请参阅@William Pursell 的回答)。

Here's an example of using these:

这是使用这些的示例:

printf "Running command:"
printf " %q" innercmd "$@" # note the space before %q -- this inserts spaces between arguments
printf "\n"
innercmd "$@"

回答by William Pursell

If the user invokes your command as:

如果用户调用您的命令为:

./script 'foo'

the first argument given to the script is the string foowithout the quotes. There is no way for your script to differentiate between that and any of the other methods by which it could get fooas an argument (eg ./script $(echo foo)or ./script fooor ./script "foo"or ./script \f\o""''""o).

给脚本的第一个参数是foo不带引号的字符串。您的脚本无法区分它和它可以foo作为参数获取的任何其他方法(例如./script $(echo foo)or./script foo./script "foo"or ./script \f\o""''""o)。

回答by Paused until further notice.

If you want to print the argument list as close as possible to what the user probably entered:

如果您想打印尽可能接近用户可能输入的参数列表:

#!/bin/bash
chars='[ !"#$&()*,;<>?\^`{|}]'
for arg
do
    if [[ $arg == *\'* ]]
    then
        arg=\""$arg"\"
    elif [[ $arg == *$chars* ]]
    then
        arg="'$arg'"
    fi
    allargs+=("$arg")    # ${allargs[@]} is to be used only for printing
done
printf '%s\n' "${allargs[*]}"

It's not perfect. An argument like ''\''"'is more difficult to accommodate than is justified.

这并不完美。类似的论点''\''"'比合理的更难以容纳。

回答by Helder Pereira

As someone else already mentioned, when you access the arguments inside of your script, it's too late to know which arguments were quote when it was called. However, you can re-quote the arguments that contain spaces or other special characters that would need to be quoted to be passed as parameters.

正如其他人已经提到的那样,当您访问脚本中的参数时,要知道调用时引用了哪些参数为时已晚。但是,您可以重新引用包含空格或其他需要引用以作为参数传递的特殊字符的参数。

Here is a Bash implementation based on Python's shlex.quote(s)that does just that:

这是一个基于Python 的shlex.quote(s)Bash 实现,它就是这样做的:

function quote() {
  declare -a params
  for param; do
    if [[ -z "${param}" || "${param}" =~ [^A-Za-z0-9_@%+=:,./-] ]]; then
      params+=("'${param//\'/\'\"\'\"\'}'")
    else
      params+=("${param}")
    fi
  done
  echo "${params[*]}"
}

Your example slightly changed to show empty arguments:

您的示例略有更改以显示空参数:

$ quote -m root@hostname,root@hostname -o -q -- 'uptime ; uname -a' ''
-m root@hostname,root@hostname -o -q -- 'uptime ; uname -a' ''