Dato un percorso del file stringa come /foo/fizzbuzz.bar
, come dovrei usare bash per estrarre solo la fizzbuzz
parte di detta stringa?
Dato un percorso del file stringa come /foo/fizzbuzz.bar
, come dovrei usare bash per estrarre solo la fizzbuzz
parte di detta stringa?
Risposte:
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%.*}
quello di rimuovere tutto dopo un punto o ${x%%.*}
di 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 ${parameter%word}
e ${parameter%%word}
trascina la sezione di corrispondenza delle parti.
guarda il comando basename:
NAME="$(basename /foo/fizzbuzz.bar .bar)"
Usando basename 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 fornito con qualsiasi distribuzione.
fname=`basename $file .$ext`
Modo puro bash:
~$ 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 punti nei suffissi dei file e non è necessario conoscere il suffisso (estensione), ma non funziona con i punti nel nome stesso.
Le funzioni basename e dirname sono ciò che cerchi:
mystring=/foo/fizzbuzz.bar
echo basename: $(basename "${mystring}")
echo basename + remove .bar: $(basename "${mystring}" .bar)
echo dirname: $(dirname "${mystring}")
Ha prodotto:
basename: fizzbuzz.bar
basename + remove .bar: fizzbuzz
dirname: /foo
mystring=$1
anziché con il valore costante corrente (che sopprimerà diversi avvisi, essendo certo di non contenere spazi / caratteri glob / etc), e risolvendo i problemi che esso reperti?
echo "basename: $(basename "$mystring")"
- in questo modo se mystring='/foo/*'
non si ottiene il *
sostituito con un elenco di file nella directory corrente al basename
termine.
L'utilizzo basename
presuppone che tu sappia qual è l'estensione del file, non è vero?
E credo che i vari suggerimenti di espressioni regolari non affrontino un nome di file contenente più di un "."
Quanto segue sembra far fronte a punti doppi. Oh, e nomi di file che contengono un "/" se stessi (solo per calci)
Per parafrasare Pascal, "Mi dispiace che questo script sia così lungo. Non ho avuto il tempo di accorciarlo"
#!/usr/bin/perl
$fullname = $ARGV[0];
($path,$name) = $fullname =~ /^(.*[^\\]\/)*(.*)$/;
($basename,$extension) = $name =~ /^(.*)(\.[^.]*)$/;
print $basename . "\n";
Oltre alla sintassi conforme POSIX utilizzata in questa risposta ,
basename string [suffix]
come in
basename /foo/fizzbuzz.bar .bar
GNUbasename
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 dal nome del file separando l'output con byte NUL usando l' -z
opzione, ad esempio per questi file che contengono spazi, righe e caratteri glob (citati da ls
):
$ ls has*
'has'$'\n''newline.bar' 'has space.bar' 'has*.bar'
Lettura in un array:
$ readarray -d $'\0' arr < <(basename -zs .bar has*)
$ declare -p arr
declare -a arr=([0]=$'has\nnewline' [1]="has space" [2]="has*")
readarray -d
richiede Bash 4.4 o più recente. Per le versioni precedenti, dobbiamo eseguire il loop:
while IFS= read -r -d '' fname; do arr+=("$fname"); done < <(basename -zs .bar has*)
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'
Che ti darà l'output
FizzBuzz
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 l'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 è 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
${parameter%word}
e${parameter%%word}
nella sezione di corrispondenza delle parti finali.