web-dev-qa-db-fra.com

Comment diviser une chaîne sur un délimiteur dans Bash?

J'ai cette chaîne stockée dans une variable:

IN="[email protected];[email protected]"

Maintenant, je voudrais diviser les chaînes par ; délimiteur de sorte que j'ai:

ADDR1="[email protected]"
ADDR2="[email protected]"

Je n'ai pas nécessairement besoin des variables ADDR1 et ADDR2. S'ils sont des éléments d'un tableau, c'est encore mieux.


Après les suggestions des réponses ci-dessous, je me suis retrouvé avec ce qui suit:

#!/usr/bin/env bash

IN="[email protected];[email protected]"

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

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

Sortie:

> [[email protected]]
> [[email protected]]

Il y avait une solution impliquant le réglage Internal_field_separator (IFS) à ;. Je ne suis pas sûr de ce qui s'est passé avec cette réponse. Comment réinitialisez-vous IFS par défaut?

RE: IFS solution, j'ai essayé ceci et cela fonctionne, je garde l'ancien IFS puis je le restaure:

IN="[email protected];[email protected]"

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

IFS=$OIFS

BTW, quand j'ai essayé

mails2=($IN)

Je n’ai reçu que la première chaîne lors de l’impression en boucle, sans crochets $IN cela fonctionne.

1813
stefanB

Vous pouvez définir la variable séparateur de champ interne (IFS), puis le laisser analyser dans un tableau. Lorsque cela se produit dans une commande, l'affectation à IFS n'a lieu que dans l'environnement de cette commande unique (à read). Il analyse ensuite l'entrée en fonction de la valeur de la variable IFS dans un tableau, sur lequel on peut ensuite effectuer une itération.

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

Il analysera une ligne d'éléments séparés par ;, en les insérant dans un tableau. Stuff pour le traitement complet de $IN, à chaque fois une ligne d'entrée séparée par ;:

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

Tiré de tableau divisé par script de Bash Shell:

