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!

1
suf noK 18 Apr. 2018 im 17:48

4 Antworten

Beste Antwort

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. :) :)

-1
ghoti 18 Apr. 2018 im 16:27

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
3
chepner 18 Apr. 2018 im 15:08
$ IFS=, arr=($(sed 's/\] \[/","/g;s/\]/"/;s/\[/"/' <<< "$var")); echo "${arr[3]}"

"Text text"
0
karakfa 18 Apr. 2018 im 15:15

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")
-1
Ed Morton 18 Apr. 2018 im 15:36