Bir dosya adında uzantıyı kapma


33

Dosya uzantısını bash'dan nasıl edinebilirim? İşte denedim:

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

Bunu yaparak bz2yoldan uzatma alabilirim /dir/subdir/file.bz2, ancak yolla ilgili bir sorunum var /dir/subdir/file-1.0.tar.bz2.

Mümkünse sadece harici programları kullanmadan bash kullanarak bir çözümü tercih ederdim.

Sorumu açıklığa kavuşturmak için, belirli bir arşivi sadece tek bir komutla çıkarmak için bir bash betiği yaratıyordum extract path_to_file. Onun sıkıştırma gördükten veya türünü arşivleyerek komut dosyası tarafından belirlenir Nasıl dosyayı ayıklamak için, ben uzantısını alırsanız .bz2 vb ben örneğin, bu dize kullanımı içermelidir düşünüyorum, .gz, .tar.gz olabilir .gzSonra .tarönce dize olup olmadığını kontrol etmelisiniz .gz- öyleyse, uzantı olmalıdır .tar.gz.


2
file = "/ dir / alt dizin / dosya 1.0.tar.bz2"; echo $ {dosya ## *.} burada '.bz2' yazdırabilir. Beklediğiniz çıktı nedir?
axel_c

1
ihtiyacım var.tar.bz2
uray

Yanıtlar:


19

Dosya adı, file-1.0.tar.bz2uzantıdır bz2. Extension ( fileext=${filename##*.}) ayıklamak için kullandığınız yöntem tamamen geçerlidir.

Nasıl sen uzantısı olmak istiyorum karar tar.bz2değil bz2ya 0.tar.bz2? Önce bu soruya cevap vermelisin. Ardından, hangi kabuk komutunun belirtiminize uygun olduğunu anlayabilirsiniz.

  • Muhtemel bir açıklama, uzantıların bir harfle başlaması gerektiğidir. Bu buluşsal buluş 7z, en iyi özel durum olarak kabul edilebilecek bazı yaygın uzantılar için başarısız olur . İşte bir bash / ksh / zsh uygulaması:

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

    POSIX taşınabilirliği caseiçin, desen eşleştirme için bir ifade kullanmanız gerekir .

    while case $basename in
            ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;;
            *) false;;
          esac
    do 
    
  • Mümkün olan başka bir şartname, bazı uzantıların kodlamaları göstermesi ve daha fazla sıyırmanın gerekli olduğunu göstermesidir. İşte bir bash / ksh / zsh uygulaması ( shopt -s extglobbash ve setopt ksh_globzsh altında gerektiren ):

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

    Bunun 0içinde bir uzantı olarak kabul edildiğini unutmayın file-1.0.gz.

¹ ve ilgili yapılar POSIX'dedir , bu nedenle bunlar kül, bash, ksh veya zsh gibi antika olmayan herhangi bir Bourne tarzı kabuğunda çalışırlar. ${VARIABLE##SUFFIX}


Son .göstergeden önceki dizginin arşiv türü tarolup olmadığını, örneğin 0yinelemeli arşiv türünün bitip bitmemesi gerektiğini kontrol ederek çözülmesi gerekir.
uray

2
@uray: bu özel durumda işe yarar, ancak genel bir çözüm değildir. Maciej'in örneğini.patch.lzma düşünün . Daha iyi bir sezgisel dize dikkate olurdu sonra son .: Bir sıkıştırma eki ise ( .7z, .bz2, .gz, ...), sıyırma devam ediyor.
Gilles 'SO- kötülük' dur

@Hayır Girişin nesi vardı? Düzenlemenizden sonra kesinlikle bozuluyor: iki kat yuvalanmış kod tek kat yuvalanmış ile aynı girintili.
Gilles 'SO- kötülük yapmayı bırak'

22

Uzantı iki kez ayıklamak yerine, yalnızca dosya adında model eşleştirmesi yaparak işleri basitleştirebilirsiniz:

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

Bu çözüm güzel basittir.
AsymLabs


2

İşte benim şutum: Noktaları yeni hatlara çevir, boru geç tail, son satırı al:

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

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

Örneğin:

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

Her durum için işe yaramaz. 'Foo.7z' ile deneyin
axel_c

Sen tırnak ve daha iyi şekilde ihtiyaç printfdosya adı bir ters eğik çizgi içeriyorsa veya ile başlayan durumunda -:"${filename#$(printf %s "$filename" | sed 's/\.[^[:digit:]].*$//g;')}"
Gilles 'SO durdurma varlık şer'

@axel_c: right ve Maciej ile aynı şartnameyi örnek olarak uyguladım. “Bir harfle başlamaktan” daha iyi bir sezgisel öneriniz nedir?
Gilles 'SO- kötülük' dur

1
@Gilles: Bilinen uzantıların önceden hesaplanmış bir listesini kullanmadıkça bir çözüm olmadığını düşünüyorum, çünkü bir uzantı herhangi bir şey olabilir.
axel_c

0

Bir gün şu zor fonksiyonları yarattım:

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

Bu basit yaklaşımı, sadece uzantılara gittiğinde değil, çoğu zaman çok yararlı buldum.

Uzantıları kontrol etmek için - Basit ve güvenilir

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

Kesme uzatması için:

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

Eklentiyi değiştirmek için:

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

Veya kullanışlı işlevlerden hoşlanıyorsanız:

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

Not: Eğer bu işlevleri beğendiyseniz ya da onları yararlı bulduysanız, lütfen bu yazıya bakın :) (ve umarım bir yorumda bulunun).


0

jackman vaka-tabanlı cevap oldukça iyi ve taşınabilir, ancak sadece bir değişkende dosya adını ve uzantısını istiyorsanız, bu çözümü buldum:

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

Yalnızca çift uzantılarla çalışır ve ilki "katran" olmalıdır.

Ancak "tar" test satırını string uzunluk testiyle değiştirebilir ve düzeltmeyi birçok kez tekrarlayabilirsiniz.


-1

Bunu kullanarak çözdüm:

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

ancak bu yalnızca bilinen arşivleme türünde çalışır, bu durumda yalnızca tar

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.