IN="[email protected];[email protected]"
arrIN=(${IN//;/ })

Explication:

Cette construction remplace toutes les occurrences de ';' (l'initiale // signifie un remplacement global) dans la chaîne IN avec ' ' (un seul espace), puis interprète la chaîne délimitée par des espaces. comme un tableau (c'est ce que font les parenthèses qui l'entourent).

La syntaxe utilisée à l'intérieur des accolades pour remplacer chaque caractère ';' par un caractère ' ' est appelée Expansion des paramètres .

Il y a quelques pièges communs:

  1. Si la chaîne d'origine contient des espaces, vous devrez utiliser IFS :
    • IFS=':'; arrIN=($IN); unset IFS;
  2. Si la chaîne d'origine comporte des espaces et que le délimiteur est une nouvelle ligne, vous pouvez définir IFS avec:
    • IFS=$'\n'; arrIN=($IN); unset IFS;
880
palindrom

Si cela ne vous dérange pas de les traiter immédiatement, j'aime bien faire ceci:

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

Vous pouvez utiliser ce type de boucle pour initialiser un tableau, mais il existe probablement un moyen plus simple de le faire. J'espère que cela aide, cependant.

224
Chris Lutz

Réponse compatible

Pour cette SO question, il y a déjà beaucoup de façons différentes de le faire dans bash . Mais bash a beaucoup de caractéristiques spéciales, appelées bashisme qui fonctionnent bien, mais cela ne fonctionnera pas dans les autres Shell .

En particulier, tableaux, tableau associatif, et substitution de modèle sont purs bashismes et ne fonctionneront peut-être pas sous un autre - coquilles.

Sur mon Debian GNU/Linux, il existe un standard Shell appelé tiret , mais je connais beaucoup de gens qui aiment utiliser ksh .

Enfin, dans de très petites situations, il existe un outil spécial appelé busybox avec son propre interpréteur Shell ( ash ).

Chaîne demandée

L'échantillon de chaîne dans la question SO est:

IN="[email protected];[email protected]"

Comme cela pourrait être utile avec espaces blancs et comme espaces blancs pourrait modifier le résultat de la routine, je préfère utiliser cet exemple de chaîne:

 IN="[email protected];[email protected];Full Name <[email protected]>"

Fractionner la chaîne en fonction du délimiteur dans bash (version> = 4.2)

Sous pure bash, nous pouvons utiliser tableaux et IFS:

var="[email protected];[email protected];Full Name <[email protected]>"
oIFS="$IFS"
IFS=";"
declare -a fields=($var)
IFS="$oIFS"
unset oIFS
IFS=\; read -a fields <<<"$IN"

L'utilisation de cette syntaxe sous bash récent ne change pas $IFS pour la session en cours, mais uniquement pour la commande en cours:

set | grep ^IFS=
IFS=$' \t\n'

Maintenant, la chaîne var est divisée et stockée dans un tableau (nommé fields):

set | grep ^fields=\\\|^var=
fields=([0]="[email protected]" [1]="[email protected]" [2]="Full Name <[email protected]>")
var='[email protected];[email protected];Full Name <[email protected]>'

Nous pourrions demander du contenu variable avec declare -p:

declare -p IN fields
declare -- IN="[email protected];[email protected];Full Name <[email protected]>"
declare -a fields=([0]="[email protected]" [1]="[email protected]" [2]="Full Name <[email protected]>")

read est le moyen le plus rapide de scinder, car il n'y a pas de forks et aucune ressource externe n'est appelée.

À partir de là, vous pouvez utiliser la syntaxe que vous connaissez déjà pour traiter chaque champ:

for x in "${fields[@]}";do
    echo "> [$x]"
    done
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]

ou supprimer chaque champ après traitement (j'aime cette approche shifting):

while [ "$fields" ] ;do
    echo "> [$fields]"
    fields=("${fields[@]:1}")
    done
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]

ou même pour une impression simple (syntaxe plus courte):

printf "> [%s]\n" "${fields[@]}"
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]

Mise à jour: récente bash > = 4.4

Vous pouvez jouer avec mapfile:

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

Cette syntaxe préserve les caractères spéciaux, les nouvelles lignes et les champs vides!

Si vous ne vous souciez pas des champs vides, vous pouvez:

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

Mais vous pouvez utiliser les champs via la fonction:

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

(Nota: \0 à la fin de la chaîne de format sont inutilisables tant que vous ne vous souciez pas des champs vides à la fin de la chaîne)

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

Rendra quelque chose comme:

Seq:      0: Sending mail to '[email protected]', done.
Seq:      1: Sending mail to '[email protected]', done.
Seq:      2: Sending mail to 'Full Name <[email protected]>', done.

Ou Supprimer la nouvelle ligne ajoutée par <<< syntaxe bash dans la fonction:

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

Rendra la même sortie:

Seq:      0: Sending mail to '[email protected]', done.
Seq:      1: Sending mail to '[email protected]', done.
Seq:      2: Sending mail to 'Full Name <[email protected]>', done.

Fractionner la chaîne en fonction du délimiteur dans Shell

Mais si vous voulez écrire quelque chose d’utilisable sous plusieurs coques, vous devez ne pas utiliser bashismes.

Il existe une syntaxe, utilisée dans de nombreux shells, pour scinder une chaîne entre first ​​ou last ​​occurrence d'une sous-chaîne:

${var#*SubStr}  # will drop begin of string up to first occur of `SubStr`
${var##*SubStr} # will drop begin of string up to last occur of `SubStr`
${var%SubStr*}  # will drop part of string from last occur of `SubStr` to the end
${var%%SubStr*} # will drop part of string from first occur of `SubStr` to the end

(Le manque de ceci est la raison principale de la publication de ma réponse;)

Comme indiqué par Score_Under :

# et % suppriment la chaîne de correspondance la plus courte possible, et

## et %% suppriment le plus longtemps possible.

# et ## signifient à partir de la gauche (début) de la chaîne, et

% et %% meand à partir de la droite (fin) de la chaîne.

Ce petit exemple de script fonctionne bien sous bash , tiret , ksh , busybox et a été testé sous le bash de Mac-OS. aussi:

var="[email protected];[email protected];Full Name <[email protected]>"
while [ "$var" ] ;do
    iter=${var%%;*}
    echo "> [$iter]"
    [ "$var" = "$iter" ] && \
        var='' || \
        var="${var#*;}"
  done
> [[email protected]]
> [[email protected]]
> [Full Name <[email protected]>]

S'amuser!

167
F. Hauri

J'ai vu quelques réponses référençant la commande cut, mais elles ont toutes été supprimées. Il est un peu étrange que personne n’en ait parlé plus en détail, car j’estime que c’est l’une des commandes les plus utiles pour effectuer ce type de tâche, en particulier pour l’analyse de fichiers journaux délimités.

Dans le cas de la scission de cet exemple spécifique dans un tableau de script bash, tr est probablement plus efficace, mais cut peut être utilisé et est plus efficace si vous souhaitez extraire des champs spécifiques du milieu.

Exemple:

$ echo "[email protected];[email protected]" | cut -d ";" -f 1
[email protected]
$ echo "[email protected];[email protected]" | cut -d ";" -f 2
[email protected]

Vous pouvez évidemment mettre cela dans une boucle et itérer le paramètre -f pour extraire chaque champ indépendamment.

Cela devient plus utile lorsque vous avez un fichier journal délimité avec des lignes comme celle-ci:

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

cut est très pratique pour pouvoir cat ce fichier et sélectionner un champ particulier pour un traitement ultérieur.

139
DougW

Cela a fonctionné pour moi:

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

Que diriez-vous de cette approche:

IN="[email protected];[email protected]" 
set -- "$IN" 
IFS=";"; declare -a Array=($*) 
echo "${Array[@]}" 
echo "${Array[0]}" 
echo "${Array[1]}" 

Source

84
errator
62
lothar

Cela fonctionne aussi:

IN="[email protected];[email protected]"
echo ADD1=`echo $IN | cut -d \; -f 1`
echo ADD2=`echo $IN | cut -d \; -f 2`

Attention, cette solution n'est pas toujours correcte. Si vous ne transmettez que "[email protected]", il sera attribué à ADD1 et à ADD2.

62
Ashok

Je pense que AWK est la meilleure et efficace commande pour résoudre votre problème. AWK est inclus par défaut dans presque toutes les distributions Linux.

echo "[email protected];[email protected]" | awk -F';' '{print $1,$2}'

va donner

[email protected] [email protected]

Bien sûr, vous pouvez stocker chaque adresse électronique en redéfinissant le champ d'impression awk.

47
Tony

Un autre regard sur réponse de Darron , voici comment je le fais:

IN="[email protected];[email protected]"
read ADDR1 ADDR2 <<<$(IFS=";"; echo $IN)
31
nickjb

Dans Bash, une méthode infaillible, qui fonctionnera même si votre variable contient des nouvelles lignes:

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

Regardez:

$ 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")'

L'astuce pour que cela fonctionne est d'utiliser l'option -d de read (délimiteur) avec un délimiteur vide, de sorte que read soit obligé de lire tout ce qui lui est transmis. Et nous alimentons read avec exactement le contenu de la variable in, sans fin de nouvelle ligne grâce à printf. Notez que nous plaçons également le délimiteur dans printf pour nous assurer que la chaîne transmise à read comporte un délimiteur de fin. Sans elle, read réduirait les champs vides en fin de chaîne:

$ 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]="")'

le dernier champ vide est conservé.


Mise à jour pour Bash≥4.4

Depuis Bash 4.4, le mapfile intégré (aussi appelé readarray) prend en charge l’option -d pour spécifier un délimiteur. Une autre manière canonique est donc:

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

Que diriez-vous de cette doublure, si vous n'utilisez pas de tableaux:

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

Voici un 3-liner propre:

in="[email protected];[email protected];[email protected]zz;[email protected]"
IFS=';' list=($in)
for item in "${list[@]}"; do echo $item; done

IFS délimitent des mots basés sur le séparateur et () sert à créer un tablea . Ensuite, [@] est utilisé pour renvoyer chaque élément en tant que mot séparé.

Si vous avez du code après cela, vous devez également restaurer $IFS, par exemple. unset IFS.

19
kenorb

Sans régler l'IFS

Si vous n'avez qu'un colon, vous pouvez le faire:

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

tu auras:

b = foo
c = bar
17
Emilien Brigand

La fonction Bash/zsh suivante divise son premier argument sur le délimiteur donné par le second argument:

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
}

