Wie kann ich die Erweiterung eines Dateinamens in einem Shell-Skript entfernen?


142

Was ist los mit dem folgenden Code?

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

So wie es ist, bekomme ich die Literalzeichenfolge $filename | cut -f1 -d'.', aber wenn ich die Anführungszeichen entferne, bekomme ich nichts. In der Zwischenzeit tippen

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

in einer Shell gibt mir die Ausgabe, die ich will , test. Ich weiß bereits, dass $filenameder richtige Wert zugewiesen wurde. Ich möchte einer Variablen den Dateinamen ohne die Erweiterung zuweisen.


6
basename $filename .exewürde das gleiche tun. Vorausgesetzt, Sie wissen immer, welche Erweiterung Sie entfernen möchten.
Mpe

6
@mpe, du meinst basename "$filename" .exe. Andernfalls wären Dateinamen mit Leerzeichen eine schlechte Nachricht.
Charles Duffy

Antworten:


112

Sie sollten die unter Verwendung von Befehlsersetzung Syntax , $(command)wenn Sie einen Befehl in / script Befehl ausgeführt werden sollen.

Also wäre deine Linie

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

Code-Erklärung:

  1. echoHolen Sie sich den Wert der Variablen $filenameund senden Sie ihn an die Standardausgabe
  2. Wir nehmen dann die Ausgabe und leiten sie an den cutBefehl weiter
  3. Der cutwird die verwenden. als Trennzeichen (auch als Trennzeichen bezeichnet) zum Schneiden des Strings in Segmente und durch -fAuswählen des Segments, das in der Ausgabe enthalten sein soll
  4. Dann $()erhält die Befehlssubstitution die Ausgabe und gibt ihren Wert zurück
  5. Der zurückgegebene Wert wird der benannten Variablen zugewiesen name

Beachten Sie, dass dies den Teil der Variablen bis zur ersten Periode angibt .:

$ 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

Vielen Dank. Mir ist auch aufgefallen, dass ich den Befehl echo verwenden muss. name =echo $filename | cut -f1 -d'.'
mimicocotopus

17
Backticks werden von POSIX nicht $()mehr unterstützt.
Jordanm

3
Das Gabeln und Paspeln, um ein paar Zeichen zu erreichen, ist die schlechteste Lösung, die man sich vorstellen kann.
Jens

39
Das Problem mit dieser Antwort ist, dass angenommen wird, dass die Eingabezeichenfolge NUR einen Punkt hat ... @chepner unten hat eine viel bessere Lösung ... name = $ {Dateiname%. *}
Scott Stensland

4
Diese Antwort ist charakteristisch für Anfänger und sollte nicht verbreitet werden. Verwenden Sie den eingebauten Mechanismus, wie in der Antwort von chepner beschrieben
neric

256

Sie können auch die Parametererweiterung verwenden:

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

6
hier die Erklärung des Befehls: gnu.org/software/bash/manual/html_node/…
Carlos Robles

1
Und hier wollte ich gerade benutzen echo -n "This.File.Has.Periods.In.It.txt" | awk -F. '{$NF=""; print $0}' | tr ' ' '.' | rev | cut -c 2- | rev. Vielen Dank.
user208145

1
Funktioniert dies mit Dateien mit mehreren Erweiterungen wie image.png.gz?
Hawker65

11
%.*entfernt nur die letzte Erweiterung; Wenn Sie alle Erweiterungen entfernen möchten , verwenden Sie %%.*.
Chepper

1
Dies scheint viel besser zu funktionieren als die akzeptierte Antwort. Die letzte Erweiterung wird nur rasiert, wenn die Datei mehr als einen Punkt enthält, während beim Ausschneiden alles nach dem ersten Punkt entfernt wird.
Dale Anderson


20

Wenn Ihr Dateiname einen Punkt enthält (außer dem der Erweiterung), verwenden Sie diesen:

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

1
Ich habe die mittlere Drehzahl vergessen, aber als ich sie sah, war das großartig!
höchste Pooba

Es ist sogar noch besser, wenn eine -sOption angegeben wird cut, sodass eine leere Zeichenfolge zurückgegeben wird, wenn der Dateiname keinen Punkt enthält.
Hibou57

1
Dies sollte meiner Meinung nach die akzeptierte Antwort sein, da es auf einem Pfad mit Punkten darin funktioniert, mit versteckten Dateien, die mit einem Punkt beginnen, oder sogar mit Dateien mit mehreren Erweiterungen.
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

Verwenden Sie, was Sie wollen. Hier gehe ich davon aus, dass der letzte .(Punkt) gefolgt von Text eine Erweiterung ist.


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

Ausgänge:

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

Beachten Sie, dass nur die letzte Erweiterung entfernt wird.


3

Meine Empfehlung ist zu verwenden basename.
Es ist standardmäßig in Ubuntu visuell einfacher Code und behandelt die meisten Fälle.

Hier sind einige Unterfälle, die sich mit Leerzeichen und Mehrpunkt- / Untererweiterungen befassen:

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

Normalerweise wird die Erweiterung von Anfang an .beseitigt, aber auf unserem ..Weg scheitert sie

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

Hier ist ein wichtiger Hinweis:

Ich habe doppelte Anführungszeichen in doppelten Anführungszeichen verwendet, um mit Leerzeichen umzugehen. Ein einfaches Anführungszeichen wird aufgrund einer SMS mit dem $ nicht bestanden. Bash ist ungewöhnlich und liest aufgrund der Erweiterung "zweite" erste "Anführungszeichen".

Sie müssen jedoch noch daran denken .hidden_files

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

nicht das erwartete "" Ergebnis. Um dies zu ermöglichen, verwenden Sie $HOMEoder /home/user_path/
weil bash wieder "ungewöhnlich" ist und erweitern Sie nicht "~" (suchen Sie nach bash BashPitfalls)

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

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

Ausgänge:

program

Wie unterscheidet sich das von der Antwort von Steven Penny vor 3 Jahren?
gniourf_gniourf

1

Wie Hawker65 im Kommentar der Antwort von chepner hervorhob, kümmert sich die am häufigsten gewählte Lösung weder um mehrere Erweiterungen (wie filename.tar.gz) noch um Punkte im Rest des Pfades (wie this.path / with) .dots / in.path.name). Eine mögliche Lösung ist:

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

Dieser entfernt "tar.gz", indem Zeichen vor der ersten Instanz eines Punkts im Dateinamen ausgewählt werden, ohne den Pfad zu zählen. Man möchte Erweiterungen wahrscheinlich nicht so entfernen.
Frotz

0

Zwei Probleme mit Ihrem Code:

  1. Sie haben ein '(Häkchen) anstelle eines' (Zurück-Häkchen) verwendet, um die Befehle zu umgeben, die die Zeichenfolge generieren, die Sie in der Variablen speichern möchten.
  2. Sie haben die Variable "$ filename" nicht in die Pipe im Befehl "cut" "zurückgeführt".

Ich würde Ihren Code in "name =` echo $ filename | cut -f 1 -d 'ändern. " `", wie unten gezeigt (beachten Sie erneut die hinteren Häkchen, die die Definition der Namensvariablen umgeben):

$> filename=foo.txt
$> echo $filename
foo.txt
$> name=`echo $filename | cut -f1 -d'.'`
$> echo $name
foo
$> 
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.