如何将 Bash 函数/脚本参数完全复制到变量中,因为'
(可能还有其他字符)无法保留在变量中?
例如
m() {
v="$@"
echo "$v"
}
$ m let it be 'foo' bar
let it be foo bar
感谢真诚的帮助,先谢谢了。
答案1
这里要注意的是,当你运行例如
m let it be 'foo bar'
程序m
不会获取整个命令行,但 shell 首先解析命令行,处理引号、变量扩展等。该命令的最终结果更像是一个单独字符串的数组,在本例中为 [ let
, it
, be
, foo bar
]。
同样,在
f="James Matthew"
l="Bond"
m "$l, $f $l"
m
只会看到 [ Bond, James Matthew Bond
],并且不知道所涉及的变量和引号。
这意味着常规程序无法到达原始输入行。
zsh 有预执行处理程序的解决方法有没有在不转义或引用的情况下关闭扩展的 shell?和zsh:别名或 shell 函数,仅回显其命令行,包括 shell 控制字符,但我想我没见过 Bash 的那个。
您得到的最接近的是DEBUG
陷阱,它看到在扩展、引用处理和重定向之前要执行的命令。但它并不完整,因为它一次只能看到一个简单的命令。注释将在看到它们之前被删除,并且未引用的;
, &
, |
, , &&
,||
或(
()
至少)仍将被解析。
无论如何,我们可以尝试:
#!/bin/bash
shopt -s extdebug
handle_@ () {
local re='[[:space:]]*@ ([^[:space:]]+)[[:space:]]+(.*)'
if [[ $BASH_COMMAND =~ $re ]]; then
"${BASH_REMATCH[1]}" "${BASH_REMATCH[2]}"
return 1
fi
}
m() {
printf "<%s>\n" "$1"
}
trap handle_@ DEBUG
@ m 'foo bar' doo "$var" > /dev/null
@ m what about this?; date
@ m or some (parentheses)?
该脚本使用DEBUG
陷阱来检测以 开头的命令@
,将下一个空格分隔的“单词”作为要运行的命令,并将其余部分作为单个参数传递给它。从陷阱处理程序返回1
告诉 shell 不要以正常方式实际运行命令,从而防止出现可能的错误消息并阻止任何嵌入式扩展运行(这需要extdebug
设置。)
输出如下所示,显示date
分号后面运行,并且括号破坏了语法:
<'foo bar' doo "$var" > /dev/null>
<what about this?>
Sun Sep 18 18:39:38 EEST 2022
debugprint.sh: line 20: syntax error near unexpected token `('
debugprint.sh: line 20: `@ m or some (parentheses)?'
说实话,这有点恶心,特殊字符的限制相当随意,而且我不保证脚本没有其他问题。
一般来说,如果您计划将此用于某种生产用途,我建议重新考虑程序的结构并通过文件或例如此处文档获取字符串,具体取决于您正在做什么。
这里的文档使得输入原始字符串相对容易,尽管它会通过命令stdin
:
m() {
m=$(cat)
printf "<%s>\n" "$m"
}
m <<'EOF'
Say "$hello" to my 'little friend'
EOF
那打印
<Say "$hello" to my 'little friend'>
使用带引号的here-doc分隔符字符串(在 中<<'EOF'
),您可以在其中放置任意文本和字符,而无需担心引号。 (当然,除了分隔符字符串本身,但您可以使用随机生成的字符串来代替EOF
。)
答案2
您必须让 shell 相信单引号是变量值的一部分,如下所示:
m let it be "'foo'" bar
答案3
shell'
在函数看到引号之前就对其进行解释,并且会对 eg"
和执行相同的操作\
。你可以使用 来逃避它们\
,例如
$ m let it be \'foo\' bar
let it be 'foo' bar
and
$ m let it be \"foo\" bar, \' \\ \"$foo\" \"\$foo\"
let it be "foo" bar, ' \ "" "$foo"
答案4
这里的问题是您传递的输入不带引号。结果,'
被外壳消耗前调用你的函数。如果您使用了 a ,也会发生同样的情况,如果您使用了像or 这样的"
通配符,它也会失败:*
?
$ ls
file1 file2
$ m "aa"bb
aabb
$ m f*2
file2
至少对我来说,最干净的解决方案是始终传递引用的输入。这将保护 shell 中任何具有特殊含义的字符,从而确保它们将原封不动地传递给您的函数:
$ m '"aa"bb'
"aa"bb
$ m "foo 'bar' baz"
foo 'bar' baz
$ m "f*2"
f*2