Par exemple, la commande

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

les rendements

a
b
c

Cette sortie peut, par exemple, être acheminée vers d'autres commandes. Exemple:

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

Par rapport aux autres solutions proposées, celle-ci présente les avantages suivants:

  • IFS n'est pas annulé: en raison de la portée dynamique de variables même locales, le remplacement de IFS par une boucle entraîne la fuite de la nouvelle valeur dans les appels de fonction effectués à l'intérieur de la boucle.

  • Les tableaux ne sont pas utilisés: la lecture d'une chaîne dans un tableau à l'aide de read nécessite l'indicateur -a dans Bash et -A dans zsh.

Si vous le souhaitez, la fonction peut être insérée dans un script comme suit:

#!/usr/bin/env bash

split() {
    # ...
}

split "[email protected]"
10
Halle Knast

Il existe un moyen simple et intelligent comme celui-ci:

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

Mais vous devez utiliser gnu xargs, BSD xargs ne peut pas supporter -d delim. Si vous utilisez Apple mac comme moi. Vous pouvez installer gnu xargs:

brew install findutils

ensuite

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

vous pouvez appliquer awk à de nombreuses situations

echo "[email protected];[email protected]"|awk -F';' '{printf "%s\n%s\n", $1, $2}'

aussi vous pouvez utiliser cette

