2924 Stimmen

Wie splitte ich eine Zeichenkette an einem Begrenzer in der Bash?

Ich habe diese Zeichenfolge in einer Variablen gespeichert:

IN="bla@some.com;john@home.com"

Nun möchte ich die Zeichenketten aufteilen nach ; Begrenzungszeichen, so dass ich habe:

ADDR1="bla@some.com"
ADDR2="john@home.com"

Ich brauche nicht unbedingt die ADDR1 y ADDR2 Variablen. Wenn sie Elemente eines Arrays sind, ist das sogar noch besser.


Nach den Vorschlägen aus den nachstehenden Antworten kam ich zu folgendem Ergebnis, das dem entspricht, was ich wollte:

#!/usr/bin/env bash

IN="bla@some.com;john@home.com"

mails=$(echo $IN | tr ";" "\n")

for addr in $mails
do
    echo "> [$addr]"
done

Ausgabe:

> [bla@some.com]
> [john@home.com]

Es gab eine Lösung, die die Einstellung Interner_Feld_Trenner (IFS) an ; . Ich bin mir nicht sicher, was mit dieser Antwort passiert ist, wie setzt man IFS zurück zum Standard?

RE: IFS Lösung, ich habe das ausprobiert und es funktioniert, ich behalte die alte IFS und dann wiederherstellen:

IN="bla@some.com;john@home.com"

OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
    echo "> [$x]"
done

IFS=$OIFS

Übrigens, als ich versuchte

mails2=($IN)

Ich habe nur die erste Zeichenkette erhalten, wenn ich sie in einer Schleife ohne Klammern ausgedruckt habe $IN es funktioniert.

30 Stimmen

In Bezug auf Ihr "Edit2": Sie können einfach "IFS aufheben" und der Standardzustand wird wiederhergestellt. Es besteht keine Notwendigkeit, ihn explizit zu speichern und wiederherzustellen, es sei denn, Sie haben Grund zu der Annahme, dass er bereits auf einen anderen Wert als den Standardwert gesetzt wurde. Wenn Sie dies innerhalb einer Funktion tun (und wenn Sie es nicht tun, warum nicht?), können Sie IFS als lokale Variable setzen, die nach dem Verlassen der Funktion auf ihren vorherigen Wert zurückgesetzt wird.

33 Stimmen

@BrooksMoses: (a) +1 für die Verwendung local IFS=... wenn möglich; (b) -1 für unset IFS wird IFS nicht genau auf seinen Standardwert zurückgesetzt, obwohl ich glaube, dass sich ein nicht gesetzter IFS genauso verhält wie der Standardwert von IFS ($' \t\n '), aber es scheint eine schlechte Praxis zu sein, blind davon auszugehen, dass Ihr Code niemals mit einem benutzerdefinierten IFS-Wert aufgerufen werden wird; (c) eine andere Idee ist, eine Subshell aufzurufen: (IFS=$custom; ...) Wenn die Subshell beendet wird, kehrt IFS zu dem ursprünglichen Zustand zurück.

0 Stimmen

Ich möchte nur einen kurzen Blick auf die Pfade werfen, um zu entscheiden, wohin ich eine ausführbare Datei werfen soll, also habe ich auf die Ausführung von ruby -e "puts ENV.fetch('PATH').split(':')" . Wenn Sie reine Bash bleiben wollen, hilft es nicht, wenn Sie eine beliebige Skriptsprache die eine eingebaute Aufteilung hat, ist einfacher.

2voto

Eduardo Lucio Punkte 1103

Hier ist meine Antwort!

DELIMITER_VAL='='

read -d '' F_ABOUT_DISTRO_R <<"EOF"
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=14.04
DISTRIB_CODENAME=trusty
DISTRIB_DESCRIPTION="Ubuntu 14.04.4 LTS"
NAME="Ubuntu"
VERSION="14.04.4 LTS, Trusty Tahr"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 14.04.4 LTS"
VERSION_ID="14.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
EOF

SPLIT_NOW=$(awk -F$DELIMITER_VAL '{for(i=1;i<=NF;i++){printf "%s\n", $i}}' <<<"${F_ABOUT_DISTRO_R}")
while read -r line; do
   SPLIT+=("$line")
done <<< "$SPLIT_NOW"
for i in "${SPLIT[@]}"; do
    echo "$i"
done

Warum ist dieser Ansatz "der beste" für mich?

Dafür gibt es zwei Gründe:

  1. Sie haben nicht zu fliehen brauchen das Begrenzungszeichen;
  2. Sie werden nicht über Problem mit Leerzeichen . Der Wert wird in dem Array ordnungsgemäß getrennt.

0 Stimmen

ZU IHRER INFORMATION, /etc/os-release y /etc/lsb-release sind als Quelle gedacht und werden nicht geparst. Ihre Methode ist also wirklich falsch. Außerdem beantworten Sie nicht ganz die Frage nach eine Zeichenkette auf ein Begrenzungszeichen verschütten.

