Ich habe unsere durchsucht, kann aber keine Lösung finden, um alle Zeichenfolgen zwischen zwei Zeichen zu einem Array mit Bash zu extrahieren.
Ich finde
sed -n 's/.*\[\(.*\)\].*/\1/p'
Dies zeigt mir aber nur den letzten Eintrag.
Mein String sieht aus wie:
var="[a1] [b1] [123] [Text text] [0x0]"
Ich möchte ein Array wie dieses:
arr[0]="a1"
arr[1]="b1"
arr[2]="123"
arr[3]="Text text"
arr[4]="0x0"
Also suche ich nach Stings zwischen [und] und lade es in ein Array ohne [und].
Danke für die Hilfe!
4 Antworten
Es gibt viele Vorschläge, die hier möglicherweise bereits für Sie funktionieren, jedoch möglicherweise nicht von Ihren Daten abhängen. Das Ersetzen eines Kommas durch das aktuelle Feldtrennzeichen von ] [
funktioniert beispielsweise, sofern in Ihren Feldern keine Kommas eingebettet sind. Welche Ihre Beispieldaten nicht haben, aber man weiß nie. :) :)
Eine ideale Lösung wäre, etwas als Feldtrennzeichen zu verwenden, das garantiert niemals Teil Ihres Feldes ist, wie eine Null. Dies ist jedoch auf tragbare Weise schwierig (d. H. Ohne zu wissen, welche Tools verfügbar sind). Eine weniger extreme Haltung könnte also darin bestehen, eine neue Zeile als Trennzeichen zu verwenden:
var="[a1] [b1] [123] [Text text] [0x0]"
mapfile -t arr < <(sed $'s/^\[//;s/] \[/\\\n/g;s/]$//' <<<"$var")
declare -p arr
Was dazu führen würde:
declare -a arr='([0]="a1" [1]="b1" [2]="123" [3]="Text text" [4]="0x0")'
Dies entspricht funktional der von Inian bereitgestellten awk
-Lösung. Beachten Sie, dass für mapfile
Bash Version 4 oder höher erforderlich ist.
Das heißt, Sie können dies auch ausschließlich innerhalb von Bash tun, ohne sich auf externe Tools wie sed verlassen zu müssen:
arr=( $var )
last=0
for i in "${!arr[@]}"; do
if [[ ${arr[$i]} != \[* ]]; then
arr[$last]="${arr[$last]} ${arr[$i]}"
unset arr[$i]
continue
fi
last=$i
done
for i in "${!arr[@]}"; do
arr[$i]="${arr[$i]:1:$((${#arr[$i]}-2))}"
done
Zu diesem Zeitpunkt führt declare -p arr
zu:
declare -a arr='([0]="a1" [1]="b1" [2]="123" [3]="Text text" [5]="0x0")'
Dies saugt Ihr $var
in das Array $arr[]
mit durch Leerzeichen getrennten Feldern ein und reduziert die Felder basierend darauf, ob sie mit einer eckigen Klammer beginnen. Es geht dann durch die Felder und ersetzt sie durch die Teilzeichenfolge, die das erste und letzte Zeichen entfernt. Es mag etwas weniger belastbar und schwerer zu lesen sein, aber es ist alles in Bash. :) :)
Es gibt keinen einfachen Weg, dies zu tun. Ich würde eine Schleife verwenden, um sie einzeln zu extrahieren:
var="[a1] [b1] [123] [Text text] [0x0]"
regex='\[([^]]*)\](.*)'
while [[ $var =~ $regex ]]; do
arr+=("${BASH_REMATCH[1]}")
var=${BASH_REMATCH[2]}
done
Im regulären Ausdruck erfasst \[([^]]*)\]
alles nach dem ersten [
bis zum nächsten ]
. (.*)
erfasst danach alles für die nächste Iteration.
Sie können declare -n
in bash
4.3 oder höher verwenden, um das Erscheinungsbild ein wenig weniger einschüchternd zu gestalten.
declare -n m1=BASH_REMATCH[1] m2=BASH_REMATCH[2]
regex='\[([^]]*)\](.*)'
var="[a1] [b1] [123] [Text text] [0x0]"
while [[ $var =~ $regex ]]; do
arr+=("$m1")
var=$m2
done
$ IFS=, arr=($(sed 's/\] \[/","/g;s/\]/"/;s/\[/"/' <<< "$var")); echo "${arr[3]}"
"Text text"
Mit GNU awk für Multi-Char RS und RT und neueren Versionen von Bash für Mapfile:
$ mapfile -t arr < <(echo "$var" | awk -v RS='[^][]+' 'NR%2{print RT}')
$ declare -p arr
declare -a arr=([0]="a1" [1]="b1" [2]="123" [3]="Text text" [4]="0x0")