Kann ich die Bash-Variablensubstitution verwenden, um einen Teil einer Variablen basierend auf einem Delimeter zu extrahieren? Ich versuche, den unmittelbaren Verzeichnisnamen eines Dateinamens abzurufen (in diesem Fall foo
).
$ filename=./foo/bar/baz.xml
Ich weiß, ich könnte so etwas wie
echo $filename | cut -d "/" -f 2
oder
echo $filename | awk -F "/" "{print $2}"
tun, aber es “ s wird langsam zum Verzweigen awk
/ cut
für mehrere Dateinamen.
Ich habe die verschiedenen Lösungen ein wenig profiliert unter Verwendung meiner realen Dateien:
echo | cut:
real 2m56.805s user 0m37.009s sys 1m26.067s
echo | awk:
real 2m56.282s user 0m38.157s sys 1m31.016s
@steeldrivers Variablensubstitution / Shell-Parametererweiterung:
real 0m0.660s user 0m0.421s sys 0m0.235s
@jai_s IFS-Wrangling:
real 1m26.243s user 0m13.751s sys 0m28.969s
Beide Vorschläge waren eine enorme Verbesserung gegenüber meinen bestehenden Ideen, aber die Variablensubstitution ist am schnellsten, weil dies nicht der Fall ist Forken Sie neue Prozesse.
Kommentare
- gnu.org/software/bash/manual / html_node / …
- Senden Sie alle Dateinamen an einen Aufruf von awk und es ist erheblich schneller als jede Lösung in Pure Bash
- Können Sie ein Array nicht verwenden, alle auf einmal ausführen und dann in ein neues Array einfügen?
Antwort
Sie können den kürzesten führenden Teilstring entfernen, der mit */
tmp="${filename#*/}"
übereinstimmt, und dann den längsten entfernen nachfolgender Teilstring, der mit /*
Antwort
echo $f a/b/c $ (IFS="/";set $f; echo $1) a $ (IFS="/";set $f; echo $2) b $ (IFS="/";set $f; echo $3) c
mit Platzhalter, mit dem es zu funktionieren scheint doppelte oder einfache Anführungszeichen –
f="a?b?c" $(IFS="?"; set $f; echo $1) a echo $f a*b*c (IFS="*"; set $f; echo $1) a
Ja, Sie müssen das IFS auf den Standardwert zurücksetzen.
unset IFS
Kommentare
- Oh, das gefällt mir.
- Dies ist normalerweise auch meine bevorzugte Methode, aber denken Sie daran, dass Bash unterstützt nur
$1
bis$9
mit dieser Syntax. Für 10. und spätere Argumente muss das Formular${10}
verwendet werden. - Funktioniert ' nicht, wenn
$f
enthält Platzhalter. Und Sie müssen anschließendIFS
wiederherstellen (oder dies in einer Befehlssubstitution tun, um den Wert eines Felds zu erhalten, und das schließt nachfolgende Zeilenumbrüche ab). - Das Beispiel funktioniert isoliert (innerhalb von Git Bash unter Windows), aber wenn ich vom Befehl find eine Pipe bekomme, wird folgende Fehlermeldung angezeigt:
echo: write error: Bad address
. - Okay, sieht so aus Ich muss jedes Mal
unset IFS
.
Antwort
Feed die Liste zu awk
, um sie zu beschleunigen:
awk -F "/" "{print $2}" < <(find /usr) awk -F "/" "{print $2}" < inputfile
Demonstration:
time awk -F "/" "{print $2; SUM++} END {print "number of directories found: " SUM}" < <(find /usr -type d) usr usr . . number of directories found: 16748 real 0m8.910s user 0m0.050s sys 0m0.050s
Antwort
Warum verwenden Sie nicht den Befehl“ dirname „anstelle all dieser awk / sed / Sachen schneiden?
filename=./foo/bar/baz.xml dirname $filename
Ausbeuten:
./foo/bar
Kommentare
- In diesem Fall habe ich nach dem unmittelbaren Verzeichnis gesucht, nicht nach dem vollständigen Verzeichnispfad.