有多种方法可以删除变量中的字符。
到目前为止我发现的最短的方法是tr
:
OUTPUT=a\'b\"c\`d_123and_a_lot_more
OUTPUT=$(echo "$OUTPUT"|tr -d "'\`\"")
echo $OUTPUT
有更快的方法吗?
'
对于,"
及其本身这样的引号来说,这种引用安全吗`
?
答案1
让我们来看看。我能想到的最短的是对您的tr
解决方案的调整:
OUTPUT="$(tr -d "\"\`'" <<<$OUTPUT)"
其他替代方案包括已经提到的变量替换,它可能比目前所示的更短:
OUTPUT="${OUTPUT//[\'\"\`]}"
当然sed
,尽管这在字符方面更长:
OUTPUT="$(sed s/[\'\"\`]//g <<<$OUTPUT)"
我不确定你的意思是最短的长度还是最短的时间。就长度而言,在删除这些特定字符时,这两个字符是尽可能短的(或者无论如何我都能得到它)。那么,哪个最快?我通过将OUTPUT
变量设置为示例中的变量进行测试,但重复了几十次:
$ echo ${#OUTPUT}
4900
$ time tr -d "\"\`'" <<<$OUTPUT
real 0m0.002s
user 0m0.004s
sys 0m0.000s
$ time sed s/[\'\"\`]//g <<<$OUTPUT
real 0m0.005s
user 0m0.000s
sys 0m0.000s
$ time echo ${OUTPUT//[\'\"\`]}
real 0m0.027s
user 0m0.028s
sys 0m0.000s
正如您所看到的,tr
显然是最快的,紧随其后的是sed
。另外,看起来 usingecho
实际上比 using 稍快<<<
:
$ for i in {1..10}; do
( time echo $OUTPUT | tr -d "\"\`'" > /dev/null ) 2>&1
done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}';
0.0025
$ for i in {1..10}; do
( time tr -d "\"\`'" <<<$OUTPUT > /dev/null ) 2>&1
done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}';
0.0029
由于差异很小,我对两者分别运行了 10 次上述测试,结果发现最快的确实是您必须开始的测试:
echo $OUTPUT | tr -d "\"\`'"
但是,当您考虑分配给变量的开销时,情况会发生变化,在这里,使用tr
比简单替换稍微慢一些:
$ for i in {1..10}; do
( time OUTPUT=${OUTPUT//[\'\"\`]} ) 2>&1
done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}';
0.0032
$ for i in {1..10}; do
( time OUTPUT=$(echo $OUTPUT | tr -d "\"\`'")) 2>&1
done | grep -oP 'real.*m\K[\d.]+' | awk '{k+=$1;} END{print k/NR}';
0.0044
因此,总而言之,当您只想查看结果时,请使用,tr
但如果您想重新分配给变量,则使用 shell 的字符串操作功能会更快,因为它们避免了运行单独的子 shell 的开销。
答案2
你可以使用变量替换:
$ OUTPUT=a\'b\"c\`d
$ echo "$OUTPUT"
a'b"c`d
使用该语法:用${parameter//pattern/string}
字符串替换所有出现的模式。
$ echo "${OUTPUT//\'/x}"
axb"c`d
$ echo "${OUTPUT//\"/x}"
a'bxc`d
$ echo "${OUTPUT//\`/x}"
a'b"cxd
$ echo "${OUTPUT//[\'\"\`]/x}"
axbxcxd
答案3
在 bash 或 zsh 中是:
OUTPUT="${OUTPUT//[\`\"\']/}"
请注意,${VAR//PATTERN/}
删除该模式的所有实例。了解更多信息bash参数扩展
该解决方案对于短字符串来说应该是最快的,因为它不涉及运行任何外部程序。然而,对于很长的字符串,情况恰恰相反——最好使用专用工具进行文本操作,例如:
$ OUTPUT="$(cat /usr/src/linux/.config)"
$ time (echo $OUTPUT | OUTPUT="${OUTPUT//set/abc}")
real 0m1.766s
user 0m1.681s
sys 0m0.002s
$ time (echo $OUTPUT | sed s/set/abc/g >/dev/null)
real 0m0.094s
user 0m0.078s
sys 0m0.006s
答案4
如果偶尔您只是想处理重用 shell 的引号,那么您可以这样做没有删除它们,也非常简单:
aq() { sh -c 'for a do
alias "$((i=$i+1))=$a"
done; alias' -- "$@"
}
该函数 shell 引用您传递给它的任何 arg 数组,并增加每个可迭代参数的输出。
这里有一些参数:
aq \
"here's an
ugly one" \
"this one is \$PATHpretty bad, too" \
'this one```****```; totally sucks'
输出
1='here'"'"'s an
ugly one'
2='this one is $PATHpretty bad, too'
3='this one```****```; totally sucks'
该输出通常来自dash
安全引号单引号输出,例如'"'"'
.bash
会做'\''
。
$IFS
在任何使用 和的POSIX shell 中,将选定的单个、非空白、非空字节替换为另一个单个字节可能是最快的$*
。
set -f; IFS=\"\'\`; set -- $var; printf %s "$*"
输出
"some ""crazy """"""""string ""here
我只是把printf
它放在那里,以便你可以看到它,但当然,如果我这样做了:
var="$*"
...而不是printf
命令$var
的值将是您在输出中看到的值。
当我set -f
指示 shell时不是to glob - 如果字符串包含可以解释为 glob 模式的字符。我这样做是因为 shell 解析器扩展了 glob 模式后它对变量执行字段分割。可以像 一样重新启用通配符set +f
。一般来说 - 在脚本中 - 我发现将我的刘海设置如下很有用:
#!/usr/bin/sh -f
然后到显式启用通配符与set +f
我可能想要的任何线路有关。
字段分割是根据 中的字符进行的$IFS
。
有两种$IFS
值 -$IFS
空白和$IFS
非空白。$IFS
空白(空格、制表符、换行符)分隔字段指定为省略顺序到单个字段(或者如果它们不先于其他内容,则根本没有)- 所以...
IFS=\ ; var=' '; printf '<%s>' $var
<>
但所有其他的都被指定为评估单个字段每次出现- 它们没有被截断。
IFS=/; var='/////'; printf '<%s>' $var
<><><><><>
全部默认情况下,变量扩展是$IFS
分隔数据数组 - 它们根据$IFS
.当您用"
-quote 引用一个时,您会覆盖该数组属性并将其计算为单个字符串。
所以当我这样做时...
IFS=\"\'\`; set -- $var
我将 shell 的参数数组设置为由的扩展$IFS
生成的许多分隔字段。$var
当它被扩展时,它包含的字符的组成$IFS
值为丢失的- 它们现在只是字段分隔符 - 它们是\0NUL
。
"$*"
- 与其他双引号变量扩展一样 - 也覆盖$IFS
.但,此外,它替换第一个字节$IFS
对于每个分隔字段在"$@"
。所以因为"
是第一的值在$IFS
所有后续分隔符都"
变为"$*"
.当你拆分它时,也不必"
在其中。$IFS
你可以改变$IFS
后 set -- $args
完全到另一个值及其新的然后第一个字节将显示为 中的字段分隔符"$*"
。更重要的是,您可以完全删除它们的所有痕迹,如下所示:
set -- $var; IFS=; printf %s "$*"
输出
some crazy string here