Vraag Hoe split ik een string op een scheidingsteken in Bash?


Ik heb deze string opgeslagen in een variabele:

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

Nu wil ik de snaren door splitsen ; scheidingsteken zodat ik:

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

Ik heb niet noodzakelijk de ADDR1 en ADDR2 variabelen. Als ze elementen van een array zijn, is dat nog beter.


Na suggesties uit de antwoorden hieronder, eindigde ik met het volgende, dat is wat ik zocht:

#!/usr/bin/env bash

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

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

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

Output:

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

Er was een oplossing met instellen Internal_field_separator (IFS) voor ;. Ik weet niet zeker wat er met dat antwoord is gebeurd, hoe reset je het IFS terug naar standaard?

OPNIEUW: IFS oplossing, ik heb dit geprobeerd en het werkt, ik behoud het oude IFS en herstel het dan:

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

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

IFS=$OIFS

Trouwens, toen ik het probeerde

mails2=($IN)

Ik kreeg alleen de eerste reeks als ik deze in een lus afdrukte, zonder haakjes eromheen $IN het werkt.


1507
2018-05-28 02:03


oorsprong


antwoorden:


U kunt de interne veldscheider (IFS) variabele en laat het vervolgens in een array parseren. Wanneer dit gebeurt in een opdracht, dan is de toewijzing aan IFS vindt alleen plaats in de omgeving van die enkele opdracht (naar read ). Vervolgens wordt de invoer geparseerd volgens de IFS variabele waarde in een array, die we vervolgens kunnen herhalen.

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

Het zal een regel van items scheiden gescheiden door ;, het in een array duwen. Materiaal voor hele verwerking van $IN, elke keer dat een regel invoer wordt gescheiden door ;:

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

922
2018-05-28 02:23



Genomen van Bash shell script split array:

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

Uitleg:

Deze constructie vervangt alle exemplaren van ';' (de initiële // betekent globaal vervangen) in de string IN met ' ' (een enkele spatie), interpreteert vervolgens de door spaties gescheiden reeks als een array (dat is wat de omringende haakjes doen).

De syntaxis die in de accolades wordt gebruikt om elke accolade te vervangen ';' karakter met een ' ' karakter wordt genoemd Parameteruitbreiding.

Er zijn een aantal veel voorkomende gotchas:

  1. Als de oorspronkelijke reeks spaties bevat, moet u deze gebruiken IFS:
    • IFS=':'; arrIN=($IN); unset IFS;
  2. Als de originele string spaties heeft en het scheidingsteken is een nieuwe regel die u kunt instellen IFS met:
    • IFS=$'\n'; arrIN=($IN); unset IFS;

743
2018-03-10 09:00



Als je het niet erg vind om ze meteen te verwerken, doe ik dit graag:

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

U kunt dit soort lus gebruiken om een ​​array te initialiseren, maar er is waarschijnlijk een eenvoudigere manier om dit te doen. Ik hoop dat dit helpt.


207
2018-05-28 02:09



Compatibel antwoord

Op deze SO-vraag is er al veel verschillende manier om dit te doen . Maar bash heeft veel speciaal functies, zogenaamde bashism dat werkt goed, maar dat werkt bij geen ander .

Met name, arrays, associatieve array, en patroonvervanging zijn puur bashisms en werkt mogelijk niet onder een ander schelpen.

Op mijn Debian GNU / Linux, er is een standaard- shell genoemd , maar ik ken veel mensen die graag gebruiken .

Eindelijk, in een zeer kleine situatie, is er een speciale tool genaamd  met zijn eigen shell-tolk ().

Gevraagde tekenreeks

De string-sample in SO-vraag is:

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

Omdat dit nuttig kan zijn bij witruimte en als witruimte zou het resultaat van de routine kunnen wijzigen, ik gebruik liever deze voorbeeldstring:

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

Splitst string gebaseerd op delimiter in  (versie> = 4.2)

Onder zuiver bash, we kunnen gebruiken arrays en IFS:

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

oIFS="$IFS"
IFS=";"
declare -a fields=($var)
IFS="$oIFS"
unset oIFS


121
2018-04-13 14:20



Hoe zit het met deze aanpak:

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

Bron


80
2018-05-28 10:31



Ik heb een aantal antwoorden gezien die verwijzen naar de cut opdracht, maar ze zijn allemaal verwijderd. Het is een beetje vreemd dat niemand daarover verder is gegaan, omdat ik denk dat het een van de nuttigere opdrachten is om dit soort dingen te doen, vooral voor het ontleden van begrensde logbestanden.

In het geval van het splitsen van dit specifieke voorbeeld in een bash-scriptarray, tr is waarschijnlijk efficiënter, maar cut kan worden gebruikt en is effectiever als u specifieke velden vanuit het midden wilt trekken.

Voorbeeld:

$ 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

Je kunt dit natuurlijk in een lus plaatsen en de parameter -f herhalen om elk veld onafhankelijk te trekken.

Dit wordt nuttiger als je een gescheiden logbestand hebt met rijen zoals deze:

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

cut is erg handig om te kunnen cat dit bestand en selecteer een bepaald veld voor verdere verwerking.


74
2018-04-27 18:20



Dit werkte voor mij:

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

65
2017-08-11 20:45



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

59
2018-05-28 02:12