Wie bekomme ich nur Dateinamen mit sed

17

Wie kann ich mit sed nur den Dateinamen ermitteln? Ich habe das

out_file=$(echo $in_file|sed "s/\(.*\.\).*/\1mp4/g")

Aber ich verstehe den Weg auch /root/video.mp4und ich will nur video.mp4.

Shixons
quelle

Antworten:

26

basenamevon den GNU- Coreutils können Ihnen dabei helfen:

$ basename /root/video.mp4
video.mp4

Wenn Sie die Erweiterung der Datei bereits kennen, können Sie sie basenamemithilfe der Syntax aufrufen basename NAME [SUFFIX], um sie zu entfernen:

$ basename /root/video.mp4 .mp4
video

Oder eine andere Option wäre, alles nach dem letzten Punkt zu schneiden sed:

$ basename /root/video.old.mp4 | sed 's/\.[^.]*$//'
video.old
uloBasEI
quelle
3
Verwenden, sed 's/\.[^.]*$//'wie Sie haben, wird fehlschlagen für (versteckte) .filenameund .und ..Verzeichnisse
Peter.O
9

Die einfachste Lösung ist, alles bis zum letzten Auftreten von /:

echo /root/video.mp4 | sed 's/.*\///'

Gänseblümchen
quelle
5

Verwenden Sie eine der folgenden Möglichkeiten:

out_file="${in_file##*/}"

out_file="$(basename $in_file)"

out_file="$(echo $in_file | sed 's=.*/==')"

out_file="$(echo $in_file | awk -F"/" '{ print $NF }')"

ps. Sie erhalten dieselbe Zeichenfolge, da sie in Ihrer Anweisung \(.*\.\)vom Anfang bis zum Punkt ( /root/video.) mit der Zeichenfolge übereinstimmt. Anschließend fügen Sie manuell .mp4die Zeichenfolge hinzu, die der ursprünglichen Zeichenfolge entspricht. Sie sollten s=.*\([^/]*\)=\1=stattdessen verwenden.

Update: (Erstes ist jetzt behoben)

Um den einzigen Dateinamen ohne Erweiterung zu erhalten, können Sie:

out_file="$(echo $in_file | sed 's=.*/==;s/\.[^.]*$/.new_ext/')"

out_file="$(echo $in_file | sed 's=\([^/]*\)\.[^./]*$=\1.new_ext=')"

out_file="$(echo $in_file | awk -F"/" '{ gsub (/\.[^/.]*$/,".new_ext",$NF);print $NF }'
eilen
quelle
Bei jeder dieser Methoden erhalte ich jedoch den Dateinamen mit dem Format und muss nur den Dateinamen ermitteln und ein neues Format manuell eingeben.
Shixons
Ah, das macht Sinn. Ich habe meine Antwort aktualisiert.
Ansturm
@rush: Es wird Randfälle geben, zB für eine Datei mit dem Namen my.file.tar.gz.
donothingsuccessfully
@donothingsuccessfully gab es ein fehlendes Punktsymbol im letzten sedund awk. Fest. Vielen Dank.
Ansturm
4

Eine der Grundlagen für die Verwendung von Regex ist, dass Muster bei der Angabe der Platzhalter von Natur aus gierig sind. Die von @uloBasEI vorgeschlagene Antwort ist sicherlich eine funktionierende Antwort, erfordert jedoch auch die Verwendung des Befehls basename. Die ursprüngliche Frage von @Shixons fordert eine Lösung, die nur sed verwendet.

Bevor Sie fortfahren, ist es immer hilfreich zu wissen, welche Version von sed das Ziel ist. Ich gehe von BSD aus (wie mit OSX ausgeliefert).

Erstens funktioniert das in der ursprünglichen Frage vorgeschlagene Muster nicht, da es vom Anfang der Eingabezeichenfolge bis einschließlich des letzten Punkts alles erfasst. Ohne Anker verschluckt diese Suche alles von links nach rechts. Das mit "/ 1" übereinstimmende Muster ist daher alles bis einschließlich des letzten Punkts. Sogar ein Dateiname mit mehreren Punkten wird als Ganzes verschluckt. Überhaupt nicht das gewünschte Ergebnis.

Der erste Schritt ist die Festlegung einer Strategie zur Identifizierung von Mustern. Hier möchten Sie alles loswerden, was links vom Dateinamen steht (wir werden uns später mit der Erweiterung befassen):

out_file="$(echo $in_file | sed 's/^\(\/.*\/\)*.*/\1/')"

Die Suche beginnt am Anfang der Zeichenfolge. Es entspricht einem Muster von "/.*" null oder mehrmals und löscht anschließend alles. Wir drucken die passenden Muster mit "\ 1". Wir suchen nicht global; Wir suchen ab dem Anfang der Zeichenfolge, indem wir den Anker ^ angeben.

Wir erhalten eine bessere Übersichtlichkeit, indem wir die Option "-E" aktivieren, damit wir Klammern nicht umgehen müssen:

out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*.*/\1/')"

Jetzt haben wir den Teil auf der linken Seite. Fügen wir das Teil rechts hinzu. Beachten Sie, dass wir den linken Teil als Muster behalten müssen, da wir so festlegen können, dass er null oder mehrmals vorkommt. Jetzt fügen wir nur noch ein Muster für den Teil rechts hinzu:

out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)/\2/')"

Wir drucken nur die zweite Übereinstimmung aus und verwerfen dabei alles außer dem Dateinamen. Die Dateinamenerweiterung muss jedoch noch entfernt werden.

out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)\..*$/\2/')"

Das "$" am Ende ist optional.

Zum Schluss überarbeiten Sie die neue Erweiterung wie folgt:

out_file="$(echo $in_file | sed -E 's/^(\/.*\/)*(.*)\..*$/\2.mp4/')"

Eine zusätzliche Optimierung besteht darin, den ersten Schrägstrich optional zu machen, um relative Pfade zu behandeln:

out_file="$(echo $in_file | sed -E 's/^([\/]?.*\/)*(.*)\..*$/\2.mp4/')"

Ich bin auf diese Frage gestoßen, weil ich faul war, als ich nach einem Sed-Muster suchte, um den Basisnamen zu ersetzen . Ich arbeite an einem abgespeckten System, auf dem dieser Befehl nicht installiert ist.

markeissler
quelle