Meraih ekstensi dalam nama file


33

Bagaimana cara saya mendapatkan ekstensi file dari bash? Inilah yang saya coba:

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

Dengan melakukan itu saya bisa mendapatkan ekstensi dari bz2path /dir/subdir/file.bz2, tapi saya punya masalah dengan path /dir/subdir/file-1.0.tar.bz2.

Saya lebih suka solusi menggunakan hanya bash tanpa program eksternal jika memungkinkan.

Untuk memperjelas pertanyaan saya, saya membuat skrip bash untuk mengekstrak arsip apa pun yang diberikan hanya dengan satu perintah extract path_to_file. Cara mengekstrak file ditentukan oleh skrip dengan melihat kompresi atau jenis pengarsipannya, yang bisa berupa .tar.gz, .gz, .bz2 dll. Saya pikir ini harus melibatkan manipulasi string, misalnya jika saya mendapatkan ekstensi .gzmaka saya harus memeriksa apakah ia memiliki string .tarsebelum .gz- jika demikian, ekstensi seharusnya .tar.gz.


2
file = "/ dir / subdir / file-1.0.tar.bz2"; echo $ {file ## *.} mencetak '.bz2' di sini. Apa output yang Anda harapkan?
axel_c

1
saya perlu.tar.bz2
uray

Jawaban:


19

Jika nama file adalah file-1.0.tar.bz2, ekstensi itu bz2. Metode yang Anda gunakan untuk mengekstrak ekstensi (fileext=${filename##*.} ) benar-benar valid¹.

Bagaimana Anda memutuskan bahwa Anda ingin menjadi ekstensi tar.bz2dan tidak bz2atau tidak0.tar.bz2 ? Anda harus menjawab pertanyaan ini terlebih dahulu. Kemudian Anda bisa mengetahui perintah shell apa yang cocok dengan spesifikasi Anda.

  • Satu spesifikasi yang mungkin adalah ekstensi harus dimulai dengan huruf. Heuristik ini gagal untuk beberapa ekstensi umum seperti 7z, yang mungkin diperlakukan sebagai kasus khusus. Berikut ini adalah implementasi bash / ksh / zsh:

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

    Untuk portabilitas POSIX, Anda perlu menggunakan casepernyataan untuk pencocokan pola.

    while case $basename in
            ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;;
            *) false;;
          esac
    do 
  • Spesifikasi lain yang mungkin adalah bahwa beberapa ekstensi menunjukkan pengkodean dan menunjukkan bahwa pengupasan lebih lanjut diperlukan. Berikut ini adalah implementasi bash / ksh / zsh (membutuhkan di shopt -s extglobbawah bash dan di setopt ksh_globbawah zsh):

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

    Perhatikan bahwa ini dianggap 0sebagai ekstensi di file-1.0.gz.

¹ dan konstruk terkait dalam POSIX , sehingga mereka bekerja di shell gaya Bourne non-antik seperti abu, bash, ksh atau zsh. ${VARIABLE##SUFFIX}


yang harus dipecahkan, dengan memeriksa apakah string sebelum .token terakhir adalah tipe arsip, misalnya tar, jika bukan tipe arsip seperti 0iterasi yang harus diakhiri.
uray

2
@ayay: itu berfungsi dalam kasus khusus ini, tapi itu bukan solusi umum. Perhatikan contoh Maciej tentang.patch.lzma . Sebuah heuristik yang lebih baik akan mempertimbangkan string setelah yang terakhir .: jika akhiran kompresi ( .7z, .bz2, .gz, ...), terus stripping.
Gilles 'SO- stop being evil'

@NoamM Apa yang salah dengan indentasi? Ini pasti rusak setelah Anda edit: kode bersarang ganda indentasi sama dengan bersarang tunggal.
Gilles 'SANGAT berhenti menjadi jahat'

22

Anda dapat menyederhanakan masalah dengan hanya melakukan pencocokan pola pada nama file daripada mengekstraksi ekstensi dua kali:

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

Solusi ini sangat sederhana.
AsymLabs


2

Inilah kesempatan saya: Menerjemahkan titik ke baris baru, menelusuri tail, mendapatkan baris terakhir:

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

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

Sebagai contoh:

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

Tidak berfungsi untuk semua kasus. Coba dengan 'foo.7z'
axel_c

Anda perlu mengutip, dan lebih baik digunakan printfjika nama file berisi backslash atau dimulai dengan -:"${filename#$(printf %s "$filename" | sed 's/\.[^[:digit:]].*$//g;')}"
Gilles 'SO- stop being evil'

@ Maxel_c: benar, dan saya telah menerapkan spesifikasi yang sama dengan Maciej sebagai contoh. Apa heuristik yang Anda sarankan lebih baik daripada "dimulai dengan huruf"?
Gilles 'SO- stop being evil'

1
@Gilles: Saya hanya berpikir tidak ada solusi kecuali Anda menggunakan daftar ekstensi yang sudah dikomputasi, karena ekstensi bisa apa saja.
axel_c

0

Suatu hari saya telah membuat fungsi-fungsi rumit:

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

Saya telah menemukan pendekatan langsung ini, sangat berguna dalam banyak kasus, tidak hanya ketika itu tentang ekstensi.

Untuk memeriksa ekstensi - Sederhana dan dapat diandalkan

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

Untuk memotong ekstensi:

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

Untuk mengubah ekstensi:

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

Atau, jika Anda suka "fungsi praktis:

~$ 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

NB Jika Anda menyukai fungsi-fungsi itu atau ternyata bermanfaat, silakan merujuk ke posting ini :) (dan semoga beri komentar).


0

jawaban kasus berbasis jackman cukup bagus dan portabel, tetapi jika Anda hanya ingin nama file dan ekstensi dalam variabel saya telah menemukan solusi ini:

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

Ini hanya berfungsi dengan ekstensi ganda dan yang pertama harus "tar".

Tetapi Anda dapat mengubah jalur tes "tar" dengan tes panjang string dan ulangi perbaikan berulang kali.


-1

saya menyelesaikannya menggunakan ini:

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

tetapi ini hanya berfungsi untuk jenis pengarsipan yang dikenal, dalam hal ini saja tar

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.