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?
.tar.bz2
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 ogsetopt 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 ifile-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 eksempeltar
, hvis den ikke er arkivtype som0
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