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
にする必要があります。
ファイル名が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%.}
これは0
をfile-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
$ echo "thisfile.txt"|awk -F . '{print $NF}'
これに関するコメント: http://liquidat.wordpress.com/2007/09/29/short-tip-get-file-extension-in-Shell-script/
これが私のショットです:ドットを改行に変換し、tail
をパイプスルーして、最後の行を取得します。
$> TEXT=123.234.345.456.456.567.678
$> echo $TEXT | tr . \\n | tail -n1
678
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
ある日、私はこれらのトリッキーな関数を作成しました:
# 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」テスト行を文字列長テストで変更し、修正を複数回繰り返すことができます。