Come rimuovo il suffisso del file e la porzione di percorso da una stringa di percorso in Bash?
Domanda
Dato un percorso del file di stringa come /foo/fizzbuzz.bar
, come userei bash per estrarre solo la parte fizzbuzz
di detta stringa?
Soluzione
Ecco come farlo con gli operatori # e% in Bash.
$ x="/foo/fizzbuzz.bar"
$ y=${x%.bar}
$ echo ${y##*/}
fizzbuzz
$ {x% .bar}
potrebbe anche essere $ {x%. *}
per rimuovere tutto dopo un punto o $ {x %%. *}
per rimuovere tutto dopo il primo punto.
Esempio:
$ x="/foo/fizzbuzz.bar.quux"
$ y=${x%.*}
$ echo $y
/foo/fizzbuzz.bar
$ y=${x%%.*}
$ echo $y
/foo/fizzbuzz
La documentazione è disponibile nel Manuale di Bash . Cerca $ {parametro% word}
e $ {parameter %% word}
sezione di corrispondenza della porzione finale.
Altri suggerimenti
guarda il comando basename:
NAME=$(basename /foo/fizzbuzz.bar .bar)
Puro bash, fatto in due operazioni separate:
-
Rimuovi il percorso da una stringa di percorso:
path=/foo/bar/bim/baz/file.gif file=${path##*/} #$file is now 'file.gif'
-
Rimuovi l'estensione da una stringa di percorso:
base=${file%.*} #${base} is now 'file'.
Usando il nome di base ho usato quanto segue per raggiungere questo obiettivo:
for file in *; do
ext=${file##*.}
fname=`basename $file $ext`
# Do things with $fname
done;
Questo non richiede alcuna conoscenza a priori dell'estensione del file e funziona anche quando hai un nome file che ha punti nel suo nome file (davanti alla sua estensione); richiede però il programma basename
, ma fa parte dei coreutils GNU, quindi dovrebbe essere distribuito con qualsiasi distro.
Pure bash way:
~$ x="/foo/bar/fizzbuzz.bar.quux.zoom";
~$ y=${x/\/*\//};
~$ echo ${y/.*/};
fizzbuzz
Questa funzionalità è spiegata da man bash in " Parameter Expansion " ;. I modi non bash abbondano: awk, perl, sed e così via.
EDIT: funziona con i punti nel file suffissi e non è necessario conoscere il suffisso (estensione), ma non funziona #em # 8217; t funziona con i punti nel < em> nome stesso.
Le funzioni basname e dirname sono ciò che stai cercando:
mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")
Ha un output:
basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
perl -pe 's/\..*$//;s{^.*/}{}'
L'uso di basename
presuppone che tu sappia qual è l'estensione del file, vero?
E credo che i vari suggerimenti di espressioni regolari non facciano fronte a un nome di file contenente più di un "quotato".
Quanto segue sembra far fronte a punti doppi. Oh, e nomi di file che contengono un " / " stessi (solo per i calci)
Per parafrasare Pascal, " Siamo spiacenti, questo script è così lungo. Non ho avuto il tempo di accorciarlo "
#!/usr/bin/perl
$fullname = $ARGV[0];
($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
print $basename . "\n";
Se non puoi usare il nome di base come suggerito in altri post, puoi sempre usare sed. Ecco un (brutto) esempio. Non è il massimo, ma funziona estraendo la stringa desiderata e sostituendo l'input con la stringa desiderata.
echo '/foo/fizzbuzz.bar' | sed 's|.*\/\([^\.]*\)\(\..*\)$|\1|g'
Quale ti darà l'output
FizzBuzz
Oltre alla sintassi conforme POSIX utilizzata in questa risposta ,
basename string [suffix]
come in
basename /foo/fizzbuzz.bar .bar
GNU basename
supporta un'altra sintassi:
basename -s .bar /foo/fizzbuzz.bar
con lo stesso risultato. La differenza e il vantaggio è che -s
implica -a
, che supporta più argomenti:
$ basename -s .bar /foo/fizzbuzz.bar /baz/foobar.bar
fizzbuzz
foobar
Questo può anche essere reso sicuro da nome file separando l'output con byte NUL usando l'opzione -z
, ad esempio per questi file contenenti spazi vuoti, newline e caratteri glob (citati da ls
):
$ ls has*
'has'
Lettura in un array:
$ readarray -d
readarray -d
richiede Bash 4.4 o versioni successive. Per le versioni precedenti, dobbiamo eseguire il ciclo:
while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
\n''newline.bar' 'has space.bar' 'has*.bar'
Lettura in un array:
<*>
readarray -d
richiede Bash 4.4 o versioni successive. Per le versioni precedenti, dobbiamo eseguire il ciclo:
<*>\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=
readarray -d
richiede Bash 4.4 o versioni successive. Per le versioni precedenti, dobbiamo eseguire il ciclo:
<*>\n''newline.bar' 'has space.bar' 'has*.bar'
Lettura in un array:
<*> readarray -d
richiede Bash 4.4 o versioni successive. Per le versioni precedenti, dobbiamo eseguire il ciclo:
readarray -d
richiede Bash 4.4 o versioni successive. Per le versioni precedenti, dobbiamo eseguire il ciclo:
Lettura in un array:
<*> readarray -d
richiede Bash 4.4 o versioni successive. Per le versioni precedenti, dobbiamo eseguire il ciclo:
Attenzione alla soluzione perl suggerita: rimuove qualsiasi cosa dopo il primo punto.
$ echo some.file.with.dots | perl -pe 's/\..*$//;s{^.*/}{}'
some
Se vuoi farlo con perl, questo funziona:
$ echo some.file.with.dots | perl -pe 's/(.*)\..*$/$1/;s{^.*/}{}'
some.file.with
Ma se stai usando Bash, le soluzioni con y = $ {x%. *}
(o basename " $ x " .ext
se conosci il estensione) sono molto più semplici.
Il basename lo fa, rimuove il percorso. Rimuoverà anche il suffisso se fornito e se corrisponde al suffisso del file ma sarà necessario conoscere il suffisso da dare al comando. Altrimenti puoi usare mv e capire quale dovrebbe essere il nuovo nome in qualche altro modo.
Combinazione della risposta più votata con la seconda risposta più votata per ottenere il nome file senza il percorso completo:
$ x="/foo/fizzbuzz.bar.quux"
$ y=(`basename ${x%%.*}`)
$ echo $y
fizzbuzz