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 ’?
.tar.bz2
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 ochsetopt 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 ifile-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 exempeltar
, om det inte är arkivtyp som0
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