我希望能够仅返回当前我正在执行的字符串中的转义文字:
foo="\'\"\(foobar\)'another'[program]\[\$var\]()"
echo "${foo//[^\\\']/}"
但它输出这个:
\'\\''\\
期望的输出应该是这样的:
\'\"\(\)\[\$\]
我仍然处于尝试获取文字单引号的阶段,但不知何故它不起作用,或者它在 bash 扩展中真的可行吗?
编辑
字符串来自 bash $READLINE_LINE,因此不会有像双引号的三重反斜杠那样的额外转义。
答案1
将变量设置为文字值
\'\"\(foobar\)'another'[program]\[\$var\]()
使用双引号字符串,您需要转义每个文字反斜杠和每个双引号或美元符号否则会触发扩展。
string="\\'\\\"\\(foobar\\)'another'[program]\\[\\\$var\\]()"
如果使用单引号字符串,您只需关心特别插入单引号:
string='\'"'"'\"\(foobar\)'"'"'another'"'"'[program]\[\$var\]()'
在这里,我选择打破单引号字符串,为每个单引号添加双引号单引号,即'"'"'
。您还可以在单引号字符串之外使用转义单引号,即'\''
。
如果引用变得太麻烦,您也可以选择使用引用的此处文档:
string=$( cat <<'END'
\'\"\(foobar\)'another'[program]\[\$var\]()
END
)
请注意,如果换行符是字符串中的最后一个字符,这将修剪掉尾随的换行符。
然后你的代码尝试删除所有反斜杠和单引号,这似乎不对。相反,使用一些工具提炼\
和以下字符的所有实例:
grep -o '\\.' <<<"$string"
这会产生
\'
\"
\(
\)
\[
\$
\]
或者,
grep -o '\\.' <<<"$string" | paste -s -d '\0' -
准确地重现问题中的输出。
bash
您也可以直接在循环中执行此操作:
while [[ $string =~ \\. ]]; do
printf '%s\n' "${BASH_REMATCH[0]}"
string=${string#*\\?}
done
或者,
while [[ $string =~ '\'. ]]; do
printf '%s\n' "${BASH_REMATCH[0]}"
string=${string#*'\'?}
done
string
只要字符串中存在这样的字符序列,就会通过修剪直到反斜杠和其他字符的下一个匹配的位来修改 的值。在每次迭代中都会打印与给定正则表达式匹配的位。
答案2
使用zsh
代替bash
,你可以这样做:
set -o extendedglob
print -r -- ${foo//(#b)((\\?)|?)/$match[2]}
或者与ksh93
:
print -r -- "${foo//@(@(\\?)|@(?))/\2}"
(你认为print -r -- "${foo//@(@(\\?)|?)/\2}"
也应该有效,但事实并非如此,看起来像一个错误)
和fish
:
string join '' (string match -ar '\\\\.' $foo)
答案3
我认为您不能使用模式替换操作来查找此类字符串,因为您需要查看前一个字符才能知道是否删除当前字符。你需要像 Perl 正则表达式的负向后查找之类的东西来做到这一点。如果你考虑像这样的字符串,那就更难了\\x
,其中第一个反斜杠应该转义第二个反斜杠,但第二个反斜杠不应该转义x
,至少在反斜杠转义通常起作用的范围内。
在循环中找到匹配的部分会更容易,但 Bash 也确实让这变得非常简单。 (有正则表达式匹配运算符[[ text =~ re ]]
,并且可以在 中找到匹配项${BASH_REMATCH[@]}
,但我认为除了手动之外,没有其他方法可以循环多次命中。)
但你可以用例如来做到这一点grep
。例如,这将输出匹配序列,每行一个:
foo="\'\\\"\(foobar\)'another'[program]\[\$var\]()"
echo "$foo" | grep -oe '\\.'
然后通过管道输出以tr -d '\n'
删除换行符。或者如果您需要在 shell 中处理它们,则使用while IFS= read -r line; do...
,但如果您这样做,您可能应该使用其他工具; shell 不太适合文本处理。