1 Stimmen

Der Awk-Unfug ist nur ein ungeschickter Weg zur Neuimplementierung von IFS="=" read -r

0 Stimmen

@gniourf_gniourf Die "Release"-Dateien sind für die Frage irrelevant. Ich glaube, Sie haben sich nicht die DELIMITER_VAL='=' variabel, richtig? Wie auch immer, danke für den Beitrag.

2voto

NevilleDNZ Punkte 1237

Zwei Bourne-ähnliche Alternativen, die beide keine Bash-Arrays benötigen:

Fall 1 : Halten Sie es schön und einfach: Verwenden Sie eine NewLine als Datensatz-Trennzeichen... z.B.

IN="bla@some.com
john@home.com"

while read i; do
  # process "$i" ... eg.
    echo "[email:$i]"
done <<< "$IN"

Hinweis: In diesem ersten Fall wird kein Unterprozess gegabelt, der bei der Listenbearbeitung hilft.

Idee: Vielleicht lohnt es sich, NL ausgiebig zu nutzen intern und erst bei der Erstellung des Endergebnisses in eine andere RS konvertiert wird extern .

Fall 2 : Verwendung eines ";" als Satztrennzeichen... z. B.

NL="
" IRS=";" ORS=";"

conv_IRS() {
  exec tr "$1" "$NL"
}

conv_ORS() {
  exec tr "$NL" "$1"
}

IN="bla@some.com;john@home.com"
IN="$(conv_IRS ";" <<< "$IN")"

while read i; do
  # process "$i" ... eg.
    echo -n "[email:$i]$ORS"
done <<< "$IN"

In beiden Fällen kann eine Teilliste innerhalb der Schleife erstellt werden und bleibt auch nach Abschluss der Schleife bestehen. Dies ist nützlich, wenn man Listen im Speicher manipuliert, anstatt sie in Dateien zu speichern. {p.s. keep calm and carry on B-) }

2voto

jeberle Punkte 700

Verwenden Sie die set eingebaut, um die $@ Array:

IN="bla@some.com;john@home.com"
IFS=';'; set $IN; IFS=$' \t\n'

Dann kann die Party beginnen:

echo $#
for a; do echo $a; done
ADDR1=$1 ADDR2=$2

0 Stimmen

Bessere Nutzung set -- $IN um einige Probleme mit "$IN" zu vermeiden, die mit einem Bindestrich beginnen. Dennoch ist die nicht quotierte Erweiterung von $IN erweitert Wildcards ( *?[ ).

1voto

Petr Újezdský Punkte 1152

Vielleicht nicht die eleganteste Lösung, aber sie funktioniert mit * und Räume:

IN="bla@so me.com;*;john@home.com"
for i in `delims=${IN//[^;]}; seq 1 $((${#delims} + 1))`
do
   echo "> [`echo $IN | cut -d';' -f$i`]"
done

Ausgänge

> [bla@so me.com]
> [*]
> [john@home.com]

Anderes Beispiel (Begrenzungszeichen am Anfang und Ende):

IN=";bla@so me.com;*;john@home.com;"
> []
> [bla@so me.com]
> [*]
> [john@home.com]
> []

Grundsätzlich werden alle Zeichen mit Ausnahme der folgenden entfernt ; Herstellung von delims z. B. ;;; . Dann tut es for Schleife von 1 a number-of-delimiters wie gezählt von ${#delims} . Der letzte Schritt besteht darin, die $i Teil mit cut .

1voto

Michael Hale Punkte 1367

Ein Einzeiler, um eine durch ';' getrennte Zeichenkette in ein Array aufzuteilen, lautet:

IN="bla@some.com;john@home.com"
ADDRS=( $(IFS=";" echo "$IN") )
echo ${ADDRS[0]}
echo ${ADDRS[1]}

Dadurch wird IFS nur in einer Subshell gesetzt, so dass Sie sich nicht um das Speichern und Wiederherstellen des Wertes kümmern müssen.

0 Stimmen

-1 das funktioniert hier nicht (ubuntu 12.04). es wird nur das erste echo mit allen $IN-Werten ausgedruckt, während das zweite leer ist. Sie können es sehen, wenn Sie echo "0: "${ADDRS[0]} \n echo "1: "${ADDRS[1]} die Ausgabe ist 0: bla@some.com;john@home.com\n 1: ( \n ist neue Zeile)

1 Stimmen

Eine funktionierende Alternative zu dieser Idee finden Sie in der Antwort von nickjb unter stackoverflow.com/a/6583589/1032370

1 Stimmen

-1. 1. IFS wird in dieser Subshell nicht gesetzt (es wird an die Umgebung von "echo" übergeben, das ein builtin ist, also passiert sowieso nichts). 2. $IN wird in Anführungszeichen gesetzt, damit es nicht dem IFS-Splitting unterliegt. 3. Die Prozesssubstitution wird durch Leerzeichen getrennt, aber dies kann die Originaldaten beschädigen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X