有多種方法可以刪除變數中的字元。
到目前為止我發現的最短的方法是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