echo "[email protected];[email protected]"|awk -F';' '{print $1,$2}' OFS="\n"
7
shuaihanhungry

C'est le moyen le plus simple de le faire.

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

Il y a quelques bonnes réponses ici (errator esp.), Mais pour que quelque chose d'analogue se scinde en d'autres langues - c'est ce que j'ai compris comme question initiale - je me suis décidé sur ceci:

IN="[email protected];[email protected]"
declare -a a="(${IN/;/ })";

Maintenant, ${a[0]}, ${a[1]}, etc., sont comme vous le souhaitiez. Utilisez ${#a[*]} pour le nombre de termes. Ou pour itérer, bien sûr:

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

NOTE IMPORTANTE:

Cela fonctionne dans les cas où il n'y a pas d'espaces à craindre, ce qui résout mon problème mais ne résout peut-être pas le vôtre. Allez avec la solution $IFS dans ce cas.

4
eukras
IN="[email protected];[email protected]"
IFS=';'
read -a IN_arr <<< "${IN}"
for entry in "${IN_arr[@]}"
do
    echo $entry
done

Sortie

[email protected]
[email protected]

Système: Ubuntu 12.04.1

3
rashok

Deux alternatives bourne-ish, où ni l'un ni l'autre n'exige de tableaux bash:

Cas 1: Keep it Nice and simple: Utilisez une NewLine comme séparateur d’enregistrements ... par ex.

IN="[email protected]
[email protected]"

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

Remarque: dans ce premier cas, aucun sous-processus n'est créé pour faciliter la manipulation de la liste.

Idée: peut-être vaut-il la peine d’utiliser NL de manière intensive en interne et de ne convertir que sur un autre RS lors de la génération du résultat final à l'extérieur .

Cas 2: Utiliser un ";" comme séparateur d’enregistrements ...

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

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

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

IN="[email protected];[email protected]"
IN="$(conv_IRS ";" <<< "$IN")"

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

Dans les deux cas, une sous-liste pouvant être composée dans la boucle est persistante une fois la boucle terminée. Ceci est utile lors de la manipulation de listes en mémoire, au lieu de stocker des listes dans des fichiers. {p.s. reste calme et continue B-)}

