sh
ファイルの配列を調べるために、次のスクリプトを (macOS 10.6) に用意しました。
files="*.jpg"
for f in $files
do
echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
name=$?
echo $name
done
これまでのところ、ファイル名が指定された内容と一致している$name
かどうかに応じて、0、1、または 2 が保持されるだけですgrep
。私が望むのは、括弧内の内容をキャプチャして([a-z]+)
、それを変数に格納することです。
grep
可能であれば、onlyを使いたいと思います。そうでない場合は、Python や Perl など、sed
またはそれに類するものは使用しないでください。私は、この問題を *nix 純粋主義者の観点から取り組みたいと思います。
ベストアンサー1
Bash を使用している場合は、以下を使用する必要さえありませんgrep
。
files="*.jpg"
regex="[0-9]+_([a-z]+)_[0-9a-z]*" # put the regex in a variable because some patterns won't work if included literally
for f in $files # unquoted in order to allow the glob to expand
do
if [[ $f =~ $regex ]]
then
name="${BASH_REMATCH[1]}"
echo "${name}.jpg" # concatenate strings
name="${name}.jpg" # same thing stored in a variable
else
echo "$f doesn't match" >&2 # this could get noisy if there are a lot of non-matching files
fi
done
正規表現を変数に入れる方がよいでしょう。一部のパターンは、そのまま含めると機能しません。
これは=~
、Bash の正規表現一致演算子である which を使用します。一致の結果は、と呼ばれる配列に保存されます$BASH_REMATCH
。最初のキャプチャ グループはインデックス 1 に保存され、2 番目 (ある場合) はインデックス 2 に保存されます。以下同様です。インデックス 0 は完全一致です。
正規表現アンカーに関する補足事項 #1:
grep
アンカーがない場合、この正規表現 (および を使用する正規表現) は、次の例などと一致しますが、これは探しているものではない可能性があることに注意してください。
123_abc_d4e5
xyz123_abc_d4e5
123_abc_d4e5.xyz
xyz123_abc_d4e5.xyz
2 番目と 4 番目の例を削除するには、正規表現を次のようにします。
^[0-9]+_([a-z]+)_[0-9a-z]*
これは、文字列が1 つ以上の数字で始まる必要があることを示しています。キャレットは文字列の先頭を表します。次のように、正規表現の最後にドル記号を追加すると、
^[0-9]+_([a-z]+)_[0-9a-z]*$
ドットは正規表現の文字の中になく、ドル記号は文字列の末尾を表すため、3 番目の例も除外されます。4 番目の例もこの一致に失敗することに注意してください。
grep
演算子に関する補足事項 #2 \K
:
GNU をお持ちの場合grep
(演算子が追加された 2.5 以降だと思います\K
):
name=$(echo "$f" | grep -Po '(?i)[0-9]+_\K[a-z]+(?=_[0-9a-z]*)').jpg
演算子\K
(可変長の後読み) は、先行するパターンを一致させますが、結果に一致は含まれません。固定長の場合(?<=)
、パターンは閉じ括弧の前に含まれます。\K
量指定子が異なる長さの文字列と一致する可能性がある場合は (例+
: 、*
、{2,4}
) を使用する必要があります。
この(?=)
演算子は固定長または可変長のパターンに一致し、「先読み」と呼ばれます。また、一致した文字列は結果に含まれません。
大文字と小文字を区別せずに一致させるには、(?i)
演算子を使用します。この演算子は、その後に続くパターンに影響するため、その位置は重要です。
ファイル名に他の文字が含まれているかどうかに応じて、正規表現を調整する必要があるかもしれません。この場合、部分文字列がキャプチャされると同時に文字列を連結する例を示しています。