
Ich möchte eine Teilzeichenfolge eines Dateinamens aus einem Pfad unbekannter Länge extrahieren. Ich kann diese beiden Teile separat ausführen, aber ich frage mich, ob es eine Möglichkeit gibt, die beiden ohne temporäre Variable zu kombinieren?
INPUT_PATH=/path/to/subfolder/file_17.txt
# I would like to extract "17", the filname will always be 'file_XX.txt'
# The subfolder name is variable length
TMP=$(basename ${INPUT_PATH})
FILE_NUMBER=${TMP:5:2}
echo ${FILE_NUMBER} # This works as expected
Ich habe es versucht ${$(basename $INPUT_PATH):5:2}
, aber es tritt ein fehlerhafter Ersetzungsfehler auf. Gibt es einen Trick, um das zu beheben?
Antwort1
Einen anderen Ansatz für das Problem wählen und eine einzeilige Lösung anbieten, die nur bash
die Funktionalität nutzt:
$ cat demo.sh
#!/bin/bash
INPUT_PATH=/path/to/subfolder/file_17.txt
FILE_NUMBER=${INPUT_PATH:((${#INPUT_PATH} -6)):2}
echo ${FILE_NUMBER}
$
$./demo.sh
17
Ein einfacherer Ansatz wäre, vom Ende der Zeichenfolge rückwärts zu zählen, d. h.
FILE_NUMBER=${INPUT_PATH: -6:2}
Offensichtlich hängt diese Lösung davon ab, dass die Zeichenfolgenvariable mit „##XXXX“ endet, wobei „##“ die beiden relevanten Ziffern und „XXXX“ die letzten 4 Zeichen der Zeichenfolge sind.
Antwort2
Da Sie Bash verwenden, können Sie Regex-Matching nutzen:
if [[ $input =~ ([[:digit:]]+)\.txt$ ]]; then
file_num=${BASH_REMATCH[1]}
fi
Antwort3
Am einfachsten ist es, FILE_NUMBER anstelle von TMP zu verwenden:
FILE_NUMBER=$(basename ${INPUT_PATH})
FILE_NUMBER=${FILE_NUMBER:5:2}
Außerdem ist die Verwendung der Parametererweiterung schneller als der Aufruf von basename:
FILE_NUMBER=${INPUT_PATH##*/}
FILE_NUMBER=${FILE_NUMBER:5:2}
Sie können sed verwenden, um alles in einer einzigen Zeile zu erledigen, aber das ist langsamer und weniger lesbar:
FILE_NUMBER=$(sed 's|.*/||;s/.....\(..\).*/\1/' <<<"$INPUT_PATH")
Antwort4
Für diejenigen, die zsh
anstelle von verwenden bash
:
6. bis 7. Zeichen derSchwanz(von zurückgegebener Teil
basename
) eines Pfades:num=${${a_path:t}[6,7]}
(
$var:t
voncsh
um diet
Krankheit zu bekommen)Sie können aus Kompatibilitätsgründen auch
num=${"$(basename -- "$a_path")":5:2}
denksh93
${var:offset:length}
Operator verwenden, der kürzlich hinzugefügt wurdezsh
. Die Befehlsersetzung muss in Anführungszeichen gesetzt werden, damit kein Array entsteht, sodass:5:2
ein Zeichenbereich statt eines Bereichs von Array-Elementen ausgewählt wird. Die Verwendung der Befehlsersetzung und -ausführungbasename
wäre jedoch weniger effizient und weniger zuverlässig als die Verwendungzsh
des integrierten:t
Operators.erste Ziffernfolge in derSchwanz:
num=${(MS)${a_path:t}##<->}
${var##pattern}
ist der KSH-Operator, der den längsten führenden String entfernt, der mitpattern
„von“ übereinstimmt$var
. Mit demM
FlagM
wird der angehängte Teil zurückgegeben, anstatt entfernt zu werden, undS
es wird nachS
Unterstrings gesucht, nicht nur am Anfang. Und<->
es stimmt mit jeder Ziffernfolge überein (<x-y>
mit nicht angegebenen Grenzen).Ziffernfolge nach a
_
imSchwanz:num=${${a_path:t}/(#m)*_(<->)*/$match[1]}
(benötigt
extendedglob
; Vorsicht, es gibt das vollständige Ende zurück, wenn kein_digits
in vorhanden ist$a_path
).Oder:
[[ $a_path:t =~ '_([[:digit:]]+)' ]] && num=$match[1]