Grib udvidelsen i et filnavn

Hvordan får jeg filtypenavnet fra bash? Her er hvad jeg prøvede:

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

Ved at gøre det kan jeg få udvidelse af bz2 fra stien /dir/subdir/file.bz2, men jeg har et problem med stien /dir/subdir/file-1.0.tar.bz2.

Jeg foretrækker en løsning, der kun bruger bash uden ekstern programmer, hvis det er muligt.

For at gøre mit spørgsmål klart oprettede jeg et bash-script for at udtrække et givet arkiv ved hjælp af en enkelt kommando af extract path_to_file. for at udpakke filen bestemmes af scriptet ved at se dens komprimering eller arkiveringstype, det kan være .tar.gz, .gz, .bz2 osv. Jeg synes, dette burde involvere strengmanipulation, for eksempel hvis jeg får udvidelsen .gz så skal jeg kontrollere, om den har strengen .tar før .gz – hvis ja, skal udvidelsen være .tar.gz.

Kommentarer

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

; ekko $ {file ## *.} udskriver ‘ .bz2 ‘ her. Hvad er det output, du ‘ forventer?

  • Jeg har brug for .tar.bz2
  • Relateret : Uddrag filnavn og udvidelse i Bash .
  • Svar

    Hvis filnavnet er file-1.0.tar.bz2, er udvidelsen bz2. Den metode, du bruger til at udtrække udvidelsen (fileext=${filename##*.}), er helt gyldig¹.

    Hvordan beslutter du, at du vil have udvidelsen til tar.bz2 og ikke bz2 eller 0.tar.bz2? Du skal først besvare dette spørgsmål. Derefter kan du finde ud af hvad shell-kommando matcher din specifikation.

    • En mulig specifikation er, at udvidelser skal begynde med et bogstav. Denne heuristik mislykkes for et par almindelige udvidelser som 7z, som måske bedst kan behandles som et specielt tilfælde. 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-bærbarhed skal du bruge en case sætning til mønstermatchning.

      while case $basename in ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;; *) false;; esac do … 
    • En anden mulig specifikation er, at nogle udvidelser angiver kodninger og indikerer, at der er behov for yderligere stripping. Her “sa bash / ksh / zsh implementering (kræver 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%.} 

      Bemærk, at dette betragter 0 som en udvidelse i file-1.0.gz.

    ¹ ${VARIABLE##SUFFIX} og relaterede konstruktioner er i POSIX , så de arbejder i en hvilken som helst ikke-antik Bourne-stil shell som aske, bash, ksh eller zsh.

    Kommentarer

    • løses ved at kontrollere, om strengen før sidste . token er arkivtype, for eksempel tar, hvis den ikke er arkivtype som 0 iteration skal slutte.
    • @uray: det fungerer i dette særlige tilfælde, men det ‘ er ikke en generel løsning Overvej Maciej ‘ s eksempel på .patch.lzma . En bedre hed istik ville være at betragte strengen efter den sidste .: hvis den ‘ er et kompressionssuffiks (.7z, .bz2, .gz, …), fortsæt med at strippe.
    • @NoamM Hvad var der galt med indrykket? Det ‘ er bestemt brudt efter din redigering: Dobbelt indlejret kode er indrykket det samme som enkeltindlejret.

    Svar

    Du kan muligvis forenkle tingene ved bare at foretage mønstermatchning på filnavnet i stedet for at udtrække udvidelsen to gange:

    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øsning er smukt 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 til .tar.gz udvidelse
    • Nå en .tar .gz er faktisk en tjære inde i en gzip-fil, så den fungerer i den forstand, at den fjerner en gz-udvidelse fra en gzip-fil.

    Svar

    Her er mit skud på det: Oversæt prikker til nye linjer, rør igennem tail, få sidste linje:

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

    Svar

    En dag har jeg oprettet de vanskelige funktioner:

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

    Jeg har fundet denne ligefremgang, meget nyttig i mange tilfælde, ikke kun når den går om udvidelser.

    Til kontrol af udvidelser – Det er simpelt og pålideligt

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

    Til afskæring af udvidelse:

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

    Til ændring af udvidelse:

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

    Eller hvis du kan lide “praktiske funktioner:

    ~$ 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 kunne lide disse funktioner eller fundet dem brugt, henvis til dette indlæg 🙂 (og forhåbentlig skriv en kommentar).

    Svar

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

    For eksempel:

    Kommentarer

    • Fungerer ikke i alle tilfælde. Prøv med ‘ foo.7z ‘
    • Du har brug for tilbud, og brug bedre printf hvis filnavnet indeholder et tilbageslag eller begynder med -: "${filename#$(printf %s "$filename" | sed 's/\.[^[:digit:]].*$//g;')}"
    • @axel_c : right, og jeg ‘ har implementeret den samme specifikation som Maciej som et eksempel. Hvilken heuristisk foreslår du, at ‘ er bedre end “begynder med et bogstav”?
    • @Gilles: jeg tror bare der ‘ er ikke en løsning, medmindre du bruger en forudberegnet liste over kendte udvidelser, fordi en udvidelse kan være hvad som helst.

    Svar

    det jackman-sagsbaserede svar er ret godt og bærbart, men hvis du bare vil have filnavnet og udvidelsen i en variabel, har jeg fundet denne løsning:

    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 kun med dobbelt udvidelser, og den første skal være “tjære”.

    Men du kan ændre “tjære” testlinjen med en strenglængdetest og gentage rettelsen flere gange .

    Svar

    Jeg løste det ved hjælp af dette:

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

    men dette fungerer kun med kendt arkiveringstype, i dette tilfælde kun tar

    Skriv et svar

    Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *