Wie teile ich eine Zeichenfolge in einem Trennzeichen in Bash?


2041

Ich habe diese Zeichenfolge in einer Variablen gespeichert:

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

Jetzt möchte ich die Zeichenfolgen nach ;Trennzeichen aufteilen , damit ich Folgendes habe:

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

Ich brauche die ADDR1und ADDR2Variablen nicht unbedingt . Wenn sie Elemente eines Arrays sind, ist das noch besser.


Nach den Vorschlägen aus den folgenden Antworten kam ich zu folgendem Ergebnis:

#!/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, bei der Internal_field_separator (IFS) auf gesetzt wurde ;. Ich bin nicht sicher, was mit dieser Antwort passiert ist. Wie können Sie IFSdie Standardeinstellungen wiederherstellen?

RE: IFSLösung, ich habe es versucht und es funktioniert, ich behalte das alte IFSund stelle es dann wieder her:

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 es versuchte

mails2=($IN)

Ich habe nur die erste Zeichenfolge beim Drucken in einer Schleife erhalten, ohne dass Klammern darum herum $INfunktionieren.


14
In Bezug auf Ihre "Edit2": Sie können IFS einfach "deaktivieren" und es wird in den Standardzustand zurückkehren. Sie müssen es nicht explizit speichern und wiederherstellen, es sei denn, Sie haben Grund zu der Annahme, dass es bereits auf einen nicht standardmäßigen Wert festgelegt wurde. Wenn Sie dies innerhalb einer Funktion tun (und wenn nicht, warum nicht?), Können Sie außerdem IFS als lokale Variable festlegen, die nach dem Beenden der Funktion auf den vorherigen Wert zurückkehrt.
Brooks Moses

19
@BrooksMoses: (a) +1 für die Verwendung, local IFS=...wo möglich; (b) -1 für unset IFS, dies setzt IFS nicht genau auf seinen Standardwert zurück, obwohl ich glaube, dass sich ein nicht gesetztes IFS genauso verhält wie der Standardwert von IFS ($ '\ t \ n'), aber es scheint eine schlechte Praxis zu sein Nehmen Sie blind an, dass Ihr Code niemals aufgerufen wird, wenn IFS auf einen benutzerdefinierten Wert eingestellt ist. (c) Eine andere Idee besteht darin, eine Unterschale aufzurufen: (IFS=$custom; ...)Wenn die Unterschale beendet wird, kehrt IFS zu dem zurück, was sie ursprünglich war.
Dubiousjim

Ich möchte nur einen kurzen Blick auf die Pfade werfen, um zu entscheiden, wohin eine ausführbare Datei geworfen werden soll, also habe ich mich für die Ausführung entschieden ruby -e "puts ENV.fetch('PATH').split(':')". Wenn Sie reine Bash bleiben möchten, hilft es nicht, aber die Verwendung einer Skriptsprache mit integriertem Split ist einfacher.
Nicooga

4
for x in $(IFS=';';echo $IN); do echo "> [$x]"; done
user2037659

2
Um es als Array zu speichern, musste ich einen weiteren Satz Klammern setzen und den \nfür nur ein Leerzeichen ändern . Die letzte Zeile ist also mails=($(echo $IN | tr ";" " ")). Jetzt kann ich die Elemente überprüfen, mailsindem ich die Array-Notation verwende mails[index]oder einfach in einer Schleife iteriere
afranques

Antworten:


1235

Sie können die IFS-Variable ( Internal Field Separator ) festlegen und sie dann in ein Array analysieren lassen. Wenn dies in einem Befehl geschieht, erfolgt die Zuweisung an IFSnur an die Umgebung dieses einzelnen Befehls (an read). Anschließend wird die Eingabe gemäß dem IFSVariablenwert in ein Array analysiert , über das wir dann iterieren können.

IFS=';' read -ra ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
    # process "$i"
done

Es wird eine durch getrennte Zeile von Elementen analysiert ;und in ein Array verschoben. Zeug für die Verarbeitung der gesamten $IN, jedes Mal eine Eingabezeile getrennt durch ;:

 while IFS=';' read -ra ADDR; do
      for i in "${ADDR[@]}"; do
          # process "$i"
      done
 done <<< "$IN"

22
Dies ist wahrscheinlich der beste Weg. Wie lange bleibt IFS in seinem aktuellen Wert, kann es meinen Code durcheinander bringen, indem es gesetzt wird, wenn es nicht sein sollte, und wie kann ich es zurücksetzen, wenn ich damit fertig bin?
Chris Lutz

7
jetzt nach dem Fix angewendet, nur innerhalb der Dauer des
Lesebefehls

14
Sie können alles auf einmal lesen, ohne eine while-Schleife zu verwenden: read -r -d '' -a addr <<< "$ in" # Das -d '' ist hier der Schlüssel, es sagt read, dass es nicht beim ersten Zeilenumbruch anhalten soll ( Dies ist die Standardeinstellung -d), wird jedoch fortgesetzt, bis EOF oder ein NULL-Byte (das nur in Binärdaten vorkommt) fortgesetzt wird.
lhunath

55
@LucaBorrione Die Einstellung IFSin derselben Zeile wie die readohne Semikolon oder anderes Trennzeichen im Gegensatz zu einem separaten Befehl bezieht sich auf diesen Befehl - daher wird sie immer "wiederhergestellt". Sie müssen nichts manuell tun.
Charles Duffy

5
@imagineerThis Es gibt einen Fehler, der Herestrings und lokale Änderungen an IFS beinhaltet und $INzitiert werden muss. Der Fehler wurde in bash4.3 behoben .
Chepner

973

Entnommen aus dem Split-Array des Bash-Shell-Skripts :

IN="bla@some.com;john@home.com"
arrIN=(${IN//;/ })

Erläuterung:

Diese Konstruktion ersetzt alle Vorkommen von ';'(der Anfang //bedeutet globales Ersetzen) in der Zeichenfolge INdurch ' '(ein einzelnes Leerzeichen) und interpretiert dann die durch Leerzeichen getrennte Zeichenfolge als Array (dies tun die umgebenden Klammern).

Die Syntax, die in den geschweiften Klammern verwendet wird, um jedes ';'Zeichen durch ein ' 'Zeichen zu ersetzen, wird als Parametererweiterung bezeichnet .

Es gibt einige häufige Fallstricke:

  1. Wenn die ursprüngliche Zeichenfolge Leerzeichen enthält, müssen Sie IFS verwenden :
    • IFS=':'; arrIN=($IN); unset IFS;
  2. Wenn die ursprüngliche Zeichenfolge Leerzeichen enthält und das Trennzeichen eine neue Zeile ist, können Sie IFS festlegen mit:
    • IFS=$'\n'; arrIN=($IN); unset IFS;

84
Ich möchte nur hinzufügen: Dies ist die einfachste von allen. Sie können mit $ {arrIN [1]} auf Array-Elemente zugreifen (natürlich beginnend mit Nullen)
Oz123

26
Gefunden: Die Technik zum Ändern einer Variablen innerhalb eines $ {} wird als 'Parametererweiterung' bezeichnet.
KomodoDave

23
Nein, ich glaube nicht, dass dies funktioniert, wenn auch Leerzeichen vorhanden sind. Es konvertiert das ',' in '' und erstellt dann ein durch Leerzeichen getrenntes Array.
Ethan

12
Sehr prägnant, aber es gibt Einschränkungen für den allgemeinen Gebrauch : Die Shell wendet Wortaufteilung und Erweiterungen auf die Zeichenfolge an, was möglicherweise unerwünscht ist. Probieren Sie es einfach mit. IN="bla@some.com;john@home.com;*;broken apart". Kurz gesagt: Dieser Ansatz wird unterbrochen, wenn Ihre Token eingebettete Leerzeichen und / oder Zeichen enthalten. In *diesem Fall stimmt ein Token mit den Dateinamen im aktuellen Ordner überein.
mklement0

53
Dies ist aus anderen Gründen ein schlechter Ansatz: Wenn Ihre Zeichenfolge beispielsweise enthält ;*;, *wird die Zeichenfolge auf eine Liste von Dateinamen im aktuellen Verzeichnis erweitert. -1
Charles Duffy

249

Wenn es Ihnen nichts ausmacht, sie sofort zu verarbeiten, mache ich das gerne:

for i in $(echo $IN | tr ";" "\n")
do
  # process
done

Sie könnten diese Art von Schleife verwenden, um ein Array zu initialisieren, aber es gibt wahrscheinlich einen einfacheren Weg, dies zu tun. Hoffe das hilft aber.


Sie sollten die IFS-Antwort behalten. Es hat mir etwas beigebracht, was ich nicht wusste, und es hat definitiv ein Array gebildet, während dies nur ein billiger Ersatz ist.
Chris Lutz

Aha. Ja, ich finde diese dummen Experimente, ich werde jedes Mal neue Dinge lernen, wenn ich versuche, Dinge zu beantworten. Ich habe Sachen basierend auf #bash IRC Feedback bearbeitet und nicht gelöscht :)
Johannes Schaub - litb

33
-1, Sie sind sich der Wortteilung offensichtlich nicht bewusst, da sie zwei Fehler in Ihren Code einführt. Eine ist, wenn Sie $ IN nicht zitieren und die andere, wenn Sie so tun, als wäre eine neue Zeile das einzige Trennzeichen, das beim Aufteilen von Wörtern verwendet wird. Sie iterieren über jedes WORT in IN, nicht über jede Zeile und definitiv nicht über jedes Element, das durch ein Semikolon begrenzt ist, obwohl es den Nebeneffekt zu haben scheint, als würde es funktionieren.
lhunath

3
Sie können es ändern, um "$ IN" | wiederzugeben tr ';' '\ n' | beim Lesen -r ADDY; do # process "$ ADDY"; getan, um ihn glücklich zu machen, denke ich :) Beachten Sie, dass dies sich verzweigt und Sie keine äußeren Variablen innerhalb der Schleife ändern können (deshalb habe ich die <<< "$ IN"
-Syntax verwendet

8
Um die Debatte in den Kommentaren zusammenzufassen: Vorsichtsmaßnahmen für den allgemeinen Gebrauch : Die Shell wendet Wortaufteilung und Erweiterungen auf die Zeichenfolge an, was möglicherweise unerwünscht ist. Probieren Sie es einfach mit. IN="bla@some.com;john@home.com;*;broken apart". Kurz gesagt: Dieser Ansatz wird unterbrochen, wenn Ihre Token eingebettete Leerzeichen und / oder Zeichen enthalten. In *diesem Fall stimmt ein Token mit den Dateinamen im aktuellen Ordner überein.
mklement0

202

Kompatible Antwort

Es gibt viele verschiedene Möglichkeiten, dies zu tun .

Allerdings ist es wichtig, zuerst zu beachten Sie, dass bashviele hat spezielle Eigenschaften (so genannte bashisms ) das wird nicht funktionieren in jedem anderen.

Insbesondere Arrays , assoziative Arrays und Mustersubstitutionen , die in den Lösungen in diesem Beitrag sowie in anderen im Thread verwendet werden, sind Bashismen und funktionieren möglicherweise nicht unter anderen Shells , die von vielen Personen verwendet werden.

Zum Beispiel: Auf meinem Debian GNU / Linux gibt es eine Standard- Shell namens;; Ich kenne viele Leute, die gerne eine andere Shell namens verwenden;; und es gibt auch ein spezielles Werkzeug namens mit seinem eigenen Shell-Interpreter ().

Angeforderte Zeichenfolge

Die in der obigen Frage zu teilende Zeichenfolge lautet:

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

Ich werde eine modifizierte Version dieser Zeichenfolge verwenden, um sicherzustellen, dass meine Lösung gegenüber Zeichenfolgen mit Leerzeichen robust ist, wodurch andere Lösungen beschädigt werden können:

IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

Teilen Sie die Zeichenfolge basierend auf dem Trennzeichen in (Version> = 4.2)

In rein bash können wir ein Array mit Elementen erstellen, die durch einen temporären Wert für IFS (das Eingabefeldtrennzeichen ) getrennt sind. Das IFS gibt unter anderem an, bashwelche Zeichen bei der Definition eines Arrays als Trennzeichen zwischen Elementen behandelt werden sollen:

IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

# save original IFS value so we can restore it later
oIFS="$IFS"
IFS=";"
declare -a fields=($IN)
IFS="$oIFS"
unset oIFS

In neueren Versionen von bash, einen Befehl mit einer IFS - Definition voran ändert die IFS für diesen Befehl nur und setzt sie auf den vorherigen Wert unmittelbar danach. Dies bedeutet, dass wir das Obige in nur einer Zeile tun können:

IFS=\; read -a fields <<<"$IN"
# after this command, the IFS resets back to its previous value (here, the default):
set | grep ^IFS=
# IFS=$' \t\n'

Wir können sehen, dass die Zeichenfolge INin einem Array mit dem Namen gespeichert wurde fields, das auf die Semikolons aufgeteilt ist:

set | grep ^fields=\\\|^IN=
# fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")
# IN='bla@some.com;john@home.com;Full Name <fulnam@other.org>'

(Wir können den Inhalt dieser Variablen auch mit anzeigen declare -p:)

declare -p IN fields
# declare -- IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
# declare -a fields=([0]="bla@some.com" [1]="john@home.com" [2]="Full Name <fulnam@other.org>")

Beachten Sie, dass dies readder schnellste Weg ist, um die Aufteilung durchzuführen, da keine Gabeln oder externen Ressourcen aufgerufen werden.

Sobald das Array definiert ist, können Sie jedes Feld (oder vielmehr jedes Element in dem Array, das Sie jetzt definiert haben) mit einer einfachen Schleife verarbeiten:

# `"${fields[@]}"` expands to return every element of `fields` array as a separate argument
for x in "${fields[@]}" ;do
    echo "> [$x]"
    done
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]

Oder Sie können jedes Feld nach der Verarbeitung mit einem Shifting- Ansatz aus dem Array entfernen , was mir gefällt:

while [ "$fields" ] ;do
    echo "> [$fields]"
    # slice the array 
    fields=("${fields[@]:1}")
    done
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]

Und wenn Sie nur einen einfachen Ausdruck des Arrays wünschen, müssen Sie es nicht einmal durchlaufen:

printf "> [%s]\n" "${fields[@]}"
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]

Update: aktuell > = 4,4

In neueren Versionen von bashkönnen Sie auch mit dem folgenden Befehl spielen mapfile:

mapfile -td \; fields < <(printf "%s\0" "$IN")

Diese Syntax bewahrt Sonderzeichen, Zeilenumbrüche und leere Felder!

Wenn Sie keine leeren Felder einfügen möchten, können Sie Folgendes tun:

mapfile -td \; fields <<<"$IN"
fields=("${fields[@]%$'\n'}")   # drop '\n' added by '<<<'

Mit mapfilekönnen Sie auch das Deklarieren eines Arrays überspringen und implizit die begrenzten Elemente "durchlaufen", indem Sie jeweils eine Funktion aufrufen:

myPubliMail() {
    printf "Seq: %6d: Sending mail to '%s'..." $1 "$2"
    # mail -s "This is not a spam..." "$2" </path/to/body
    printf "\e[3D, done.\n"
}

mapfile < <(printf "%s\0" "$IN") -td \; -c 1 -C myPubliMail

(Hinweis: Das \0am Ende der Formatzeichenfolge ist nutzlos, wenn Sie sich nicht um leere Felder am Ende der Zeichenfolge kümmern oder diese nicht vorhanden sind.)

mapfile < <(echo -n "$IN") -td \; -c 1 -C myPubliMail

# Seq:      0: Sending mail to 'bla@some.com', done.
# Seq:      1: Sending mail to 'john@home.com', done.
# Seq:      2: Sending mail to 'Full Name <fulnam@other.org>', done.

Oder Sie könnten verwenden <<<und in den Funktionskörper eine Verarbeitung aufnehmen, um die hinzugefügte neue Zeile zu löschen:

myPubliMail() {
    local seq=$1 dest="${2%$'\n'}"
    printf "Seq: %6d: Sending mail to '%s'..." $seq "$dest"
    # mail -s "This is not a spam..." "$dest" </path/to/body
    printf "\e[3D, done.\n"
}

mapfile <<<"$IN" -td \; -c 1 -C myPubliMail

# Renders the same output:
# Seq:      0: Sending mail to 'bla@some.com', done.
# Seq:      1: Sending mail to 'john@home.com', done.
# Seq:      2: Sending mail to 'Full Name <fulnam@other.org>', done.

Teilen Sie die Zeichenfolge basierend auf dem Trennzeichen in

Wenn Sie nicht verwenden können bash, oder wenn Sie etwas schreiben wollen , die in vielen verschiedenen Muscheln verwendet werden können, können Sie oft nicht verwenden bashisms - und dazu gehören auch die Arrays wir oben haben in den Lösungen.

Wir müssen jedoch keine Arrays verwenden, um "Elemente" eines Strings zu durchlaufen. In vielen Shells wird eine Syntax zum Löschen von Teilzeichenfolgen einer Zeichenfolge aus dem ersten oder letzten Auftreten eines Musters verwendet. Beachten Sie, dass* ein Platzhalter ist, der für null oder mehr Zeichen steht:

(Das Fehlen dieses Ansatzes in einer bisher veröffentlichten Lösung ist der Hauptgrund, warum ich diese Antwort schreibe;)

${var#*SubStr}  # drops substring from start of string up to first occurrence of `SubStr`
${var##*SubStr} # drops substring from start of string up to last occurrence of `SubStr`
${var%SubStr*}  # drops substring from last occurrence of `SubStr` to end of string
${var%%SubStr*} # drops substring from first occurrence of `SubStr` to end of string

Wie von Score_Under erklärt :

#und %löschen Sie den kürzestmöglichen übereinstimmenden Teilstring vom Anfang bzw. Ende des Strings, und

##und %%löschen Sie den längstmöglichen passenden Teilstring.

Mit der obigen Syntax können wir einen Ansatz erstellen, bei dem wir Teilstring- "Elemente" aus der Zeichenfolge extrahieren, indem wir die Teilzeichenfolgen bis zum oder nach dem Trennzeichen löschen.

Der folgende Codeblock funktioniert gut in (einschließlich Mac OS bash ),, , und 's ::

IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"
while [ "$IN" ] ;do
    # extract the substring from start of string up to delimiter.
    # this is the first "element" of the string.
    iter=${IN%%;*}
    echo "> [$iter]"
    # if there's only one element left, set `IN` to an empty string.
    # this causes us to exit this `while` loop.
    # else, we delete the first "element" of the string from IN, and move onto the next.
    [ "$IN" = "$iter" ] && \
        IN='' || \
        IN="${IN#*;}"
  done
# > [bla@some.com]
# > [john@home.com]
# > [Full Name <fulnam@other.org>]

Habe Spaß!


15
Die #, ##, %und %%Substitutionen haben , was IMO ist eine einfachere Erklärung (für wie viel sie löschen) zu erinnern: #und %die kürzest mögliche passende String löschen, und ##und %%möglichst lange löschen.
Score_Under

1
Das IFS=\; read -a fields <<<"$var"schlägt bei Zeilenumbrüchen fehl und fügt eine nachfolgende Zeilenumbruch hinzu. Die andere Lösung entfernt ein nachfolgendes leeres Feld.
Isaac

Das Shell-Trennzeichen ist die eleganteste Antwort, Punkt.
Eric Chen

Könnte die letzte Alternative mit einer Liste von Feldtrennzeichen verwendet werden, die an einer anderen Stelle festgelegt sind? Zum Beispiel möchte ich dies als Shell-Skript verwenden und eine Liste von Feldtrennzeichen als Positionsparameter übergeben.
sancho.s ReinstateMonicaCellio

Ja, in einer Schleife:for sep in "#" "ł" "@" ; do ... var="${var#*$sep}" ...
F. Hauri

184

Ich habe einige Antworten gesehen, die auf den cutBefehl verweisen , aber alle wurden gelöscht. Es ist ein wenig seltsam, dass niemand darauf eingegangen ist, weil ich denke, dass dies einer der nützlicheren Befehle für diese Art von Dingen ist, insbesondere für das Parsen von begrenzten Protokolldateien.

Wenn Sie dieses spezielle Beispiel in ein Bash-Skript-Array aufteilen, trist es wahrscheinlich effizienter, cutkann aber verwendet werden und ist effektiver, wenn Sie bestimmte Felder aus der Mitte ziehen möchten.

Beispiel:

$ echo "bla@some.com;john@home.com" | cut -d ";" -f 1
bla@some.com
$ echo "bla@some.com;john@home.com" | cut -d ";" -f 2
john@home.com

Sie können dies natürlich in eine Schleife einfügen und den Parameter -f wiederholen, um jedes Feld unabhängig zu ziehen.

Dies ist nützlicher, wenn Sie eine begrenzte Protokolldatei mit folgenden Zeilen haben:

2015-04-27|12345|some action|an attribute|meta data

cutEs ist sehr praktisch, catdiese Datei zu verwenden und ein bestimmtes Feld für die weitere Verarbeitung auszuwählen.


6
Ein großes Lob für die Verwendung cut, es ist das richtige Werkzeug für den Job! Viel geklärt als jeder dieser Shell-Hacks.
MisterMiyagi

4
Dieser Ansatz funktioniert nur, wenn Sie die Anzahl der Elemente im Voraus kennen. Sie müssten etwas mehr Logik darauf programmieren. Außerdem wird für jedes Element ein externes Tool ausgeführt.
uli42

Genau das, wonach ich gesucht habe, um leere Zeichenfolgen in einer CSV zu vermeiden. Jetzt kann ich auch den genauen 'Spalten'-Wert angeben. Arbeiten Sie mit IFS, das bereits in einer Schleife verwendet wird. Besser als erwartet für meine Situation.
Louis Loudog Trottier

Sehr nützlich zum Abrufen von IDs und PIDs, dh
Milos Grujic

Diese Antwort ist es wert, über eine halbe Seite nach unten zu scrollen :)
Gucu112

124

Das hat bei mir funktioniert:

string="1;2"
echo $string | cut -d';' -f1 # output is 1
echo $string | cut -d';' -f2 # output is 2

1
Obwohl es nur mit einem einzelnen Zeichenbegrenzer funktioniert, suchte das OP danach (Datensätze, die durch ein Semikolon getrennt sind).
GuyPaddock

Beantwortet vor ungefähr vier Jahren von @Ashok und vor mehr als einem Jahr von @DougW als Ihre Antwort mit noch mehr Informationen. Bitte poste eine andere Lösung als die anderer.
MAChitgarha

90

Wie wäre es mit diesem Ansatz:

IN="bla@some.com;john@home.com" 
set -- "$IN" 
IFS=";"; declare -a Array=($*) 
echo "${Array[@]}" 
echo "${Array[0]}" 
echo "${Array[1]}" 

Quelle


7
+1 ... aber ich würde die Variable nicht "Array" nennen ... pet peev, denke ich. Gute Lösung.
Yzmir Ramirez

14
+1 ... aber die "Menge" und deklarieren -a sind nicht notwendig. Sie hätten genauso gut nurIFS";" && Array=($IN)
ata

+1 Nur eine Randnotiz: Sollte es nicht empfehlenswert sein, das alte IFS beizubehalten und dann wiederherzustellen? (wie von stefanB in seinem edit3 gezeigt) Leute, die hier landen (manchmal nur eine Lösung kopieren und einfügen), denken möglicherweise nicht darüber nach
Luca Borrione

6
-1: Erstens hat @ata Recht, dass die meisten Befehle in diesem Programm nichts bewirken. Zweitens wird die Wortaufteilung verwendet, um das Array zu bilden, und es wird nichts unternommen, um die Glob-Erweiterung zu verhindern (wenn Sie also Glob-Zeichen in einem der Array-Elemente haben, werden diese Elemente durch übereinstimmende Dateinamen ersetzt).
Charles Duffy

1
Empfehlen Sie zu verwenden $'...': IN=$'bla@some.com;john@home.com;bet <d@\ns* kl.com>'. Dann echo "${Array[2]}"wird eine Zeichenfolge mit Zeilenumbruch gedruckt. set -- "$IN"ist auch in diesem Fall notwendig. Ja, um eine Glob-Expansion zu verhindern, sollte die Lösung Folgendes enthalten set -f.
John_West

79

Ich denke, AWK ist der beste und effizienteste Befehl, um Ihr Problem zu lösen. AWK ist standardmäßig in fast jeder Linux-Distribution enthalten.

echo "bla@some.com;john@home.com" | awk -F';' '{print $1,$2}'

wird geben

bla@some.com john@home.com

Natürlich können Sie jede E-Mail-Adresse speichern, indem Sie das awk-Druckfeld neu definieren.


3
Oder noch einfacher: echo "bla@some.com; john@home.com" | awk 'BEGIN {RS = ";"} {print}'
Jaro

@Jaro Das hat perfekt für mich funktioniert, als ich eine Zeichenfolge mit Kommas hatte und sie in Zeilen umformatieren musste. Vielen Dank.
Aquarelle

In diesem Szenario hat es funktioniert -> "echo" $ SPLIT_0 "| awk -F 'inode =' '{print $ 1}'"! Ich hatte Probleme beim Versuch, Atringe ("inode =") anstelle von Zeichen (";") zu verwenden. $ 1, $ 2, $ 3, $ 4 werden als Positionen in einem Array festgelegt! Wenn es eine Möglichkeit gibt, ein Array einzustellen ... besser! Vielen Dank!
Eduardo Lucio

@EduardoLucio, was ich denke im Begriff ist , vielleicht können Sie zunächst Ihre Trennzeichen ersetzen inode=in ;zum Beispiel durch sed -i 's/inode\=/\;/g' your_file_to_process, dann definieren , -F';'wenn anwenden awk, Hoffnung , die Ihnen helfen kann.
Tong

66
echo "bla@some.com;john@home.com" | sed -e 's/;/\n/g'
bla@some.com
john@home.com

4
-1 Was ist, wenn die Zeichenfolge Leerzeichen enthält? Zum Beispiel IN="this is first line; this is second line" arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )wird in diesem Fall ein Array von 8 Elementen erzeugt (ein Element für jeden getrennten
Wortraum

3
@Luca Nein, das sed-Skript erstellt genau zwei Zeilen. Was die mehreren Einträge für Sie erstellt, ist, wenn Sie es in ein Bash-Array (das standardmäßig auf Leerzeichen aufgeteilt wird) setzen
Lothar

Genau das ist der Punkt: Das OP muss Einträge in einem Array speichern, um eine Schleife darüber zu erstellen, wie Sie in seinen Änderungen sehen können. Ich denke, Ihre (gute) Antwort wurde nicht erwähnt, um dies arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )zu erreichen, und um Ratschläge zu geben, IFS IFS=$'\n'für diejenigen zu ändern, die in Zukunft hier landen und eine Zeichenfolge mit Leerzeichen teilen müssen. (und um es danach wieder herzustellen). :)
Luca Borrione

