web-dev-qa-db-ja.com

grepの基本/拡張posix正規表現文字列のメタ文字のエスケープ

Grepに渡す前に、変数内の文字列のすべてのメタ文字をエスケープすることは可能ですか? SE( ここ )と良い説明 ここ で以前に同様の質問があったことは知っていますが、基本/拡張posix正規表現でそれが可能かどうかだけ興味がありましたPerlパターンの代わりにパターン? (現在、ソリューションに飛び込むのではなく、最初に理解するためにPerl正規表現構文を読んでいます)

なぜこの要件:(メタ、回答には不要)

ファイルをfile_name.ext.000file_name.ext.001...などに分割する大きなファイルを分割するための小さなスクリプトを作成しようとしていました。これは正常に機能します。今、私はすでに分割されているファイルを分割するのは好きではありません(つまり、すべて数字である3文字の拡張子を持つファイル名があり、それらのサイズは元のファイルサイズになります。今度はfile_name.ext.*のようなプレーンシェル拡張を使用するとまた、file_name.ext.ext2を持つファイルと一致するため、再分割する必要がない場合でも、合計サイズの不一致と分割が発生します。したがって、file_name.ext.###という名前のファイルのみをチェックします。###は数字です。これらの部分のファイルサイズを見つけるための現在の式は、次のようになります。この:

FILE_SIZE_EXISTING=$( (find "$DESTINATION" -type f -regextype posix-extended -regex "^$DESTINATION/$FILE_BASENAME(\.[[:digit:]]{3})?$" -print0 | xargs -0 stat --printf="%s\\n" 2>/dev/null || echo 0) | paste -sd+ | bc)

これは単純なファイル名で機能します。ただし、いくつかの派手な名前の場合は機能しません。 []などを含みます。回避策はありますか?私はシェルスクリプトに慣れていないので、Perlについてあまり知りません。

6
mg007

特殊文字の引用方法(ポータブル)

次のスニペットは、拡張正規表現で特別な各文字の前にバックスラッシュを追加します。sedを使用して、文字][()\.^$?*+のいずれかが出現する場合は、バックスラッシュとそれに続く文字に置き換えます。

raw_string='test[string]\.wibble'
quoted_string=$(printf %s "$raw_string" | sed 's/[][()\.^$?*+]/\\&/g')

これにより、$raw_stringの末尾の改行が削除されます。それが問題である場合は、最後に不活性文字を追加して文字列が改行で終わっていないことを確認してから、その文字を削除します。

quoted_string=$(printf %sa "$raw_string" | sed 's/[][()\.^$?*+]/\\&/g')
quoted_string=${quoted_string%?}

特殊文字を引用する方法(bashまたはzshで)

Bashとzshにはパターン置換機能があり、文字列がそれほど長くない場合は高速化できます。置換は文字列でなければならないため、ここでは面倒です。したがって、各文字を個別に置換する必要があります。最初に円記号をエスケープする必要があることに注意してください。

quoted_string=${raw_string//\\//\\\\}
for c in \[ \] \( \) \. \^ \$ \? \* \+; do
  quoted_string=${quoted_string//"$c"/"\\$c"}
done

特殊文字の引用方法(ksh93)

Kshの文字列置換構造は、bashおよびzshの骨抜きバージョンよりも強力です。パターン内のグループへの参照をサポートします。

quoted_string=${raw_string//@([][()\.^$?*+])/\\\1}

あなたが実際に欲しいもの

ここではfindは必要ありません:シェルパターンは、3桁で終わるファイルと一致するのに十分です。パーツファイルが存在しない場合、globパターンは展開されないままになります。ファイルサイズを追加する簡単な方法もあります。stat(多くのunixバリアントに存在しますが、それぞれに異なる構文があります)を使用し、複雑なパイプライン処理を行って値を合計するのではなく、wc -c(通常のファイルでは、ほとんどのシステムで、wcはファイルサイズを確認し、ファイルを開いてバイトを読み取る必要はありません)。

set -- "$DESTINATION/$FILE_BASENAME".[0-9][0-9][0-9]
case $1 in
  *\]) # The glob was left intact, so no part exists
    do_split …;;
  *) # The glob was expanded, so at least one part exists
    FILE_SIZE_EXISTING=$(wc -c "$@" | sed -n '$s/[^0-9]//gp')
    if [ "$FILE_SIZE_EXISTING" -ne "$(wc -c <"$DESTINATION/$FILE_BASENAME")" ]; then
      do_split …
    fi

合計サイズのテストはあまり信頼できないことに注意してください。ファイルが変更されても同じサイズのままであると、古いパーツになってしまいます。ファイルが変更されない場合は問題ありません。唯一のリスクは、パーツが切り捨てられたり欠落したりする可能性があることです。