2
NevilleDNZ

Outre les réponses fantastiques qui ont déjà été fournies, s’il ne s’agit que d’imprimer les données, vous pouvez envisager d’utiliser awk:

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

Cela définit le séparateur de champ sur ; afin qu'il puisse parcourir les champs avec une boucle for et imprimer en conséquence.

Tester

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

Avec une autre entrée:

$ 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
fedorqui
IN='[email protected];[email protected];Charlie Brown <[email protected];!"#$%&/()[]{}*? 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

Sortie:

[email protected]
[email protected]
Charlie Brown <[email protected]
!"#$%&/()[]{}*? are no problem
simple is beautiful :-)

Explication: Une simple affectation à l'aide de parenthesis () convertit la liste séparée par des points-virgules en un tableau, à condition que vous disposiez de l'IFS correct. La boucle FOR standard gère les éléments individuels de ce tableau comme d'habitude. Notez que la liste donnée pour la variable IN doit être "dure", c'est-à-dire avec des ticks simples.

IFS doit être sauvegardé et restauré car Bash ne traite pas une affectation de la même manière qu'une commande. Une autre solution consiste à envelopper l'affectation dans une fonction et à l'appeler avec un IFS modifié. Dans ce cas, il n'est pas nécessaire de sauvegarder/restaurer l'IFS séparément. Merci pour "Bize" pour le signaler.

2
ajaaskel

Si pas d'espace, pourquoi pas cela?

IN="[email protected];[email protected]"
arr=(`echo $IN | tr ';' ' '`)

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

Ok les gars!

Voici ma réponse!

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

Pourquoi cette approche est-elle "la meilleure" pour moi?

Pour deux raisons:

  1. Vous faites pas besoin d'échapper le délimiteur;
  2. Vous n'aurez pas problème avec les espaces vides. La valeur sera correctement séparée dans le tableau!

[]

1
Eduardo Lucio

Dans Android Shell, la plupart des méthodes proposées ne fonctionnent tout simplement pas:

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

Qu'est-ce que le travail est:

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

// signifie remplacement global.

1
18446744073709551615

Utilisez le set intégré pour charger le tableau [email protected]:

IN="[email protected];[email protected]"
IFS=';'; set $IN; IFS=$' \t\n'

Ensuite, que la fête commence:

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

Peut-être pas la solution la plus élégante, mais fonctionne avec * et des espaces:

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

Les sorties

> [[email protected] me.com]
> [*]
> [[email protected]]

Autre exemple (délimiteurs au début et à la fin):

IN=";[email protected] me.com;*;[email protected];"
> []
> [[email protected] me.com]
> [*]
> [[email protected]]
> []

En gros, il supprime tous les caractères autres que ; rendant delims eg. ;;;. Ensuite, la boucle for passe de 1 à number-of-delimiters telle que comptée par ${#delims}. La dernière étape consiste à obtenir en toute sécurité la partie $i à l'aide de cut.

0
Petr Újezdský

Une ligne pour diviser une chaîne séparée par un ';' dans un tableau est:

IN="[email protected];[email protected]"
ADDRS=( $(IFS=";" echo "$IN") )
echo ${ADDRS[0]}
echo ${ADDRS[1]}

Cela ne fait que définir IFS dans un sous-shell, vous évitant ainsi de sauvegarder et de restaurer sa valeur.

0
Michael Hale

Cela va même gérer les espaces:

IFS=';' read ADDR1 ADDR2 <<< $(echo ${IN})
0
Mat Bess