Prinderea extensiei într-un nume de fișier

Cum obțin extensia de fișier din bash? Iată ce am încercat:

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

Procedând astfel pot obține extensia bz2 din calea /dir/subdir/file.bz2, dar am o problemă cu calea /dir/subdir/file-1.0.tar.bz2.

Aș prefera o soluție folosind doar bash fără extern programe dacă este posibil.

Pentru a clarifica întrebarea, am creat un script bash pentru a extrage orice arhivă dată doar printr-o singură comandă a extract path_to_file. Cum extragerea fișierului este determinată de script, văzând tipul de compresie sau arhivare a acestuia, care ar putea fi .tar.gz, .gz, .bz2 etc. Cred că acest lucru ar trebui să implice manipularea șirurilor, de exemplu dacă primesc extensia .gz atunci ar trebui să verific dacă are șirul .tar înainte de .gz – dacă da, extensia ar trebui fi .tar.gz.

Comentarii

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

; echo $ {file ## *.} imprimă aici ‘ .bz2 ‘ Care este rezultatul pe care ‘ îl așteptați?

  • Am nevoie de .tar.bz2
  • : Extrageți numele fișierului și extensia în Bash .
  • Răspuns

    Dacă numele fișierului este file-1.0.tar.bz2, extensia este bz2. Metoda pe care o utilizați pentru a extrage extensia (fileext=${filename##*.}) este perfect valabilă¹.

    Cum decideți că doriți ca extensia să fie tar.bz2 și nu bz2 sau 0.tar.bz2? Mai întâi trebuie să răspundeți la această întrebare. Apoi puteți afla ce comanda shell se potrivește cu specificația dvs.

    • O posibilă specificație este că extensiile trebuie să înceapă cu o literă. Această euristică eșuează pentru câteva extensii comune, cum ar fi 7z, care ar putea fi tratat cel mai bine ca un caz special. Aici „implementarea sa bash / ksh / zsh:

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

      Pentru portabilitatea POSIX, trebuie să utilizați o instrucțiune case pentru potrivirea modelelor.

      while case $basename in ?*.*) case ${basename##*.} in [A-Za-z]*|7z) true;; *) false;; esac;; *) false;; esac do … 
    • O altă posibilă specificație este că unele extensiile denotă codificări și indică faptul că este necesară o stripare suplimentară. Aici „implementarea sa bash / ksh / zsh (necesită shopt -s extglob sub bash și setopt ksh_glob sub 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%.} 

      Rețineți că acest lucru consideră 0 o extensie în file-1.0.gz.

    ¹ ${VARIABLE##SUFFIX} și construcțiile aferente sunt în POSIX , deci funcționează în orice coajă în stil Bourne care nu este antică, cum ar fi ash, bash, ksh sau zsh.

    Comentarii

    • care ar trebui să fie rezolvat, verificând dacă șirul dinaintea ultimului simbol . este tip arhivă, de exemplu tar, dacă nu este tip arhivă ca 0 iterația ar trebui să se încheie.
    • @uray: funcționează în acest caz particular, dar ‘ nu este o soluție generală . Luați în considerare Maciej ‘ exemplu de .patch.lzma . Un heur mai bun istic ar fi să ia în considerare șirul după ultimul .: dacă este ‘ un sufix de compresie (.7z, .bz2, .gz, …), continuați dezlipirea.
    • @NoamM Ce a fost în neregulă cu indentarea? ‘ este cu siguranță rupt după editarea dvs.: codul dublu imbricat este indentat la fel ca cel cuplat individual.

    Răspundeți

    S-ar putea să simplificați lucrurile făcând doar potrivirea de tipare pe numele fișierului, decât să extrageți extensia de două ori:

    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 

    Comentarii

    • Această soluție este foarte simplă.

    Răspuns

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

    Comentarii aici: http://liquidat.wordpress.com/2007/09/29/short-tip-get-file-extension-in-shell-script/

    Comentarii

    • nu funcționează pentru extensia .tar.gz
    • Ei bine, o .tar .gz este de fapt un tar în interiorul unui fișier gzip, deci funcționează în sensul că elimină o extensie gz dintr-un fișier gzip.

    Răspuns

    Iată ce am făcut: traduceți punctele în linii noi, treceți prin tail, obțineți ultima linie:

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

    Răspuns

    Într-o zi, am „creat acele funcții complicate:

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

    Am găsit această abordare simplă, foarte utilă în multe cazuri, nu numai când merge despre extensii.

    Pentru verificarea extensiilor – Este simplu și fiabil

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

    Pentru extensia de tăiere:

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

    Pentru schimbarea extensiei:

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

    Sau, dacă vă plac „funcțiile la îndemână:

    ~$ 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 Dacă v-au plăcut aceste funcții sau le-ați găsit folosite, vă rugăm să consultați această postare 🙂 (și, sperăm, puneți un comentariu).

    Răspuns

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

    De exemplu:

    Comentarii

    • Nu funcționează pentru toate cazurile. Încercați cu ‘ foo.7z ‘
    • Aveți nevoie de ghilimele și utilizați mai bine printf în cazul în care numele fișierului conține o bară inversă sau începe cu -: "${filename#$(printf %s "$filename" | sed 's/\.[^[:digit:]].*$//g;')}"
    • @axel_c : dreapta și am ‘ am implementat aceeași specificație ca Maciej ca exemplu. Ce euristică sugerați că ‘ este mai bun decât „începe cu o literă”?
    • @Gilles: Cred că există ‘ nu este o soluție decât dacă utilizați o listă precomputată de extensii cunoscute, deoarece o extensie poate fi orice.

    Răspuns

    răspunsul bazat pe cazurile jackman este destul de bun și portabil, dar dacă doriți doar numele fișierului și extensia într-o variabilă, am găsit această soluție:

    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 

    Funcționează numai cu extensii duble, iar prima trebuie să fie „tar”.

    Dar puteți schimba linia de test „tar” cu un test de lungime a șirului și repetați remedierea de mai multe ori .

    Răspuns

    Am rezolvat-o folosind acest lucru:

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

    dar acest lucru funcționează numai pentru tipul de arhivare cunoscut, în acest caz numai tar

    Lasă un răspuns

    Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *