変数内の文字を置き換える最短の方法


18

変数内の文字を置き換えるには多くの方法があります。

私が見つけた最短の方法はtrこれまでのところです:

OUTPUT=a\'b\"c\`d_123and_a_lot_more
OUTPUT=$(echo "$OUTPUT"|tr -d "'\`\"")
echo $OUTPUT

もっと速い方法はありますか?そして、これは'"および `のような引用に対して安全です?


使用し続けることができると思いますtr。BASHのPEは優れていますが、この場合trははるかに高速です。例えばecho "$OUTPUT" | tr -dc '[[:alpha:]]' あなただけ英数字持つようにしたいので、
バレンティンBajrami

2
引用に精通していることに興味があるので、常に変数を引用してください!echo "$OUTPUT"。またはより良いです:printf "%s\n" "$OUTPUT"。(何が起こるときOUTPUT="-n"?)
musiphil 14年

codegolfの閲覧、特にbashのヒントも検討してください。
hoosierEE 14年

回答:


22

どれどれ。私が思いつくことができる最短はあなたの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場合は使用しますが、変数に再割り当てする場合は、シェルの文字列操作機能を使用する方が、別のサブシェルを実行するオーバーヘッドを回避できるため、高速です。


4
OPがに変更された値のバックを設定するに興味を持っているのでOUTPUT、あなたは、コマンド置換サブシェルに伴うオーバーヘッドを考慮する必要がありますtrし、sedソリューション
iruvar

@ 1_CR yes
テルドン

1
なく、かなり、OUTPUT="${OUTPUT//[`\"\']/}" コマンド置換関与していません
iruvar

@ 1_CRああ、そうですね、あなたはかなり正しいです。それは結果を変えます。ありがとう、編集された回答。
テルドン

2
コマンド置換を伴うメソッドには、文字列をいくぶんマングルするという欠点があります。(それを避けることはできますが、コマンドを大幅に複雑にすることを犠牲にしてください。)特に、コマンド置換は末尾の改行を削除します。
ジル 'SO-悪である停止' 14

15

変数置換を使用できます:

$ 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

@ rubo77 echo ${OUTPUT//[`\"\']/x}与えるaxbxcxa
カオス14年

展開に「変数展開」という名前を付けるのは正しくありません。「パラメータ展開」と呼ばれます。
gena2x 14年

@ gena2x-ここでのコメントの意味がわかりませんか?
slm

12

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

1
実際、tr高速です。正規表現とグロブは高価であり、外部プログラムはありませんが、bashはのようなものより常に遅くなりますtr
テルドン

それは入力データと正規表現の実装に大きく依存します。あなたの答えでは、特定の大きなデータセットを取りましたが、データセットは小さいかもしれません。または異なる。また、正規表現の時間ではなくエコーの時間を測定するため、比較が本当に公正かどうかはわかりません。
gena2x 14年

良い点。ただし、テストせずに速度について主張することはできません。実際、変数に代入するときはこれは速いように見えますが、画面に印刷するときはtr勝ちます(私の答えを参照)。私はそれが多くの要因に依存することに同意しますが、実際にテストせずにどちらが勝つかを判断できないのはまさにそのためです。
テルドン

6

偶然に、シェルで再利用するために引用符を処理しようとしている場合は、それら削除せずにこれ行うことができ、それも非常に簡単です:

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パターンを展開するためです。グロビングはのように再度有効にできますset +f。一般的に-スクリプトで-私のように私の強打を設定すると便利です:

#!/usr/bin/sh -f

そしてすることを明示的グロブ有効set +f、私はそれをしたいかもしれないものは何でもライン上。

フィールドの分割は、の文字​​に基づいて行われ$IFSます。

$IFS値には、$IFS空白と$IFS非空白の2種類があります。$IFS空白(スペース、タブ、改行)で区切られたフィールドは、単一のフィールドに順番に移動するように指定されています(他のフィールドの前にない場合はまったくありません)-so ...

IFS=\ ; var='      '; printf '<%s>' $var
<>

ただし、他のすべては、出現ごとに1つのフィールドに評価されるように指定されています-それらは切り捨てられません。

IFS=/; var='/////'; printf '<%s>' $var
<><><><><>

すべての変数展開は、デフォルトで$IFS区切りデータ配列です-それらは、に従って別々のフィールドに分割されます$IFS。あなたはときに"1を-quoteあなたはその配列プロパティをオーバーライドして、1つの文字列として、それを評価します。

だから…

IFS=\"\'\`; set -- $var

シェルの引数配列を、の展開$IFSによって生成された多くの区切りフィールドに設定しています$var。展開されると、に含まれる文字の構成値$IFS失われます -現在はフィールドセパレーターにすぎません-です\0NUL

"$*"-他の二重引用符で囲まれた変数展開と同様、-のフィールド分割品質もオーバーライドします$IFS。しかし、加えて、それはの最初のバイトに代入$IFS それぞれ区切られたフィールドの中を"$@"。だから、理由"だった最初の値は、$IFS 後続のすべての区切り文字になる""$*"そして、あなたがそれを分割するとき"も、その必要はありません$IFS。あなたは変えることができ$IFS た後 set -- $args、完全に別の値にし、その新しい最初のバイトはその後のフィールド区切り文字用に表示されるでしょう"$*"。さらに、次のようにすべての痕跡を完全に削除できます。

set -- $var; IFS=; printf %s "$*"

出力

some crazy string here

とてもいい、+ 1。それは確かに速いのだろうか。私の答えのアプローチと比較するタイミングテストをいくつか追加できますか?あなたの方が速くなると思いますが、見たいです。
テルドン

@terdon-それはシェルに依存します。それは他のどのシェルよりもほぼ間違いなく高速ですtrが、違いはbashこの${var//$c/$newc/}ケースでは不確かです。その場合でも多少のマージンで高速になると期待していますが、このことは常に使用しているため、通常は心配しませんdash-一般的にあらゆる点で桁違いに高速です。それで比較するのは難しいです。
mikeserv 14年

@terdon-試した。しかし-でもbash- 両方を行うtime (IFS=\"\'`; set -- $var; printf %s "$*")と、すべてのフィールドでtime (var=${var//\'`/\"/})結果が0.0000s得られます。私は何か間違ったことをしていますか?そこのバッククォートの前にバックスラッシュがあるはずですが、コメントコードフィールドにバッククォートを入れる方法はわかりません。
mikeserv 14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.