Ta tag i tillägget i ett filnamn

Hur får jag filtillägget från bash? Här är vad jag försökte:

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

Genom att göra det kan jag få förlängning av bz2 från sökvägen /dir/subdir/file.bz2, men jag har problem med sökvägen /dir/subdir/file-1.0.tar.bz2.

Jag föredrar en lösning som bara använder bash utan extern program om det är möjligt.

För att klargöra min fråga skapade jag ett bash-skript för att extrahera ett visst arkiv bara med ett enda kommando av extract path_to_file. för att extrahera filen bestäms av skriptet genom att se dess komprimering eller arkiveringstyp, det kan vara .tar.gz, .gz, .bz2 etc. Jag tycker att detta borde innebära strängmanipulation, till exempel om jag får tillägget .gz så ska jag kontrollera om den har strängen .tar före .gz – i så fall ska tillägget vara .tar.gz.

Kommentarer

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

; echo $ {file ## *.} skriver ut ’ .bz2 ’ här. Vilken utgång förväntar du dig ’?

  • Jag behöver .tar.bz2
  • Relaterad : Extrahera filnamn och tillägg i Bash .
  • Svar

    Om filnamnet är file-1.0.tar.bz2 är tillägget bz2. Metoden du använder för att extrahera tillägget (fileext=${filename##*.}) är helt giltig¹.

    Hur bestämmer du att du vill att tillägget ska vara tar.bz2 och inte bz2 eller 0.tar.bz2? Du måste svara på den här frågan först. Då kan du ta reda på vad shell-kommandot matchar din specifikation.

    • En möjlig specifikation är att tillägg måste börja med en bokstav. Denna heuristik misslyckas för några vanliga tillägg som 7z, som bäst kan behandlas som ett specialfall. Här ”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%.} 

      För POSIX-portabilitet måste du använda ett case uttalande för mönstermatchning.

      while case $basename in ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;; *) false;; esac do … 
    • En annan möjlig specifikation är att vissa tillägg betecknar kodningar och indikerar att ytterligare strippning behövs. Här ”sa bash / ksh / zsh implementering (kräver shopt -s extglob under bash och 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%.} 

      Observera att detta anser att 0 är en förlängning i file-1.0.gz.

    ¹ ${VARIABLE##SUFFIX} och relaterade konstruktioner finns i POSIX , så de fungerar i alla icke-antika skal i Bourne-stil som ask, bash, ksh eller zsh.

    Kommentarer

    • lösas genom att kontrollera om strängen före sista . -token är arkivetyp, till exempel tar, om det inte är arkivtyp som 0 iteration ska avslutas.
    • @uray: det fungerar i det här fallet, men det ’ är inte en allmän lösning Tänk på Maciej ’ s exempel på .patch.lzma . En bättre heur istic skulle vara att betrakta strängen efter den sista .: om den ’ är ett kompressionssuffix (.7z, .bz2, .gz, …), fortsätt strippningen.
    • @NoamM Vad var fel med indragningen? Det ’ är definitivt trasigt efter din redigering: dubbelt kapad kod är indragen samma som enstaka kapslad.

    Svar

    Du kan förenkla saker genom att bara göra mönstermatchning på filnamnet istället för att extrahera tillägget två gånger:

    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

    • Den här lösningen är vackert enkel.

    Svar

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

    Kommentarer om detta här: http://liquidat.wordpress.com/2007/09/29/short-tip-get-file-extension-in-shell-script/

    Kommentarer

    • fungerar inte för .tar.gz förlängning
    • Tja en .tar .gz är faktiskt en tjära i en gzip-fil så den fungerar i den meningen att den tar bort ett gz-tillägg från en gzip-fil.

    Svar

    Här är mitt skott på det: Översätt prickar till nya rader, rör igenom tail, få sista raden:

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

    Svar

    En dag har jag skapat de där knepiga funktionerna:

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

    Jag har hittat det här enkla tillvägagångssättet, mycket användbart i många fall, inte bara när det går om tillägg.

    För att kontrollera tillägg – Det är enkelt och pålitligt

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

    För avskärningstillägg:

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

    För att ändra tillägg:

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

    Eller, om du gillar ”praktiska 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 Om du gillade dessa funktioner eller tyckte att de var användbara, se det här inlägget 🙂 (och förhoppningsvis kommentera).

    Svar

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

    Till exempel:

    Kommentarer

    • Fungerar inte i alla fall. Försök med ’ foo.7z ’
    • Du behöver offertar, och använd bättre printf om filnamnet innehåller en backslash eller börjar med -: "${filename#$(printf %s "$filename" | sed 's/\.[^[:digit:]].*$//g;')}"
    • @axel_c : right, och jag ’ har implementerat samma specifikation som Maciej som ett exempel. Vilken heuristisk föreslår du att ’ är bättre än ”börjar med en bokstav”?
    • @Gilles: jag tror bara att ’ är inte en lösning såvida du inte använder en förberäknad lista över kända tillägg, eftersom ett tillägg kan vara vad som helst.

    Svar

    det jackman-fallbaserade svaret är ganska bra och bärbart, men om du bara vill ha filnamn och tillägg i en variabel har jag hittat den här 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 fungerar bara med dubbla tillägg och den första måste vara ”tjära”.

    Men du kan ändra ”tjära” testraden med ett stränglängdstest och upprepa fixen flera gånger .

    Svar

    jag löste det med det här:

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

    men detta fungerar bara för känd arkiveringstyp, i det här fallet bara tar

    Lämna ett svar

    Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *