変数内の文字を削除する方法はたくさんあります。
これまで私が見つけた最も短い方法は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)"
長さが最短か、所要時間が最短か、どちらを意味しているのかわかりません。長さの点では、特定の文字を削除する場合、これら 2 つは最短です (または、私が取得できる最短です)。では、どちらが速いでしょうか?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
。また、 を使用する方がecho
を使用するよりも実際にはわずかに速いようです<<<
。
$ 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
違いはわずかであるため、上記のテストを 2 つそれぞれに対して 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
が、変数に再割り当てする場合は、別のサブシェルを実行するオーバーヘッドを回避できるため、シェルの文字列操作機能を使用する方が高速です。
答え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
万が一、シェルで引用符を再利用したいだけなら、次のようにすればよい。それなしそれらを削除するのも非常に簡単です:
aq() { sh -c 'for a do
alias "$((i=$i+1))=$a"
done; alias' -- "$@"
}
その関数シェルは渡された引数配列を引用符で囲み、反復可能な引数ごとに出力を増分します。
以下にいくつかの引数を示します。
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
'\''
選択した単一の非空白、非ヌルバイトを別の単一のバイトに置き換える操作は、POSIX シェルで と を使用すると最も速く実行できる可能性があり$IFS
ます$*
。
set -f; IFS=\"\'\`; set -- $var; printf %s "$*"
出力
"some ""crazy """"""""string ""here
皆さんに見ていただけるようにここに載せましたがprintf
、もちろん、もし私がこうしていたら:
var="$*"
printf
...コマンドの値ではなく、$var
出力に表示されるものになります。
set -f
私がシェルに指示するとないglob - 文字列にglobパターンとして解釈できる文字が含まれている場合。シェルのパーサーがglobパターンを展開するため、これを行います。後変数に対してフィールド分割を実行します。グロブは のように再度有効にできますset +f
。一般的に、スクリプトでは、次のように bang を設定すると便利だと思います。
#!/usr/bin/sh -f
そして明示的にグロビングを有効にするset +f
私が望むどんなラインでも。
フィールド分割は 内の文字に基づいて行われます$IFS
。
$IFS
値には、$IFS
空白と$IFS
非空白の2 種類があります。$IFS
空白(スペース、タブ、改行)区切られたフィールドは省略するように指定されます順序単一のフィールドに(または、他の何かに先行しない場合は、何もない)- それで...
IFS=\ ; var=' '; printf '<%s>' $var
<>
しかし、他のすべては単一のフィールドに評価されるように指定されています発生ごとに- 切り捨てられません。
IFS=/; var='/////'; printf '<%s>' $var
<><><><><>
全て変数展開は、デフォルトでは$IFS
区切られたデータ配列であり、 に従って個別のフィールドに分割されます$IFS
。 1 つを -quote すると、"
その配列プロパティがオーバーライドされ、単一の文字列として評価されます。
だから私がそうすると...
IFS=\"\'\`; set -- $var
私はシェルの引数配列を、の展開$IFS
によって生成された多くの区切られたフィールドに設定します。展開されると、含まれる文字の構成要素の値は次のようになります。$var
$IFS
失った- それらは現在フィールド区切り文字のみです - それらは です\0NUL
。
"$*"
- 他の二重引用符で囲まれた変数展開と同様に - のフィールド分割特性も上書きします$IFS
。しかし、加えて、最初のバイトを$IFS
区切られたフィールドごとにで"$@"
。だから"
、初め価値$IFS
後続の区切り文字はすべて"
になります"$*"
。分割するときにも、は必要"
ではありません。$IFS
$IFS
後 set -- $args
全く別の価値に変わり、その新しい最初のバイトは、 のフィールド区切り文字として表示されます"$*"
。さらに、次のようにして、それらの痕跡をすべて完全に削除することもできます。
set -- $var; IFS=; printf %s "$*"
出力
some crazy string here