1
@ Luca Guter Punkt. Die Array-Zuweisung war jedoch nicht in der ursprünglichen Frage enthalten, als ich diese Antwort schrieb.
Lothar

65

Dies funktioniert auch:

IN="bla@some.com;john@home.com"
echo ADD1=`echo $IN | cut -d \; -f 1`
echo ADD2=`echo $IN | cut -d \; -f 2`

Seien Sie vorsichtig, diese Lösung ist nicht immer korrekt. Wenn Sie nur "bla@some.com" übergeben, wird es sowohl ADD1 als auch ADD2 zugewiesen.


1
Sie können -s verwenden, um das erwähnte Problem zu vermeiden: superuser.com/questions/896800/… "-f, --fields = LIST Wählen Sie nur diese Felder aus. Drucken Sie auch jede Zeile, die kein Trennzeichen enthält, es sei denn, die Option -s ist aktiviert angegeben "
Fersarr

34

Eine andere Sicht auf Darrons Antwort , so mache ich es:

IN="bla@some.com;john@home.com"
read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)

Ich denke schon! Führen Sie die obigen Befehle aus und dann "echo $ ADDR1 ... $ ADDR2" und ich erhalte die Ausgabe "bla@some.com ... john@home.com"
nickjb

1
Dies funktionierte WIRKLICH gut für mich ... Ich habe es verwendet, um über ein Array von Zeichenfolgen zu iterieren, die durch Kommas getrennte DB-, SERVER- und PORT-Daten enthielten, um mysqldump zu verwenden.
Nick

5
Diagnose: Die IFS=";"Zuordnung existiert nur in der $(...; echo $IN)Unterschale; Aus diesem Grund denken einige Leser (einschließlich mir) zunächst, dass es nicht funktionieren wird. Ich nahm an, dass ADDR1 alle $ IN verschluckt hatte. Aber nickjb ist richtig; es funktioniert. Der Grund dafür ist, dass der echo $INBefehl seine Argumente mit dem aktuellen Wert von $ IFS analysiert, sie dann jedoch unabhängig von der Einstellung von $ IFS mit einem Leerzeichen in stdout umwandelt. Der Nettoeffekt ist also so, als hätte man aufgerufen read ADDR1 ADDR2 <<< "bla@some.com john@home.com"(beachten Sie, dass die Eingabe nicht durch Leerzeichen getrennt, sondern durch Leerzeichen getrennt ist).
Dubiousjim

