Bashを使用してファイルの各行からデフォルト名を取得する方法

Bashを使用してファイルの各行からデフォルト名を取得する方法

次の行を含むファイルがあります。

ファイル1

./filled/Event_repeatFILLED.svelte
./dir2/Miscellaneous_servicesFILLED.svelte
./outline/Line_weightFILLED.svelte
./two-tone/ArchitectureFILLED.svelte
...many more lines ...

基本名だけを含むファイルを作成したいと思います。

ファイル2

Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte
...

現在、以下は機能しません。

sed 's/^.\///' file1 > file2

Shell / Bashを使用してこれを行うにはどうすればよいですか?

答え1

sed 代替コマンドでは、異なる区切り文字を使用する必要があります。たとえば、

$ sed 's|.*/||' file 
Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte
...many more lines ...

または:

$ perl -pe 's|.*/||' file 
Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte
...many more lines ...

または以下を使用してawk

$ awk -F'/' '{print $NF}' file 
Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte
...many more lines ...

それとも、次のような愚かなことがあります。

$ rev file | cut -d / -f 1 | rev
Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte
...many more lines ...

あるいは、コメントに提案されているように使用することもできますが、basenameこれはより複雑で遅くなります。

$ while IFS= read -r fileName; do basename -- "$fileName"; done < file
Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte

最後に、本当に純粋なシェルソリューションが必要な場合は、次のようにすることができます。

$ while IFS= read -r fileName; do printf '%s\n' "${fileName##*/}"; done < file
Event_repeatFILLED.svelte
Miscellaneous_servicesFILLED.svelte
Line_weightFILLED.svelte
ArchitectureFILLED.svelte
...many more lines ...

ただし、最後の2つは使用しないでください。効率が最も低く複雑です。バラよりシェルループを使用してテキストを処理するのはなぜ悪い習慣と見なされますか?

答え2

xargsとのGNU実装では、basename次のことができます。

xargs -rd '\n' -a file1 basename -a -- > file2

使用の利点は、basenameまたはなどの一部の特殊なケースを正しく処理できることです。//some/dir/

同等の操作は手動で行われます。

LC_ALL=C sed -e 's:^//*$:/:;t' -e 's:/*$::; s:.*/::' < file1 > file2
  • LC_ALL=Cファイルパスがロケールの有効なテキストで構成されるという保証がないため、必要です。
  • /または/////...特別処理
  • 末尾が/削除されました。
  • 次に、右端のすべての項目を削除します/

を使用している場合は、(for ail)修飾子(から借り)をzsh使用することもできます。:ttcsh

print -rC1 -- ${${(f)"$(<file1)"}:t} > file2

/(または//、...)の場合は、代わりに空の文字列を返すという点で///GNUとは異なります。basename/

そしてperl

perl -MFile::Basename -lpe '$_=basename$_' < file1 > file2

答え3

あまり深く入らず、コマンドに関する即時の質問は、ポイントが「すべて」を意味するものであることです。一つ正規表現の「文字」です。必要な数の文字を一致させる必要があります.*(標準正規表現ではできるだけ多く)。

だから試してみてください

sed 's/^.*\///' file1 > file2

他の回答やコメントに記載されている注意事項が適用されます。

関連情報