私はファイルの配列を調べるためにsh
(Mac OSX 10.6)にこの小さなスクリプトを持っています。この時点で、Googleは役に立ちませんでした。
files="*.jpg"
for f in $files
do
echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
name=$?
echo $name
done
これまでのところ(明らかに、シェルの達人にとって)$name
は、grep
がファイル名が提供されたものと一致することを発見したかどうかに応じて、0、1または2を保持するだけです。 私たちが欲しいのは、([a-z]+)
という親の中にあるものをキャプチャし、それを変数に格納することです。
可能であればでgrep
のみを使用したい。そうでない場合は、PythonやPerlなどを使用しないでください。sed
またはそれに類するもの - 私はShellが初めてなので、* nixの純粋主義者の立場からこれを攻撃したいと思います。
また、超クールなボーナスとして、シェルで文字列をどのように連結できるかについて興味がありますか?私がキャプチャしたグループは$ nameに格納された "somename"という文字列でしたか?そして私はそれの最後に ".jpg"という文字列を追加したいのですが、cat $name '.jpg'
?
時間があれば、何が起こっているのか説明してください。
Bashを使っているのなら、grep
を使う必要すらありません。
files="*.jpg"
regex="[0-9]+_([a-z]+)_[0-9a-z]*"
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の正規表現マッチ演算子である=~
を使います。一致の結果は$BASH_REMATCH
という名前の配列に保存されます。最初のキャプチャグループはインデックス1に、2番目のキャプチャグループ(ある場合)はインデックス2などに格納されます。インデックス0は完全一致です。
アンカーなしでは、この正規表現(そして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番目の例もこの一致に失敗することに注意してください。
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)
演算子を使用します。それはそれに続くパターンに影響を与えるので、その位置は重要です。
ファイル名に他の文字が含まれているかどうかに応じて、正規表現を調整する必要があります。この場合、部分文字列がキャプチャされると同時に文字列を連結する例を示します。
少なくとも一般的には、これは純粋なgrep
では実際には不可能です。
ただし、パターンが適切な場合、パイプライン内でgrep
を複数回使用して、最初に行を既知の形式に減らしてから、必要なビットだけを抽出することができます。 (cut
やsed
などのツールはこれではるかに優れていますが)。
議論のために、パターンが少し単純だったとします:[0-9]+_([a-z]+)_
これを次のように抽出できます:
echo $name | grep -Ei '[0-9]+_[a-z]+_' | grep -oEi '[a-z]+'
最初のgrep
は全体のパターンに一致しない行を削除し、2番目のgrep
(--only-matching
が指定されている)は名前のアルファ部分を表示します。これは、パターンが適切であるためにのみ機能します。「アルファ部分」は、必要なものを引き出すのに十分な固有性です。
(脇:個人的にgrep
+ cut
を使用して、次のようになります:echo $name | grep {pattern} | cut -d _ -f 2
。これはcut
を取得し、区切り文字で分割して行をフィールドに解析します_
、フィールド2のみを返します(フィールド番号は1から始まります)。
Unixの哲学は、1つのことを実行し、それをうまく実行し、それらを組み合わせて重要なタスクを達成するツールを持つことです。したがって、grep
+ sed
などは、よりUnix的な方法であると主張します物事を行う:-)
答えはすでに受け入れられていることを私は理解していますが、「厳密に* nixの純粋主義者の角度」からすると、この仕事に適したツールはpcregrep
のようです。まだ言及されていること。行を変更してみてください。
echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
name=$?
次のとおりです。
name=$(echo $f | pcregrep -o1 -Ei '[0-9]+_([a-z]+)_[0-9a-z]*')
キャプチャグループ1の内容のみを取得します。
pcregrep
ツールは、grep
で使用した構文と同じ構文をすべて使用しますが、必要な機能を実装しています。
パラメータ-o
は、裸の場合はgrep
バージョンと同じように機能しますが、表示するキャプチャグループを示すpcregrep
の数値パラメータも受け入れます。
このソリューションでは、スクリプトに最小限の変更しか必要ありません。あなたは単に一つのモジュール式ユーティリティを他のものと取り替えそしてパラメータを微調整するだけです。
おもしろい注:複数の-o引数を使用して、複数のキャプチャー・グループを行に現れる順序で戻すことができます。
私は信じているgrepだけでは不可能
sedの場合
name=`echo $f | sed -E 's/([0-9]+_([a-z]+)_[0-9a-z]*)|.*/\2/'`
私はボーナスを突き刺すつもりだ:
echo "$name.jpg"
これはgawkを使った解決策です。それは私が私が頻繁に使用する必要があると思うので私はそれのための関数を作成しました
function regex1 { gawk 'match($0,/'$1'/, ary) {print ary['${2:-'1'}']}'; }
使用するだけ
$ echo 'hello world' | regex1 'hello\s(.*)'
world
あなたへの提案 - 最後のアンダースコア以降の名前の部分を削除するためにパラメータ展開を使うことができます。
f=001_abc_0za.jpg
work=${f%_*}
name=${work#*_}
name
はabc
という値になります。
Apple の開発者向けドキュメント を参照して、 'Parameter Expansion'を検索してください。
bashがあれば、拡張グロビングを使うことができます
shopt -s extglob
shopt -s nullglob
shopt -s nocaseglob
for file in +([0-9])_+([a-z])_+([a-z0-9]).jpg
do
IFS="_"
set -- $file
echo "This is your captured output : $2"
done
または
ls +([0-9])_+([a-z])_+([a-z0-9]).jpg | while read file
do
IFS="_"
set -- $file
echo "This is your captured output : $2"
done