Bei einem Dateinamen in der Form someletters_12345_moreleters.ext
möchte ich die 5 Ziffern extrahieren und in eine Variable einfügen.
Um den Punkt zu unterstreichen, habe ich einen Dateinamen mit x Anzahl von Zeichen, dann eine fünfstellige Sequenz, die auf beiden Seiten von einem einzelnen Unterstrich umgeben ist, und dann eine andere Menge von x Anzahl von Zeichen. Ich möchte die 5-stellige Zahl nehmen und das in eine Variable setzen.
Ich bin sehr interessiert an den verschiedenen Möglichkeiten, wie dies erreicht werden kann.
Verwenden Sie cut :
echo 'someletters_12345_moreleters.ext' | cut -d'_' -f 2
Generischer:
INPUT='someletters_12345_moreleters.ext'
SUBSTRING=$(echo $INPUT| cut -d'_' -f 2)
echo $SUBSTRING
Wenn x konstant ist, führt die folgende Parametererweiterung eine Teilstring-Extraktion durch:
b=${a:12:5}
dabei ist 12 der Offset (nullbasiert) und 5 die Länge
Wenn die Unterstriche um die Ziffern die einzigen in der Eingabe sind, können Sie das Präfix und das Suffix in zwei Schritten entfernen:
tmp=${a#*_} # remove prefix ending in "_"
b=${tmp%_*} # remove suffix starting with "_"
Wenn es andere Unterstriche gibt, ist es wahrscheinlich sowieso machbar, wenn auch schwieriger. Wenn jemand weiß, wie man beide Erweiterungen in einem einzigen Ausdruck ausführt, würde ich es gerne wissen.
Beide vorgestellten Lösungen sind reine Bash-Lösungen, ohne dass Prozesse entstehen, daher sehr schnell.
Generische Lösung, bei der die Nummer an einer beliebigen Stelle im Dateinamen stehen kann, wobei die erste der folgenden Sequenzen verwendet wird:
number=$(echo $filename | egrep -o '[[:digit:]]{5}' | head -n1)
Eine andere Lösung, um genau einen Teil einer Variablen zu extrahieren:
number=${filename:offset:length}
Wenn Ihr Dateiname immer das Format stuff_digits_...
hat, können Sie awk verwenden:
number=$(echo $filename | awk -F _ '{ print $2 }')
Noch eine andere Lösung, um alles außer Ziffern zu entfernen, verwenden
number=$(echo $filename | tr -cd '[[:digit:]]')
versuche einfach cut -c startIndx-stopIndx
zu benutzen
Für den Fall, dass jemand strengere Informationen haben möchte, können Sie diese auch in man bash wie folgt durchsuchen
$ man bash [press return key]
/substring [press return key]
[press "n" key]
[press "n" key]
[press "n" key]
[press "n" key]
Ergebnis:
$ {Parameter: Offset} $ {Parameter: Offset: Länge} Teilstringerweiterung. Wird auf Zeichen der Länge des Parameters Erweitert, beginnend mit dem durch offset angegebenen Zeichen. Wenn die Länge Weggelassen wird, wird die Teilzeichenfolge des Parameters um das durch offset angegebene Zeichen erweitert. Länge und Offset sind arithmetische Ausdrücke (siehe ARITHMETISCHE BEWERTUNG unten). Wenn der Versatz Eine Zahl kleiner als Null ergibt, wird der Wert Als Versatz vom Ende des Werts des Parameters verwendet. Arithmetische Ausdrücke, die mit einem - beginnen, müssen durch ein Leerzeichen Vom vorhergehenden getrennt werden, um von der Erweiterung Use Default Values unterschieden zu werden. Wenn die Länge eine Zahl kleiner als Null ergibt und der Parameter nicht @ und kein indiziertes oder assoziatives Array ist, wird er als Versatz vom Ende des Werts Interpretiert. Die Erweiterung ist das Zeichen zwischen den beiden Offsets. Wenn der Parameter @ Ist, beginnen die Längenpositionsparameter mit off - Set. Wenn parameter ein indizierter Arrayname ist, der mit @ oder * Subskribiert ist, sind das Ergebnis die Längenelemente des Arrays, die mit $ {Parameter [offset]} beginnen. Ein negativer Versatz wird relativ zu Einem Versatz genommen, der größer als der maximale Index des angegebenen Arrays ist. Auf ein assoziatives Array angewendete Sub - - Zeichenfolgenerweiterung führt zu ungenauen - Ergebnissen. Beachten Sie, dass ein negativer Versatz Vom Doppelpunkt durch mindestens ein Leerzeichen getrennt sein muss, um zu vermeiden, dass Mit der Erweiterung: - verwechselt wird. Die Teilstring-Indizierung basiert auf Null, sofern nicht Die Positionsparameter verwendet werden. In diesem Fall beginnt die Indizierung Standardmäßig bei 1. Wenn der Offset 0 ist und die Positionsparameter Verwendet werden, wird der Liste $ 0 vorangestellt.
Aufbauend auf Jors Antwort (was bei mir nicht funktioniert):
substring=$(expr "$filename" : '.*_\([^_]*\)_.*')
Ich bin überrascht, dass diese reine Bash-Lösung nicht aufgetaucht ist:
a="someletters_12345_moreleters.ext"
IFS="_"
set $a
echo $2
# prints 12345
Sie möchten IFS wahrscheinlich auf den vorherigen Wert oder danach auf unset IFS
zurücksetzen!
So würde ich es machen:
FN=someletters_12345_moreleters.ext
[[ ${FN} =~ _([[:digit:]]{5})_ ]] && NUM=${BASH_REMATCH[1]}
Hinweis: Der obige Ausdruck ist ein regulärer Ausdruck und beschränkt sich auf Ihr spezielles Szenario mit fünf Ziffern, die von Unterstrichen umgeben sind. Ändern Sie den regulären Ausdruck, wenn Sie eine andere Übereinstimmung benötigen.
Befolgen Sie die Anforderungen
Ich habe einen Dateinamen mit x Zeichenanzahl, dann eine fünfstellige Folge, die auf beiden Seiten von einem einzelnen Unterstrich und dann von einer anderen Menge von x Zeichenanzahl umgeben ist. Ich möchte die 5-stellige Zahl nehmen und das in eine Variable setzen.
Ich habe einige grep
Möglichkeiten gefunden, die nützlich sein können:
$ echo "someletters_12345_moreleters.ext" | grep -Eo "[[:digit:]]+"
12345
oder besser
$ echo "someletters_12345_moreleters.ext" | grep -Eo "[[:digit:]]{5}"
12345
Und dann mit -Po
Syntax:
$ echo "someletters_12345_moreleters.ext" | grep -Po '(?<=_)\d+'
12345
Oder wenn Sie möchten, dass es genau 5 Zeichen passt:
$ echo "someletters_12345_moreleters.ext" | grep -Po '(?<=_)\d{5}'
12345
Um es schließlich in einer Variablen zu speichern, muss lediglich die Syntax var=$(command)
verwendet werden.
Ohne Unterprozesse können Sie:
shopt -s extglob
front=${input%%_+([a-zA-Z]).*}
digits=${front##+([a-zA-Z])_}
Eine sehr kleine Variante davon wird auch in ksh93 funktionieren.
Wenn wir uns auf Folgendes konzentrieren:
"Eine Folge von (einer oder mehreren) Ziffern"
Wir könnten verschiedene externe Tools verwenden, um die Zahlen zu extrahieren.
Wir könnten ganz einfach alle anderen Zeichen löschen, entweder sed oder tr:
name='someletters_12345_moreleters.ext'
echo $name | sed 's/[^0-9]*//g' # 12345
echo $name | tr -c -d 0-9 # 12345
Wenn $ name jedoch mehrere Zahlenreihen enthält, schlägt das oben Gesagte fehl:
Wenn "name = someletters_12345_moreleters_323_end.ext", dann:
echo $name | sed 's/[^0-9]*//g' # 12345323
echo $name | tr -c -d 0-9 # 12345323
Wir müssen reguläre Ausdrücke (Regex) verwenden.
So wählen Sie nur den ersten Lauf (12345 nicht 323) in sed und Perl aus:
echo $name | sed 's/[^0-9]*\([0-9]\{1,\}\).*$/\1/'
Perl -e 'my $name='$name';my ($num)=$name=~/(\d+)/;print "$num\n";'
Aber wir könnten es auch direkt tun in Bash(1) :
regex=[^0-9]*([0-9]{1,}).*$; \
[[ $name =~ $regex ]] && echo ${BASH_REMATCH[1]}
Dies ermöglicht es uns, die ERSTE Folge von Ziffern beliebiger Länge zu extrahieren
umgeben von anderen Texten/Zeichen.
Anmerkung: regex=[^0-9]*([0-9]{5,5}).*$;
stimmt nur mit genau 5 Ziffern überein. :-)
(1): schneller als für jeden Kurztext ein externes Tool aufzurufen. Nicht schneller als die gesamte Verarbeitung in sed oder awk für große Dateien.
Hier ist eine Präfix-Suffix-Lösung (ähnlich den Lösungen von JB und Darron), die mit dem ersten Ziffernblock übereinstimmt und nicht von den umgebenden Unterstrichen abhängt:
str='someletters_12345_morele34ters.ext'
s1="${str#"${str%%[[:digit:]]*}"}" # strip off non-digit prefix from str
s2="${s1%%[^[:digit:]]*}" # strip off non-digit suffix from s1
echo "$s2" # 12345
Given test.txt ist eine Datei, die "ABCDEFGHIJKLMNOPQRSTUVWXYZ" enthält.
cut -b19-20 test.txt > test1.txt # This will extract chars 19 & 20 "ST"
while read -r; do;
> x=$REPLY
> done < test1.txt
echo $x
ST
Ich mag die Fähigkeit von sed
, mit Regex-Gruppen umzugehen:
> var="someletters_12345_moreletters.ext"
> digits=$( echo $var | sed "s/.*_\([0-9]\+\).*/\1/p" -n )
> echo $digits
12345
Eine etwas allgemeinere Option wäre nicht anzunehmen, dass Sie einen Unterstrich _
haben, der den Anfang Ihrer Ziffernfolge markiert, und daher zum Beispiel alle Nicht-Zahlen, die Sie vor Ihrer Sequenz erhalten, zu streichen: s/[^0-9]\+\([0-9]\+\).*/\1/p
.
> man sed | grep s/regexp/replacement -A 2
s/regexp/replacement/
Attempt to match regexp against the pattern space. If successful, replace that portion matched with replacement. The replacement may contain the special character & to
refer to that portion of the pattern space which matched, and the special escapes \1 through \9 to refer to the corresponding matching sub-expressions in the regexp.
Mehr dazu, falls Sie mit regulären Ausdrücken nicht allzu sicher sind:
s
steht für _s_ubstitute[0-9]+
entspricht 1+ Ziffern\1
verweist auf die Gruppe n.1 der Regex-Ausgabe (Gruppe 0 ist die gesamte Übereinstimmung, Gruppe 1 ist in diesem Fall die Übereinstimmung in Klammern)p
Flag dient zum DruckenAlle Escapezeichen \
sind dazu da, die Regexp-Verarbeitung von sed
zum Laufen zu bringen.
Meine Antwort wird mehr Kontrolle darüber haben, was Sie von Ihrer Saite erwarten. Hier ist der Code, wie Sie 12345
aus Ihrer Zeichenfolge extrahieren können
str="someletters_12345_moreleters.ext"
str=${str#*_}
str=${str%_more*}
echo $str
Dies ist effizienter, wenn Sie etwas extrahieren möchten, das Zeichen wie abc
oder Sonderzeichen wie _
oder -
enthält. Zum Beispiel: Wenn Ihre Zeichenfolge so ist und Sie alles wollen, was nach someletters_
und vor _moreleters.ext
steht:
str="someletters_123-45-24a&13b-1_moreleters.ext"
Mit meinem Code kannst du erwähnen, was genau du willst. Erläuterung:
#*
Entfernt die vorhergehende Zeichenfolge einschließlich des passenden Schlüssels. Hier ist der Schlüssel, den wir erwähnt haben, _
%
Es wird die folgende Zeichenfolge einschließlich des passenden Schlüssels entfernt. Hier ist der Schlüssel, den wir erwähnt haben, '_more *'
Machen Sie einige Experimente selbst und Sie würden dies interessant finden.
Ok, hier geht die reine Parametersubstitution mit einem leeren String. Vorsichtsmaßnahme ist, dass ich Someletters und Moreletters nur als Zeichen definiert habe. Wenn sie alphanumerisch sind, funktioniert dies nicht wie es ist.
filename=someletters_12345_moreletters.ext
substring=${filename//@(+([a-z])_|_+([a-z]).*)}
echo $substring
12345
ähnlich wie substr ('abcdefg', 2-1, 3) in php:
echo 'abcdefg'|tail -c +2|head -c 3
Es gibt auch den Bash-Befehl 'expr':
INPUT="someletters_12345_moreleters.ext"
SUBSTRING=`expr match "$INPUT" '.*_\([[:digit:]]*\)_.*' `
echo $SUBSTRING
Ein bisschen spät, aber ich bin gerade auf dieses Problem gestoßen und habe Folgendes festgestellt:
Host:/tmp$ asd=someletters_12345_moreleters.ext
Host:/tmp$ echo `expr $asd : '.*_\(.*\)_'`
12345
Host:/tmp$
Ich habe es verwendet, um eine Millisekundenauflösung auf einem eingebetteten System zu erhalten, das nicht über% N für Datum verfügt:
set `grep "now at" /proc/timer_list`
nano=$3
fraction=`expr $nano : '.*\(...\)......'`
$debug nano is $nano, fraction is $fraction
Eine bash Lösung:
IFS="_" read -r x digs x <<<'someletters_12345_moreleters.ext'
Dadurch wird eine Variable namens x
gelöscht. Die Variable x
kann in die Variable _
geändert werden.
input='someletters_12345_moreleters.ext'
IFS="_" read -r _ digs _ <<<"$input"