web-dev-qa-db-ja.com

ファイル名の拡張子をつかむ

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

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

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

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

私の質問を明確にするために、私はextract path_to_fileの単一のコマンドだけで特定のアーカイブを抽出するbashスクリプトを作成していました。ファイルを抽出する方法は、圧縮またはアーカイブの種類(.tar.gz、.gz、.bz2など)を確認することにより、スクリプトによって決定されます。たとえば、拡張子.gzを取得する場合、これには文字列操作が含まれるはずです。 .tarの前に文字列.gzがあるかどうかを確認する必要があります—ある場合、拡張子は.tar.gzにする必要があります。

36
uray

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

拡張機能をtar.bz2またはbz2ではなく0.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の実装です(bashではshopt -s extglob、zshではsetopt ksh_globが必要です)。

    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%.}
    

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

¹ ${VARIABLE##SUFFIX}および関連する構成要素は [〜#〜] posix [〜#〜] にあるため、ash、bash、ksh、zshなどの非骨董品のBourneスタイルのシェルで機能します。

拡張子を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
25
glenn jackman
$ echo "thisfile.txt"|awk -F . '{print $NF}'

これに関するコメント: http://liquidat.wordpress.com/2007/09/29/short-tip-get-file-extension-in-Shell-script/

7
Chris

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

$> TEXT=123.234.345.456.456.567.678
$> echo $TEXT | tr . \\n | tail -n1
678
2
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
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

追伸これらの関数が気に入った、またはそれらが十分に使用されていることがわかった場合は、この投稿を参照してください:)(うまくいけばコメントを入力してください)。

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

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

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

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

0
eadmaster