web-dev-qa-db-ja.com

Bashでパス文字列からファイルのサフィックスとパス部分を削除する方法

/foo/fizzbuzz.barのような文字列ファイルのパスが与えられた場合、その文字列のfizzbuzz部分だけを抽出するのにbashをどのように使用すればよいでしょうか。

350

これはBashの#と%演算子を使って行う方法です。

$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz

${x%.bar}は、ドットの後のすべてを削除するための${x%.*}、または最初のドットの後のすべてを削除するための${x%%.*}とすることもできます。

例:

$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz

ドキュメントは Bashマニュアル にあります。 ${parameter%Word}${parameter%%Word}の末尾部分の一致セクションを探します。

491
Zan Lynx

basenameコマンドを見てください。

NAME=$(basename /foo/fizzbuzz.bar .bar)
190
zigdon

純粋なbash。2つの別々の操作で行われます。

  1. パス文字列からパスを削除します。

    path=/foo/bar/bim/baz/file.gif
    
    file=${path##*/}  
    #$file is now 'file.gif'
    
  2. パス文字列から拡張子を削除します。

    base=${file%.*}
    #${base} is now 'file'.
    
33
Param

Basenameを使う私はこれを達成するために以下を使いました:

for file in *; do
    ext=${file##*.}
    fname=`basename $file $ext`

    # Do things with $fname
done;

これはファイル拡張子の先験的な知識を必要とせず、ファイル名の中にドットがあるファイル名(拡張子の前)がある場合でも機能します。それはプログラムbasenameを必要としますが、これはGNU coreutilsの一部なので、どんなディストリビューションでも出荷されるべきです。

15
mike

純粋なbashの方法:

~$ x="/foo/bar/fizzbuzz.bar.quux.zoom"; 
~$ y=${x/\/*\//}; 
~$ echo ${y/.*/}; 
fizzbuzz

この機能は "bash"の "Parameter Expansion"で説明されています。非bashの方法はたくさんあります:awk、Perl、sedなど。

編集:ファイル内のドットサフィックスで動作し、サフィックス(拡張子)を知る必要はありませんが、しません'は名前自体の中のドットで動作します。

12
Vinko Vrsalovic

Basenameおよびdirname関数は、あなたが求めているものです。

mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")

出力があります:

basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
10
Jerub

この回答 で使用される POSIX準拠構文 に加えて、

basename string [suffix]

のように

basename /foo/fizzbuzz.bar .bar

GNU basename は別の構文をサポートします:

basename -s .bar /foo/fizzbuzz.bar

同じ結果で。違いと利点は、-sが複数の引数をサポートする-aを含むことです:

$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar

これは、-zオプションを使用してNULバイトで出力を分離することにより、ファイル名セーフにすることもできます。たとえば、空白、改行、グロブ文字を含むこれらのファイル(lsで引用):

$ ls has*
'has'$'\n''newline.bar'  'has space.bar'  'has*.bar'

配列への読み込み:

$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")

readarray -dにはBash 4.4以降が必要です。古いバージョンでは、ループする必要があります。

while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
3
Benjamin W.

他の投稿で提案されているようにbasenameを使用できない場合は、いつでもsedを使用できます。これは(醜い)例です。それは最大ではありませんが、必要な文字列を抽出し、入力を必要な文字列に置き換えることによって機能します。

echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'

これはあなたに結果をもたらすでしょう

フィズバズ

3
nymacro

basenameを使うことはあなたがファイル拡張子が何であるか知っていると仮定します、そうではありませんか?

そして私は、さまざまな正規表現の提案が複数の "。"を含むファイル名に対応していないと信じています。

以下はダブルドットに対応しているようです。おお、それ自身に "/"を含むファイル名(ちょうどキックのために)

Pascalを言い換えれば、「このスクリプトは非常に長いのですみません。短くする時間はありませんでした」


  #!/usr/bin/Perl
  $fullname = $ARGV[0];
  ($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
  ($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
  print $basename . "\n";
 
3
Perl -pe 's/\..*$//;s{^.*/}{}'
3
mopoke

提案されているPerlの解決策に注意してください。最初のドットの後にあるものはすべて削除されます。

$ echo some.file.with.dots | Perl -pe 's/\..*$//;s{^.*/}{}'
some

あなたがPerlでそれをやりたいのなら、これはうまくいきます:

$ echo some.file.with.dots | Perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with

しかし、Bashを使っているのであれば、y=${x%.*}(または、拡張子がわかっていればbasename "$x" .ext)を使った解決法はもっと簡単です。

2
mivk

ベース名はそれを行い、パスを削除します。指定されている場合、およびファイルのサフィックスと一致する場合は、サフィックスも削除されますが、コマンドに付けるサフィックスを知る必要があります。そうでなければ、mvを使用して、新しい名前が他の方法であるべきものを見つけ出すことができます。

1
waynecolvin

フルパスなしでファイル名を取得するために、最高評価の回答と2番目に高い評価の回答を組み合わせます。

$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz
1
c.gutierrez