为什么第一个方法会这样?

为什么第一个方法会这样?

我正在尝试调试我的 shell 脚本:

content_type='-H "Content-Type: application/json"'
curl $content_type -d "{"test": "test"}" example.com

我了解到这并没有达到我的预期。在此脚本中,传递给curl(由于)的参数$content_type是:

  1. -H
  2. "Content-Type:
  3. application/json"

而不是(我所期望的):

  1. -H
  2. Content-Type: application/json

我知道我可以编写如下脚本:

content_type="Content-Type: application/json"
curl -H "$content_type" -d "{"test": "test"}" example.com

它会以这种方式工作,但我想知道是否可以在单个变量中保存包含参数的多个空间。

对于第二个(工作)版本,如果我想排除内容类型,我需要删除两件事:-H$content_type

但是,我想知道是否可以将选项及其值放入单个实体中,以便排除/包含内容类型将导致删除/添加单个实体。

使用数组是不可移植的,因为 POSIX 中没有数组。

为什么第一个方法会这样?

原因是分词。当我们定义content_type如下时:

content_type='-H "Content-Type: application/json"'

它具有以下价值:

-H "Content-Type: application/json"

当我们引用不带引号的变量(即,$content_type而不是"$content_type")时,扩展值将成为分词的主题。从分词页面bash 手册:

shell 扫描未出现在双引号内的参数扩展、命令替换和算术扩展的结果以进行分词。

来自同一页面:

shell 将 的每个字符$IFS视为分隔符,并使用这些字符作为字段终止符将其他扩展的结果拆分为单词。如果 IFS 未设置,或其值恰好为 <space><tab><newline>,则默认...

因此,通过用作分隔-H "Content-Type: application/json"符来分割。<space><tab><newline>这给了我们:

-H
"Content-Type:
application/json"

答案1

标准 shell 中的标准数组式结构。它是位置参数。所以一个可能的解决方案是使用set -- …"$@"。在你的情况下你会这样做:

set -- -H "Content-Type: application/json"
curl "$@" -d "{"test": "test"}" example.com

请注意,这会限制您只能使用一个可用阵列。例如,您不能将一个用于curl,而另一个用于另一个程序。当然,它会破坏脚本的参数。

答案2

复制相关部分吉尔斯的回答

如何将命令存储在变量中?

“命令”可以表示三件事:命令名称(可执行文件的名称,带或不带完整路径,或函数名称,内置或别名),带参数的命令名称,或一段 shell 代码。因此有不同的方式将它们存储在变量中。

如果您有命令名称,只需存储它并像平常一样使用带有双引号的变量即可。

command_path="$1"
"$command_path" --option --message="hello world"

如果您有一个带参数的命令,则问题与上面的文件名列表相同:这是一个字符串列表,而不是一个字符串。您不能将参数填充到一个中间有空格的字符串中,因为如果这样做,您将无法区分作为参数一部分的空格和分隔参数的空格之间的区别。如果您的 shell 有数组,则可以使用它们。

cmd=(/path/to/executable --option --message="hello world" --)
cmd=("${cmd[@]}" "$file1" "$file2")
"${cmd[@]}"

如果您使用的 shell 没有数组怎么办?如果您不介意修改位置参数,您仍然可以使用它们。

set -- /path/to/executable --option --message="hello world" --
set -- "$@" "$file1" "$file2"
"$@"

如果您需要存储复杂的 shell 命令(例如重定向、管道等)怎么办?或者如果您不想修改位置参数?然后您可以构建一个包含该命令的字符串,并使用eval内置命令。

code='/path/to/executable --option --message="hello world" -- /path/to/file1 | grep "interesting stuff"'
eval "$code"

我建议任何人阅读全文吉尔斯的回答尽管。它包含大量有用的信息。

相关内容