1
Dies schlägt bei Leerzeichen und Zeilenumbrüchen fehl und erweitert Platzhalter *in der echo $INmit einer nicht zitierten Variablenerweiterung.
Isaac

Diese Lösung gefällt mir sehr gut. Eine Beschreibung, warum es funktioniert, wäre sehr nützlich und würde es zu einer besseren Gesamtantwort machen.
Michael Gaskill

32

In Bash, einer kugelsicheren Methode, die auch dann funktioniert, wenn Ihre Variable Zeilenumbrüche enthält:

IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")

Aussehen:

$ in=$'one;two three;*;there is\na newline\nin this field'
$ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")
$ declare -p array
declare -a array='([0]="one" [1]="two three" [2]="*" [3]="there is
a newline
in this field")'

Der Trick, damit dies funktioniert, besteht darin, die -dOption read(Trennzeichen) mit einem leeren Trennzeichen zu verwenden, sodass readgezwungen ist, alles zu lesen, was eingegeben wird. Und wir füttern readmit genau dem Inhalt der Variablen in, ohne nachfolgende Zeilenumbrüche dank printf. Beachten Sie, dass wir auch das Trennzeichen einfügen, printfum sicherzustellen, dass die übergebene Zeichenfolge readein nachfolgendes Trennzeichen enthält. Ohne sie readwürden potenzielle nachfolgende leere Felder gekürzt:

$ in='one;two;three;'    # there's an empty field
$ IFS=';' read -d '' -ra array < <(printf '%s;\0' "$in")
$ declare -p array
declare -a array='([0]="one" [1]="two" [2]="three" [3]="")'

Das nachfolgende leere Feld bleibt erhalten.


Update für Bash≥4.4

Seit Bash 4.4 unterstützt das eingebaute mapfile(auch bekannt als readarray) die -dOption, ein Trennzeichen anzugeben. Daher ist ein anderer kanonischer Weg:

mapfile -d ';' -t array < <(printf '%s;' "$in")

5
Ich fand es als die seltene Lösung auf dieser Liste, die korrekt mit \nLeerzeichen und *gleichzeitig funktioniert . Auch keine Schleifen; Auf die Array-Variable kann nach der Ausführung in der Shell zugegriffen werden (im Gegensatz zur Antwort mit der höchsten Bewertung). Beachten Sie, in=$'...'dass dies nicht mit doppelten Anführungszeichen funktioniert. Ich denke, es braucht mehr Upvotes.
John_West

28

Wie wäre es mit diesem einen Liner, wenn Sie keine Arrays verwenden:

IFS=';' read ADDR1 ADDR2 <<<$IN

Verwenden Sie diese Option, read -r ...um sicherzustellen, dass beispielsweise die beiden Zeichen "\ t" in der Eingabe dieselben zwei Zeichen in Ihren Variablen sind (anstelle eines einzelnen Tabulatorzeichens).
Dubiousjim

-1 Dies funktioniert hier nicht (Ubuntu 12.04). Das Hinzufügen echo "ADDR1 $ADDR1"\n echo "ADDR2 $ADDR2"zu Ihrem Snippet wird ausgegeben ADDR1 bla@some.com john@home.com\nADDR2(\ n ist Newline)
Luca Borrione

Dies ist wahrscheinlich auf einen Fehler zurückzuführen, bei dem es sich um IFSZeichenfolgen handelt, die in bash4.3 behoben wurden . Das Zitieren $INsollte das Problem beheben. (Theoretisch $INunterliegt es nach dem Erweitern keiner Wortaufteilung oder -globierung, was bedeutet, dass die Anführungszeichen unnötig sein sollten. Selbst in 4.3 ist jedoch noch mindestens ein Fehler vorhanden - gemeldet und geplant, um behoben zu werden -, sodass das Zitieren weiterhin gut ist Idee.)
Chepner

Dies bricht ab, wenn $ in Zeilenumbrüche enthält, auch wenn $ IN in Anführungszeichen steht. Und fügt eine nachfolgende Newline hinzu.
Isaac

Ein Problem mit diesem und vielen anderen Lösungen besteht auch darin, dass davon ausgegangen wird, dass genau $ ZWEI Elemente in $ IN enthalten sind - ODER dass Sie bereit sind, das zweite und nachfolgende Element in ADDR2 zusammenzuschlagen. Ich verstehe, dass dies die Frage erfüllt, aber es ist eine Zeitbombe.
Steven der leicht amüsierte

22

Ohne das IFS einzustellen

Wenn Sie nur einen Doppelpunkt haben, können Sie dies tun:

