ファイル名の拡張子を取得する


33

bashからファイル拡張子を取得するにはどうすればよいですか?ここに私が試したものがあります:

filename=`basename $filepath`
fileext=${filename##*.}

それをすることでbz2、パスからの拡張を取得できます/dir/subdir/file.bz2が、パスに問題があります/dir/subdir/file-1.0.tar.bz2

可能であれば、外部プログラムなしでbashのみを使用するソリューションをお勧めします。

質問を明確にするために、bashスクリプトを作成して、指定されたアーカイブを1つのコマンドだけで抽出しましたextract path_to_file。その圧縮を見たりタイプをアーカイブすることにより、スクリプトによって決定されますどのようにファイルを抽出するために、それは私が拡張子を取得する場合.bz2などI例えば、これは文字列操作を関与させるべきだと思う、.gzを、.tar.gzの可能性があり.gz、私はその後、.tar前に文字列があるかどうかを確認する必要があります.gz—その場合、拡張子はである必要があります.tar.gz


2
file = "/ dir / subdir / file-1.0.tar.bz2"; echo $ {file ## *。}は、ここに '.bz2'を出力します。あなたが期待している出力は何ですか?
axel_c

1
私が必要です.tar.bz2
uray

回答:


19

ファイル名がのfile-1.0.tar.bz2場合、拡張子はbz2です。拡張機能(fileext=${filename##*.})の抽出に使用している方法は完全に有効です¹。

どのようにあなたが延長になりたいことを決めんtar.bz2といませんbz20.tar.bz2?最初にこの質問に答える必要があります。その後、どのシェルコマンドが仕様に一致するかを把握できます。

  • 可能な仕様の1つは、拡張子が文字で始まる必要があることです。このヒューリスティックは、のようないくつかの一般的な拡張では失敗します7z。これは、特別なケースとして最も適切に処理される可能性があります。bash / ksh / zshの実装は次のとおりです。

    basename=$filename; fileext=
    while [[ $basename = ?*.* &&
             ( ${basename##*.} = [A-Za-z]* || ${basename##*.} = 7z ) ]]
    do
      fileext=${basename##*.}.$fileext
      basename=${basename%.*}
    done
    fileext=${fileext%.}
    

    POSIXの移植性のcaseために、パターンマッチング用のステートメントを使用する必要があります。

    while case $basename in
            ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;;
            *) false;;
          esac
    do 
    
  • 別の可能な仕様は、一部の拡張機能がエンコーディングを示し、さらにストリッピングが必要であることを示すことです。bash / ksh / zshの実装は次のとおりです(shopt -s extglobbashおよびsetopt ksh_globzshで必要):

    basename=$filename
    fileext=
    while [[ $basename = ?*.@(bz2|gz|lzma) ]]; do
      fileext=${basename##*.}.$fileext
      basename=${basename%.*}
    done
    if [[ $basename = ?*.* ]]; then
      fileext=${basename##*.}.$fileext
      basename=${basename%.*}
    fi
    fileext=${fileext%.}
    

    これ0はの拡張と見なされることに注意してくださいfile-1.0.gz

¹ および関連するコンストラクトはPOSIXにあるため、ash、bash、ksh、zshなどの非アンティークBourneスタイルのシェルで動作します。 ${VARIABLE##SUFFIX}


これは、最後の.トークンの前の文字列がアーカイブタイプであるかどうかをチェックすることで解決する必要があります。たとえばtar0反復のようなアーカイブタイプではない場合は終了します。
-uray

2
@uray:これはこの特定のケースで機能しますが、一般的な解決策ではありません。Maciejの例を.patch.lzma考えてみましょう。より良いヒューリスティックは、文字列を考慮するだろう後に最後の.:それは圧縮接尾だ場合(.7z.bz2.gz、...)、ストリッピング続けます。
ジル「SO-悪であるのをやめる」

@NoamMインデントの何が問題になっていますか?編集後は間違いなく壊れています。二重にネストされたコードは、単一にネストされたコードと同じようにインデントされます。
ジル「SO-悪であるのをやめなさい」

22

拡張子を2回抽出するのではなく、ファイル名でパターンマッチングを行うだけで問題を簡素化できます。

case "$filename" in
    *.tar.bz2) bunzip_then_untar ;;
    *.bz2)     bunzip_only ;;
    *.tar.gz)  untar_with -z ;;
    *.tgz)     untar_with -z ;;
    *.gz)      gunzip_only ;;
    *.zip)     unzip ;;
    *.7z)      do something ;;
    *)         do nothing ;;
esac

このソリューションは非常にシンプルです。
AsymLabs


2

これが私のショットです:ドットを改行に変換し、パイプスルーしtail、最後の行を取得します:

$> TEXT=123.234.345.456.456.567.678
$> echo $TEXT | tr . \\n | tail -n1
678

0
echo ${filename#$(echo $filename | sed 's/\.[^[:digit:]].*$//g;')}

例えば:

% echo $filename
2.6.35-zen2.patch.lzma
% echo ${filename#$(echo $filename | sed 's/\.[^[:digit:]].*$//g;')}
.patch.lzma

すべての場合に機能するわけではありません。'foo.7z'で試してください
axel_c

あなたは引用符を必要とし、より良い使用printf、ファイル名の場合では、バックスラッシュが含まれているかで始まります-"${filename#$(printf %s "$filename" | sed 's/\.[^[:digit:]].*$//g;')}"
ジル「SO-停止されて悪」

@axel_c:そうです、例としてMaciejと同じ仕様を実装しました。「文字で始まる」よりも優れたヒューリスティックを提案しますか?
ジル 'SO-悪であるのをやめる

1
@Gilles:事前に計算された既知の拡張子のリストを使用しない限り、解決策はないと思います。拡張子は何でもかまいません。
axel_c

0

ある日、これらのトリッキーな関数を作成しました。

# args: string how_many
function get_last_letters(){ echo ${1:${#1}-$2:$2}; }
function cut_last_letters(){ echo ${1:0:${#1}-$2}; }

この単純なアプローチは、拡張機能だけでなく、多くの場合に非常に役立つことがわかりました。

拡張機能をチェックするため- シンプルで信頼性の高い

~$ get_last_letters file.bz2 4
.bz2
~$ get_last_letters file.0.tar.bz2 4
.bz2

切断延長の場合:

~$ cut_last_letters file.0.tar.bz2 4
file.0.tar

拡張子を変更する場合:

~$ echo $(cut_last_letters file.0.tar.bz2 4).gz
file.0.tar.gz

または、「便利な機能:

~$ function cut_last_letters_and_add(){ echo ${1:0:${#1}-$2}"$3"; }
~$ cut_last_letters_and_add file.0.tar.bz2 4 .gz
file.0.tar.gz

PSこれらの機能が気に入った場合、またはそれらが使い古されている場合は、この投稿を参照してください:)(そしてできればコメントを入れてください)。


0

ジャックマンのケースベースの答えはかなり良くて移植性がありますが、変数にファイル名と拡張子が必要な場合は、この解決策を見つけました:

INPUTFILE="$1"
INPUTFILEEXT=$( echo -n "$INPUTFILE" | rev | cut -d'.' -f1 | rev )
INPUTFILEEXT=$( echo -n $INPUTFILEEXT | tr '[A-Z]' '[a-z]' ) # force lowercase extension
INPUTFILENAME="`echo -n \"$INPUTFILE\" | rev | cut -d'.' -f2- | rev`"

# fix for files with multiple extensions like "gbamidi-v1.0.tar.gz"
INPUTFILEEXT2=$( echo -n "$INPUTFILENAME" | rev | cut -d'.' -f1 | rev )
if [ "$INPUTFILEEXT2" = "tar" ]; then
    # concatenate the extension
    INPUTFILEEXT="$INPUTFILEEXT2.$INPUTFILEEXT"
    # update the filename
    INPUTFILENAME="`echo -n \"$INPUTFILENAME\" | rev | cut -d'.' -f2- | rev`"
fi

これは二重の拡張子でのみ機能し、最初の拡張子は「tar」でなければなりません。

ただし、「tar」テスト行を文字列長テストで変更して、修正を複数回繰り返すことができます。


-1

私はこれを使用してそれを解決しました:

filename=`basename $filepath`
fileext=${filename##*.}
fileext2=${filename%.*}
fileext3=${fileext2##*.}
if [ "$fileext3" == "tar" ]; then
    fileext="tar."$fileext
fi

ただし、これは既知のアーカイブタイプでのみ機能します。この場合のみ tar

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.