変数から文字を削除する最も短い方法

変数から文字を削除する最も短い方法

変数から文字を削除する方法はいくつかあります。

これまで私が見つけた最も短い方法は次のとおりです。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

違いが小さいため、両方のテストに対して上記のテストを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'\''

$IFSとを使用するすべてのPOSIXシェルでは、空白ではなくnull以外の選択された単一バイトを別の単一バイトに置き換えることがおそらく最速です$*

set -f; IFS=\"\'\`; set -- $var; printf %s "$*"

出力

"some ""crazy """"""""string ""here

私はただprintfあなたが見ることができるようにそこに置くだけですが、もちろんこうすれば次のようになります。

var="$*"

printf...コマンドの代わりに、$var値は出力に表示される値になります。

私がset -fシェルに指示したときいいえto glob - 文字列にglobパターンとして解釈できる文字が含まれている場合。シェルパーサーがglobパターンを拡張するので、これを行います。後ろに変数に対してフィールド分割を実行します。ワイルドカードは次のように再度有効にできますset +f。通常、スクリプトで前髪を次のように設定すると便利です。

#!/usr/bin/sh -f

だから明示的にワイルドカードを有効にするset +f私が望むすべての行に関連しています。

フィールド分割はの文字に基づいています$IFS

スペースとスペース以外の2つの$IFS値があります。スペース$IFS$IFS$IFS(スペース、タブ、改行)省略されたと指定されたフィールド区切り注文する単一フィールドとして(または他のものより前にない場合はまったくありません)- だから...

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

ただし、他のすべての項目は単一のフィールドを評価するように指定されています。毎回- 切れません。

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

みんなデフォルトでは、変数拡張は$IFS区切りデータ配列です$IFS。 -quote で引用すると、配列"属性をオーバーライドして単一の文字列として評価します。

だから私がこれをするとき...

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

シェルの引数配列を$IFS拡張で生成された区切りフィールドの数に設定しました。$var拡張すると、埋め込み文字の構成$IFS値は次のようになります。失われた- 今は単なるフィールド区切り記号です。です\0NUL

"$*"- 他の二重引用符変数の拡張と同様に - もオーバーライドされます$IFSまた、最初のバイトを置き換えます。$IFS 区切られた各フィールドについて存在する"$@""だからこそ最初値は$IFS 以降のすべての区切り記号""$*"分割時に"その中に含める必要もありません。$IFSあなたは変更することができます$IFS 後ろに set -- $args全く異なる値で新しいこれにより、最初のバイトがフィールド区切り記号として表示されます"$*"。さらに、次のようにすべてのトレースを完全に削除できます。

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

出力

some crazy string here

関連情報