Ta tak i utvidelsen i et filnavn

Hvordan får jeg filtypen fra bash? Her er det jeg prøvde:

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

Ved å gjøre det kan jeg få utvidelse av bz2 fra banen /dir/subdir/file.bz2, men jeg har et problem med banen /dir/subdir/file-1.0.tar.bz2.

Jeg foretrekker en løsning som bare bruker bash uten ekstern programmer hvis det er mulig.

For å gjøre spørsmålet mitt klart, opprettet jeg et bash-skript for å trekke ut et gitt arkiv bare ved en enkelt kommando på extract path_to_file. for å trekke ut filen bestemmes av skriptet ved å se komprimering eller arkiveringstype, som kan være .tar.gz, .gz, .bz2 osv. Jeg tror dette burde innebære strengmanipulering, for eksempel hvis jeg får utvidelsen .gz så bør jeg sjekke om den har strengen .tar før .gz – i så fall bør utvidelsen være .tar.gz.

Kommentarer

  • file = » /dir/subdir/file-1.0.tar.bz2

; ekko $ {file ## *.} skriver ut ‘ .bz2 ‘ her. Hva er utgangen du ‘ forventer?

  • jeg trenger .tar.bz2
  • Relatert : Pakk ut filnavn og utvidelse i Bash .
  • Svar

    Hvis filnavnet er file-1.0.tar.bz2, er utvidelsen bz2. Metoden du bruker for å trekke ut utvidelsen (fileext=${filename##*.}) er helt gyldig¹.

    Hvordan bestemmer du at du vil at utvidelsen skal være tar.bz2 og ikke bz2 eller 0.tar.bz2? Du må svare på dette spørsmålet først. Så kan du finne ut hva skallkommando samsvarer med spesifikasjonen din.

    • En mulig spesifikasjon er at utvidelser må begynne med en bokstav. Denne heuristikken mislykkes for noen få vanlige utvidelser som 7z, som best kan behandles som et spesielt tilfelle. Her «sa bash / ksh / zsh implementering:

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

      For POSIX-portabilitet, må du bruke en case uttalelse for mønstermatching.

      while case $basename in ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;; *) false;; esac do … 
    • En annen mulig spesifikasjon er at noen utvidelser angir kodinger og indikerer at det er behov for videre stripping. Her «sa bash / ksh / zsh implementering (krever shopt -s extglob under bash og setopt ksh_glob under 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%.} 

      Merk at dette anser 0 for å være en utvidelse i file-1.0.gz.

    ¹ ${VARIABLE##SUFFIX} og relaterte konstruksjoner er i POSIX , så de fungerer i et ikke-antikt Bourne-stil skall som ask, bash, ksh eller zsh.

    Kommentarer

    • løses ved å sjekke om strengen før siste . token er arkivtype, for eksempel tar, hvis det ikke er arkivtype som 0 iterasjonen skal avsluttes.
    • @uray: det fungerer i dette tilfellet, men det ‘ er ikke en generell løsning Tenk på Maciej ‘ s eksempel på .patch.lzma . En bedre heur istisk ville være å ta i betraktning strengen etter den siste .: hvis den ‘ er et kompresjonssuffiks (.7z, .bz2, .gz, …), fortsett å strippe.
    • @NoamM Hva var galt med fordypningen? Den ‘ er definitivt ødelagt etter redigeringen din: Dobbelt nestet kode er innrykket den samme som enkelt nestet.

    Svar

    Du kan forenkle ting ved å bare gjøre mønstermatching på filnavnet i stedet for å trekke ut utvidelsen to ganger:

    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 

    Kommentarer

    • Denne løsningen er vakkert enkel.

    Svar

    $ echo "thisfile.txt"|awk -F . "{print $NF}" 

    Kommentarer til dette her: http://liquidat.wordpress.com/2007/09/29/short-tip-get-file-extension-in-shell-script/

    Kommentarer

    • fungerer ikke for .tar.gz utvidelse
    • Vel en .tar .gz er faktisk en tjære i en gzip-fil, så den fungerer i den forstand at den fjerner en gz-utvidelse fra en gzip-fil.

    Svar

    Her er skuddet mitt: Oversett prikker til nye linjer, rør gjennom tail, få siste linje:

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

    Svar

    En dag har jeg opprettet de vanskelige funksjonene:

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

    Jeg har funnet denne greie tilnærmingen, veldig nyttig i mange tilfeller, ikke bare når det går om utvidelser.

    For å sjekke utvidelser – Det er enkelt og pålitelig

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

    For avskjæring av utvidelse:

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

    For endring av utvidelse:

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

    Eller hvis du liker «praktiske funksjoner:

    ~$ 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 Hvis du likte disse funksjonene eller fant dem brukt, referer til dette innlegget 🙂 (og forhåpentligvis legg en kommentar).

    Svar

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

    For eksempel:

    Kommentarer

    • Fungerer ikke i alle tilfeller. Prøv med ‘ foo.7z ‘
    • Du trenger tilbud, og bedre bruk printf hvis filnavnet inneholder en tilbakeslag eller begynner med -: "${filename#$(printf %s "$filename" | sed 's/\.[^[:digit:]].*$//g;')}"
    • @axel_c : right, og jeg ‘ har implementert samme spesifikasjon som Maciej som et eksempel. Hvilken heuristisk antyder du at ‘ er bedre enn “begynner med en bokstav”?
    • @Gilles: jeg tror bare det ‘ er ikke en løsning med mindre du bruker en forhåndsberegnet liste over kjente utvidelser, fordi en utvidelse kan være hva som helst.

    Svar

    det jackman-saksbaserte svaret er ganske bra og bærbart, men hvis du bare vil ha filnavnet og utvidelsen i en variabel, har jeg funnet denne løsningen:

    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 

    Det fungerer bare med doble utvidelser, og den første må være «tjære».

    Men du kan endre «tjære» testlinjen med en strenglengdetest og gjenta fiksen flere ganger .

    Svar

    jeg løste det ved hjelp av dette:

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

    men dette fungerer bare for kjent arkiveringstype, i dette tilfellet bare tar

    Legg igjen en kommentar

    Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *