U. *_*ndl 7 bash parameter quoting
这个问题听起来可能很复杂,但事实上并非如此!考虑:
% f() { echo "$@"; }
% f a
a
% f cmd -o"value with space"
cmd -ovalue with space
% f cmd -ovalue with space
cmd -ovalue with space
% f cmd -o'value with "quotes"'
cmd -ovalue with "quotes"
% f cmd -ovalue with "quotes"
cmd -ovalue with quotes
Run Code Online (Sandbox Code Playgroud)
显然,“带空间的值”只是一个参数的属性丢失了;同样,双引号在重新输入时也会被“吃掉”。
所需的输出是可以再次用作输入以产生相同输出的输出。
我不认为 BASH 中内置了允许这样做的东西,对吧?
如果我想要做什么并不明显:我有一个存储在 shell 数组中的命令,并且我想将这样的数组打印到标准输出,以便用户可以复制并粘贴输出以在 shell 提示符(或script),以便重现数组中的相同命令。
考虑这个(愚蠢的)例子:
> X=(echo "Bob's car is named \"Bobby\"")
Run Code Online (Sandbox Code Playgroud)
一个普通的echo "${X[@]}"
会输出
echo 鲍勃的车名为“鲍比”
而一种可能的正确输出可能是
echo 鲍勃的汽车名为“鲍比”'
Bash 中参数扩展期间的转换选项之一是(似乎从 Bash 4.4 开始可用;旧版本输出“错误替换”):
${parameter@operator}
扩展要么是参数值的转换,要么是参数本身的信息,具体取决于运算符的值。每个运算符都是一个字母:[...] 扩展是一个字符串,它是以可重复用作输入的格式引用的参数值。
Q
bash-5.2$ f() { echo "${@@Q}"; }
bash-5.2$ f cmd -o'value with "quotes"'
'cmd' '-ovalue with "quotes"'
bash-5.2$ f cmd -ovalue with "quotes"
'cmd' '-ovalue' 'with' 'quotes'
Run Code Online (Sandbox Code Playgroud)
您正在寻找的功能称为序列化(美式英语中的序列化)。
这里一个简单的命令是一个由一个或多个字符串组成的数组,因此它可以归结为序列化一个数组。
如果命令是外部命令,则存在进一步的限制,即字符串不能包含 NUL 字节,因为它们作为 C 样式字符串传递给系统execve()
调用。在大多数 shell 中,即使对于不涉及execve()
系统调用的命令(例如内置命令或函数),也有相同的限制,唯一的例外是 zsh shell。
因此,如果您可以假设命令参数不包含 NUL 字节,则序列化很容易:您只需以 NUL 分隔来打印它们:
print0() {
[ "$#" -eq 0 ] ||
printf '%s\0' "$@"
}
Run Code Online (Sandbox Code Playgroud)
print0 cmd -o"value with space" > file
Run Code Online (Sandbox Code Playgroud)
在bash
4.4 或更高版本中,将其作为参数列表读回只是:
readarray -td '' args < file
Run Code Online (Sandbox Code Playgroud)
其中-d ''
将 NUL 字节设置为分隔符,-t
从值中删除分隔符,这在当前版本的 bash 中不是绝对必要的,因为它不能在变量中存储 NUL。
然后做:
"${args[@]}"
Run Code Online (Sandbox Code Playgroud)
来执行命令。
或者甚至使用 GNU xargs
:
xargs -r0a file env
Run Code Online (Sandbox Code Playgroud)
但请注意,除了 zsh 之外,您不能像在所有其他 shell 中一样将该序列化的结果存储在变量中,shell 变量的值中不能有 NUL 字节。
JSON、XML、YAML 是用于序列化复杂数据结构的常用格式,但它们也有自己的问题(例如,JSON 字符串必须由字符组成,而参数字符串是任意字节的数组),更重要的是,很少有 shell具有对解析它们的内置支持(ksh93v-beta 版本对解析 JSON 有一些实验性支持,但这有很多错误并在较新的版本中被删除)。
一些语言具有内置的序列化格式。例如,php
有serialize()
相应的unserialize()
功能,但php
没有很好的 API 来执行命令。
解释语言中的常见方法是将序列化为代码。例如,这就是Data::Dumper
in所做的事情。如果您有一个带有和作为参数的perl
数组,则可以将其存储为,然后只需评估该 perl 代码即可取回该数组。cmd
-ovalue with space
@array = ("cmd", "-o value with space")
在 bash 或 zsh 等类似 Korn 的 shell 中,这很容易完成,因为这正是所做的typeset -p
。typeset -p argv
在 zsh 中,您可以在函数中执行此serialise
操作,但不能执行此操作typeset -p @
,因为它@
不是变量。在 中bash
,位置参数未映射到argv
变量,您仍然可以使用临时数组。
serialise() {
local args
args=( "$@" )
typeset -p args
}
Run Code Online (Sandbox Code Playgroud)
serialised=$(serialise cmd -o"value with space")
Run Code Online (Sandbox Code Playgroud)
那么反序列化就是:
eval "$serialised"
Run Code Online (Sandbox Code Playgroud)
这将创建$args
数组(请注意,如果在函数中运行,该数组将是该函数的本地数组)。
进而:
"${args[@]}"
Run Code Online (Sandbox Code Playgroud)
再次运行该命令。
请注意,反序列化必须使用与序列化完成时相同的 shell 版本、相同的操作系统和相同的区域设置来完成。有关如何序列化字符串的更多详细信息,请参阅“转义变量以用作另一个脚本的内容”的答案。
为了完整起见,ksh93 具有比其他 shell 更复杂的数据结构,包括多维数组、结构和对象,因此有内置的序列化和反序列化支持。
print -C var
read -C var
例如,您可以使用以下命令复制变量:
print -C var | read -C var_copy
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
630 次 |
最近记录: |