Come posso rimuovere l'estensione di un nome file in uno script di shell?


147

Cosa c'è di sbagliato nel seguente codice?

name='$filename | cut -f1 -d'.''

Come è, ottengo la stringa letterale $filename | cut -f1 -d'.', ma se rimuovo le virgolette non ottengo nulla. Nel frattempo, digitando

"test.exe" | cut -f1 -d'.'

in un guscio mi dà l'uscita che voglio, test. So già che $filenameè stato assegnato il valore giusto. Quello che voglio fare è assegnare a una variabile il nome del file senza l'estensione.


6
basename $filename .exefarebbe la stessa cosa. Ciò presuppone che tu sappia sempre quale estensione vuoi rimuovere.
mpe,

6
@mpe, vuoi dire basename "$filename" .exe. Altrimenti i nomi di file con spazi sarebbero cattive notizie.
Charles Duffy,

Risposte:


114

Dovresti usare la sintassi di sostituzione dei comandi$(command) quando vuoi eseguire un comando in script / comando.

Quindi la tua linea sarebbe

name=$(echo "$filename" | cut -f 1 -d '.')

Spiegazione del codice:

  1. echoottenere il valore della variabile $filenamee inviarlo a standard output
  2. Quindi prendiamo l'output e lo inoltriamo al cutcomando
  3. Il cututilizzerà il. come delimitatore (noto anche come separatore) per tagliare la stringa in segmenti e -fselezioniamo quale segmento vogliamo avere in output
  4. Quindi la $()sostituzione del comando otterrà l'output e ne restituirà il valore
  5. Il valore restituito verrà assegnato alla variabile denominata name

Si noti che ciò fornisce la parte della variabile fino al primo periodo .:

$ filename=hello.world
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello.hello.hello
$ echo "$filename" | cut -f 1 -d '.'
hello
$ filename=hello
$ echo "$filename" | cut -f 1 -d '.'
hello

Grazie. Ho anche notato che devo usare il comando echo. name =echo $filename | cut -f1 -d'.'
mimicocotopus

17
I backtick sono deprecati da POSIX, $()è preferito.
Giordania,

3
Forking e piping per arrivare ad alcuni personaggi è la peggior soluzione che si possa immaginare.
Jens,

39
Il problema con questa risposta è che si presume che la stringa di input abbia UN SOLO punto ... @chepner di seguito ha una soluzione molto migliore ... name = $ {nomefile%. *}
Scott Stensland

4
Questa risposta è caratteristica dei principianti e non dovrebbe essere diffusa. Usa il meccanismo incorporato come descritto dalla risposta di
Chepner

260

Puoi anche usare l'espansione dei parametri:

$ filename=foo.txt
$ echo "${filename%.*}"
foo

6
qui la spiegazione del comando: gnu.org/software/bash/manual/html_node/…
Carlos Robles,

1
E qui stavo per usare echo -n "This.File.Has.Periods.In.It.txt" | awk -F. '{$NF=""; print $0}' | tr ' ' '.' | rev | cut -c 2- | rev. Grazie.
user208145

1
Funziona con file con più estensioni come image.png.gz?
Hawker65,

11
%.*rimuoverà solo l'ultima estensione; se si desidera rimuovere tutte le estensioni, utilizzare %%.*.
Chepner,

1
Questo sembra funzionare molto meglio della risposta accettata. Rade solo l'ultima estensione se il file ha più di un punto, mentre il taglio rimuove tutto dopo il primo punto.
Dale Anderson,


20

Se il tuo nome file contiene un punto (diverso da quello dell'estensione), utilizza questo:

echo $filename | rev | cut -f 2- -d '.' | rev

1
Ho dimenticato il mezzo giro, ma una volta che l'ho visto, è stato fantastico!
suprema Pooba,

È ancora meglio con -sun'opzione data a cut, in modo che restituisca una stringa vuota ogni volta che il nome file non contiene punti.
Hibou57,

1
Questa dovrebbe essere la risposta accettata secondo me, dal momento che funziona sul percorso con punti in essi, con file nascosti che iniziano con un punto o anche con file con più estensioni.
Tim Krief,

20
file1=/tmp/main.one.two.sh
t=$(basename "$file1")                        # output is main.one.two.sh
name=$(echo "$file1" | sed -e 's/\.[^.]*$//') # output is /tmp/main.one.two
name=$(echo "$t" | sed -e 's/\.[^.]*$//')     # output is main.one.two

usa quello che vuoi. Qui presumo che l'ultimo .(punto) seguito dal testo sia estensione.


6
#!/bin/bash
file=/tmp/foo.bar.gz
echo $file ${file%.*}

uscite:

/tmp/foo.bar.gz /tmp/foo.bar

Si noti che viene rimossa solo l'ultima estensione.


3

La mia raccomandazione è di usare basename.
È di default in Ubuntu, codice visivamente semplice e si occupa della maggior parte dei casi.

Ecco alcuni sotto-casi per occuparsi di spazi e multi-punto / sub-estensione:

pathfile="../space fld/space -file.tar.gz"
echo ${pathfile//+(*\/|.*)}

Solitamente elimina l'estensione dal primo ., ma fallisce nel nostro ..percorso

echo **"$(basename "${pathfile%.*}")"**  
space -file.tar     # I believe we needed exatly that

Ecco una nota importante:

Ho usato virgolette doppie tra virgolette per gestire gli spazi. La virgoletta singola non passerà a causa di un sms di $. Bash è insolito e legge le "seconde" prime "virgolette" a causa dell'espansione.

Tuttavia, devi ancora pensare .hidden_files

hidden="~/.bashrc"
echo "$(basename "${hidden%.*}")"  # will produce "~" !!!  

"" non il risultato atteso. Per farlo accadere usa $HOMEo /home/user_path/
perché di nuovo bash è "insolito" e non espandere "~" (cerca bash BashPitfalls)

hidden2="$HOME/.bashrc" ;  echo '$(basename "${pathfile%.*}")'

1
#!/bin/bash
filename=program.c
name=$(basename "$filename" .c)
echo "$name"

uscite:

program

In che modo differisce dalla risposta data da Steven Penny 3 anni fa?
gniourf_gniourf,

1

Come sottolineato da Hawker65 nel commento della risposta di Chepner, la soluzione più votata non si occupa né di più estensioni (come nomefile.tar.gz), né di punti nel resto del percorso (come questo.percorso / con .dots / in.path.name). Una possibile soluzione è:

a=this.path/with.dots/in.path.name/filename.tar.gz
echo $(dirname $a)/$(basename $a | cut -d. -f1)

Questo spoglia "tar.gz" selezionando i caratteri prima della prima istanza di un punto nel nome file senza contare il percorso. Probabilmente uno non vuole eliminare le estensioni in quel modo.
Frotz,

0

Due problemi con il tuo codice:

  1. Hai usato un '(segno di spunta) anziché un `(segno di spunta indietro) per circondare i comandi che generano la stringa che desideri memorizzare nella variabile.
  2. Non hai "ripetuto" la variabile "$ nomefile" nella pipe nel comando "taglia".

Modificherei il tuo codice in "name =` echo $ nomefile | cut -f 1 -d '.' `", come mostrato di seguito (di nuovo, nota i segni di spunta che circondano la definizione della variabile del nome):

$> filename=foo.txt
$> echo $filename
foo.txt
$> name=`echo $filename | cut -f1 -d'.'`
$> echo $name
foo
$> 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.