a="foo:bar"
b=${a%:*}
c=${a##*:}

Sie erhalten:

b = foo
c = bar

20

Hier ist ein sauberer 3-Liner:

in="foo@bar;bizz@buzz;fizz@buzz;buzz@woof"
IFS=';' list=($in)
for item in "${list[@]}"; do echo $item; done

Dabei werden IFSWörter basierend auf dem Trennzeichen abgegrenzt und ()zum Erstellen eines Arrays verwendet . Dann [@]wird jedes Element als separates Wort zurückgegeben.

Wenn Sie danach Code haben, müssen Sie auch wiederherstellen $IFS, z unset IFS.


5
Durch die Verwendung von $innicht zitierten Platzhaltern können Platzhalter erweitert werden.
Isaac

10

Die folgende Bash / zsh-Funktion teilt ihr erstes Argument in das durch das zweite Argument angegebene Trennzeichen auf:

split() {
    local string="$1"
    local delimiter="$2"
    if [ -n "$string" ]; then
        local part
        while read -d "$delimiter" part; do
            echo $part
        done <<< "$string"
        echo $part
    fi
}

Zum Beispiel der Befehl

$ split 'a;b;c' ';'

ergibt

a
b
c

Diese Ausgabe kann beispielsweise an andere Befehle weitergeleitet werden. Beispiel:

$ split 'a;b;c' ';' | cat -n
1   a
2   b
3   c

Gegenüber den anderen angegebenen Lösungen bietet diese folgende Vorteile:

  • IFSwird nicht überschrieben: Aufgrund des dynamischen Bereichs selbst lokaler Variablen führt das Überschreiben IFSeiner Schleife dazu, dass der neue Wert in Funktionsaufrufe aus der Schleife gelangt.

  • Arrays werden nicht verwendet: Das Einlesen eines Strings in ein Array mit readerfordert das Flag -ain Bash und -Ain zsh.

Falls gewünscht, kann die Funktion wie folgt in ein Skript eingefügt werden:

#!/usr/bin/env bash

split() {
    # ...
}

split "$@"

Scheint nicht mit Trennzeichen zu funktionieren, die länger als 1 Zeichen sind: split = $ (split "$ content" "file: //")
madprops

Richtig - von help read:-d delim continue until the first character of DELIM is read, rather than newline
Halle Knast

8

Sie können awk auf viele Situationen anwenden

echo "bla@some.com;john@home.com"|awk -F';' '{printf "%s\n%s\n", $1, $2}'

auch du kannst das benutzen

echo "bla@some.com;john@home.com"|awk -F';' '{print $1,$2}' OFS="\n"

7

Es gibt einen einfachen und intelligenten Weg wie diesen:

echo "add:sfff" | xargs -d: -i  echo {}

Aber Sie müssen gnu xargs verwenden, BSD xargs kann -d delim nicht unterstützen. Wenn Sie Apple Mac wie ich verwenden. Sie können gnu xargs installieren:

brew install findutils

dann

echo "add:sfff" | gxargs -d: -i  echo {}

4

Dies ist der einfachste Weg, dies zu tun.

spo='one;two;three'
OIFS=$IFS
IFS=';'
spo_array=($spo)
IFS=$OIFS
echo ${spo_array[*]}

4

Hier gibt es einige coole Antworten (insbesondere Errator), aber für etwas Analoges, das in andere Sprachen aufgeteilt werden kann - was ich mit der ursprünglichen Frage gemeint habe - habe ich mich dazu entschlossen:

IN="bla@some.com;john@home.com"
declare -a a="(${IN/;/ })";

Jetzt ${a[0]}sind ${a[1]}usw. so, wie Sie es erwarten würden. Verwenden Sie ${#a[*]}für die Anzahl der Begriffe. Oder um es zu wiederholen:

for i in ${a[*]}; do echo $i; done

WICHTIGE NOTIZ:

Dies funktioniert in Fällen, in denen es keine Leerzeichen gibt, über die Sie sich Sorgen machen müssen, wodurch mein Problem gelöst wurde, Ihr Problem jedoch möglicherweise nicht gelöst wird. Gehen Sie $IFSin diesem Fall mit den Lösungen.


Funktioniert nicht, wenn INmehr als zwei E-Mail-Adressen enthalten sind. Bitte beziehen Sie sich auf die gleiche Idee (aber behoben) bei Palindroms Antwort
Olibre

Besser verwenden ${IN//;/ }(doppelter Schrägstrich), damit es auch mit mehr als zwei Werten funktioniert. Beachten Sie, dass alle Platzhalter ( *?[) erweitert werden. Und ein nachfolgendes leeres Feld wird verworfen.
Isaac

3
IN="bla@some.com;john@home.com"
IFS=';'
read -a IN_arr <<< "${IN}"
for entry in "${IN_arr[@]}"
do
    echo $entry
done

Ausgabe

bla@some.com
john@home.com

System: Ubuntu 12.04.1


IFS wird nicht in den spezifischen Kontext von readhier gesetzt und kann daher den Rest des Codes, falls vorhanden, stören.
Codeforester

2

Wenn kein Platz, warum nicht das?

IN="bla@some.com;john@home.com"
arr=(`echo $IN | tr ';' ' '`)

echo ${arr[0]}
echo ${arr[1]}

2

Verwenden Sie die setintegrierte Funktion, um das $@Array zu laden :

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

Dann lass die Party beginnen:

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

Besser verwenden set -- $IN, um einige Probleme mit "$ IN" zu vermeiden, beginnend mit Bindestrich. Die nicht zitierte Erweiterung von $INwird jedoch Platzhalter erweitern ( *?[).
Isaac

2

Zwei bourne-ish Alternativen, für die keine Bash-Arrays erforderlich sind:

Fall 1 : Halten Sie es schön und einfach: Verwenden Sie eine NewLine als Datensatztrennzeichen ... z.

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, um die Listenmanipulation zu unterstützen.

Idee: Vielleicht lohnt es sich, NL ausgiebig intern zu verwenden und nur dann in eine andere RS zu konvertieren, wenn das Endergebnis extern generiert wird .

Fall 2 : Verwenden eines ";" als Datensatztrennzeichen ... zB.

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 Unterliste innerhalb der Schleife erstellt werden, die nach Abschluss der Schleife bestehen bleibt. Dies ist nützlich, wenn Sie Listen im Speicher bearbeiten und stattdessen Listen in Dateien speichern. {ps bleib ruhig und mach weiter B-)}


2

Abgesehen von den fantastischen Antworten, die bereits gegeben wurden, sollten Sie nur die Daten ausdrucken, die Sie möglicherweise verwenden möchten awk:

awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"

Dadurch wird das Feldtrennzeichen auf gesetzt ;, sodass es die Felder mit einer forSchleife durchlaufen und entsprechend drucken kann.

Prüfung

$ IN="bla@some.com;john@home.com"
$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "$IN"
> [bla@some.com]
> [john@home.com]

Mit einer anderen Eingabe:

$ awk -F";" '{for (i=1;i<=NF;i++) printf("> [%s]\n", $i)}' <<< "a;b;c   d;e_;f"
> [a]
> [b]
> [c   d]
> [e_]
> [f]

2

In der Android-Shell funktionieren die meisten der vorgeschlagenen Methoden einfach nicht:

$ IFS=':' read -ra ADDR <<<"$PATH"                             
/system/bin/sh: can't create temporary file /sqlite_stmt_journals/mksh.EbNoR10629: No such file or directory

Was funktioniert ist:

$ for i in ${PATH//:/ }; do echo $i; done
/sbin
/vendor/bin
/system/sbin
/system/bin
/system/xbin

wo //bedeutet globaler Ersatz.


1
Schlägt fehl, wenn ein Teil von $ PATH Leerzeichen (oder Zeilenumbrüche) enthält. Erweitert auch Platzhalter (Sternchen *, Fragezeichen? Und geschweifte Klammern […]).
Isaac

2
IN='bla@some.com;john@home.com;Charlie Brown <cbrown@acme.com;!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'
set -f
oldifs="$IFS"
IFS=';'; arrayIN=($IN)
IFS="$oldifs"
for i in "${arrayIN[@]}"; do
echo "$i"
done
set +f

Ausgabe:

bla@some.com
john@home.com
Charlie Brown <cbrown@acme.com
!"#$%&/()[]{}*? are no problem
simple is beautiful :-)

Erläuterung: Durch die einfache Zuweisung mit in Klammern () wird eine durch Semikolons getrennte Liste in ein Array konvertiert, sofern Sie dabei über das richtige IFS verfügen. Die Standard-FOR-Schleife behandelt einzelne Elemente in diesem Array wie gewohnt. Beachten Sie, dass die für die IN-Variable angegebene Liste in "harten" Anführungszeichen stehen muss, dh mit einzelnen Ticks.

IFS muss gespeichert und wiederhergestellt werden, da Bash eine Zuweisung nicht wie einen Befehl behandelt. Eine alternative Problemumgehung besteht darin, die Zuweisung in eine Funktion einzubinden und diese Funktion mit einem modifizierten IFS aufzurufen. In diesem Fall ist ein separates Speichern / Wiederherstellen von IFS nicht erforderlich. Vielen Dank für "Bize", dass Sie darauf hingewiesen haben.


!"#$%&/()[]{}*? are no problem[]*?na ja ... nicht ganz: sind glob zeichen. Was ist also mit dem Erstellen dieses Verzeichnisses und der Datei: `mkdir '!" # $% &'; Touch '! "# $% & / () [] {} Hast du hahahaha - bist kein Problem' und dem Ausführen deines Befehls? einfach mag schön sein, aber wenn es kaputt ist, ist es kaputt.
gniourf_gniourf

@gniourf_gniourf Die Zeichenfolge wird in einer Variablen gespeichert. Bitte beachten Sie die ursprüngliche Frage.
Ajaaskel

1
@ajaaskel du hast meinen Kommentar nicht ganz verstanden. Gehen Sie in ein Arbeitsverzeichnis und geben Sie die folgenden Befehle ein : mkdir '!"#$%&'; touch '!"#$%&/()[]{} got you hahahaha - are no problem'. Sie werden nur ein Verzeichnis und eine Datei mit seltsam aussehenden Namen erstellen, muss ich zugeben. Führen Sie dann Ihre Befehle mit genau dem aus, was INSie angegeben haben : IN='bla@some.com;john@home.com;Charlie Brown <cbrown@acme.com;!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'. Sie werden sehen, dass Sie nicht die erwartete Ausgabe erhalten. Weil Sie eine Methode verwenden, die Pfadnamenerweiterungen unterliegt, um Ihre Zeichenfolge zu teilen.
gniourf_gniourf

Dies ist zu zeigen , dass die Zeichen *, ?, [...]und sogar, wenn extglobgesetzt, !(...), @(...), ?(...), +(...) sind Probleme mit dieser Methode!
gniourf_gniourf

1
@gniourf_gniourf Vielen Dank für ausführliche Kommentare zum Globbing. Ich habe den Code so angepasst, dass er nicht mehr funktioniert. Mein Punkt war jedoch nur zu zeigen, dass eine ziemlich einfache Zuordnung die Aufteilungsarbeit erledigen kann.
Ajaaskel

1

Okay Leute!

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 für mich "der Beste"?

Aus zwei Gründen:

  1. Sie müssen nicht entkommen dem Trennzeichen .
  2. Sie werden kein Problem mit Leerzeichen haben . Der Wert wird im Array richtig getrennt!

[] 's


Zu Ihrer Information, /etc/os-releaseund /etc/lsb-releasesollen beschafft und nicht analysiert werden. Ihre Methode ist also wirklich falsch. Außerdem beantworten Sie die Frage nach dem Verschieben einer Zeichenfolge auf ein Trennzeichen
gniourf_gniourf

0

Ein Einzeiler zum Teilen einer durch ';' getrennten Zeichenfolge. in ein Array ist:

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

Dadurch wird IFS nur in einer Subshell festgelegt, sodass Sie sich nicht um das Speichern und Wiederherstellen des Werts kümmern müssen.


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

1
In der Antwort von Nickjb finden Sie eine funktionierende Alternative zu dieser Idee. stackoverflow.com/a/6583589/1032370
Luca Borrione

1
-1, 1. IFS wird in dieser Subshell nicht festgelegt (es wird an die Umgebung von "echo" übergeben, die integriert ist, sodass sowieso nichts passiert). 2. $INwird zitiert, damit es nicht der IFS-Aufteilung unterliegt. 3. Die Prozessersetzung wird durch Leerzeichen aufgeteilt, dies kann jedoch die Originaldaten beschädigen.
Score_Under

0

Vielleicht nicht die eleganteste Lösung, aber 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 (Begrenzer am Anfang und am Ende):

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

Grundsätzlich entfernt sie jedes andere Zeichen als zu ;machen delimszB. ;;;. Dann wird eine forSchleife von 1bis ausgeführt, number-of-delimiterswie von gezählt ${#delims}. Der letzte Schritt besteht darin, das $idritte Teil sicher zu verwenden cut.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.