
bash スクリプトには次の変数があります。
file_name='this_is_the_hart_part.csv'
使用
var2=$(echo $file_name | sed -e 's/_{2}\(.*\)_{3}/\1/')
変数 $file_name のアンダースコア 2 と 3 の間に含まれる部分文字列「the」を抽出します。
しかし、$var2 は $file_name と等しくなります。sed コマンドをどのように変更すればよいでしょうか?
答え1
でサポートされている正規表現の種類では、sed
との非貪欲な一致は許可されません*
。
3 番目の で区切られたフィールドを取得したい場合_
、 を使用すると最も簡単に実行できますcut
。
cut -d '_' -f 3
または、次のようにしますawk
:
awk -F '_' '{ print $3 }'
または、シェルでは、最初の 2 つのフィールドを連続して削除し、末尾をトリミングします。
str=${file_name#*_}
str=${str#*_}
str=${str%%_*}
"$str"
最後の単語になりますthe
。この最後のバリエーションを使用するのが、これら 3 つの方法の中で最も速く、最も堅牢な方法でしょう。
変数置換により、先頭ビットから最初のアンダースコアまでが削除された${variable#*_}
文字列が生成されます。 は、最初のアンダースコアから の末尾までのすべてを削除します。これらは標準的な変数置換です。$variable
${variable%%_*}
$variable
ファイル名に変数置換を使用する利点は、 や では処理できawk
ない、改行を含むファイル名に対処できることです。一般に、ファイル名には行指向のテキスト編集ツールを使用しないでください。sed
cut
さらに、 を使用していますecho $file_name
。$file_name
は引用符で囲まれていないため、 の一部であるすべての文字 ($IFS
デフォルトではスペース、タブ、改行) に対して単語分割が行われ、生成された単語にファイル名グロブ文字が含まれている場合、シェルによって現在のディレクトリ内のファイル名と照合されます。 また、ファイル名のバックスラッシュも消えたり、望ましくない効果が生じることがあります (展開を引用符で囲んだ場合でも)。 が引用符で囲まれていない場合ksh
、シェルは の値に対して中括弧展開も行います$file_name
。
答え2
まず最初に注意すsed
べきことは文章デフォルトでは1行ずつ処理するユーティリティですが、ファイル名には任意の文字(改行を含む)や非文字(非文字でもよい)を含めることができます。文章)。
また、変数を引用符で囲まないことは非常に特別な意味を持つ、そんなことはまずしたくないでしょう、それはまた潜在的に非常に危険。
また、echo
任意のデータを出力するためには使用できないので、printf
代わりに。
また、Bourne のようなシェルでの変数割り当て構文は でありvar=value
、 ではありません$var=value
。
の出力全体echo
(または、より正確にはprintf
) をsed
のパターン スペースに読み込むには、次のようにします。
printf '%s\n' "$filename" | sed -e :1 -e '$!{N;b1' -e '}'
次に、 2 番目と 3 番目の間の部分を抽出するコードを追加します_
。
var2=$(
printf '%s\n' "$filename" |
sed -ne :1 -e '$!{N;b1' -e '}' -e 's/^\([^_]*_\)\{2\}\([^_]*\)_.*/\2/p'
)
非貪欲な部分は、 (非文字[^_]*
のシーケンス)を使用して対処されますが、保証に反して、境界を超えた一致は発生しません(ただし、多くの実装では、非文字で依然として問題が発生します)。_
.*
_
この場合、代わりにシェル パラメータ拡張演算子を使用できます。
case $filename in
(*_*_*_*) var2=${filename#*_*_}; var2=${var2%%_*};;
(*) var2=;;
esac
ファイル名がテキストでない場合、または抽出する部分が改行文字で終わる場合、これはより適切に機能します (また、より効率的です)。
いくつかのシェルには、より高度な演算子が備わっているzsh
か、または次のようなものがあります:ksh93
zsh
:分割して
_
3番目のフィールドを取得します:var2=${"${(@s:_:)filename}"[3]}
および後方参照を使用します
${var/pattern/replacement}
(その場合、まず変数に少なくとも 3 つのアンダースコアが含まれていることを確認する必要があります。含まれていない場合は、置換は行われません)。set -o extendedglob var2=${filename/(#b)*_*_(*)_*/$match[1]}
ksh93
:var2=${filename/*_*_@(*)_*/\1}
答え3
@Kusalananda さんのおっしゃる通り、これはsed
間違ったツールであり、非貪欲なマッチングはできません。しかし、非貪欲なマッチングの回避策として、以下
[^_]*
のものを使用できます。_
したがって、あなたの場合は次のようなことができます:
printf '%s\n' "$file_name" | sed -e 's/^[^_]*_[^_]*_\([^_]*\).*$/\1/g'
しかし...あなたのユースケースでは、他のツールを使用する方が良いでしょう...