Question Comment séparer une chaîne sur un délimiteur dans Bash?


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

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

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

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

Je n'ai pas nécessairement besoin du ADDR1 et ADDR2 variables Si ce 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 ce que j'étais après:

#!/usr/bin/env bash

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

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

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

Sortie:

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

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

RÉ: IFS solution, j'ai essayé ceci et cela fonctionne, je garde l'ancien IFS puis restaurez-le:

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

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

IFS=$OIFS

BTW, quand j'ai essayé

mails2=($IN)

J'ai seulement obtenu la première chaîne en l'imprimant en boucle, sans supports autour $IN Ça marche.


1507
2018-05-28 02:03


origine


Réponses:


Vous pouvez définir le séparateur de champ interne (IFS), puis laissez-le analyser dans un tableau. Lorsque cela se produit dans une commande, l'affectation à IFS ne prend place que dans l'environnement de cette seule commande (pour read ). Il analyse ensuite l'entrée en fonction de la IFS valeur variable dans un tableau, que nous pouvons ensuite parcourir.

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 le poussant dans un tableau. Trucs pour le traitement de l'ensemble 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"

922
2018-05-28 02:23



Pris à partir de Tableau de partage de script shell Bash:

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

Explication:

Cette construction remplace toutes les occurrences de ';' (la première // signifie remplacer 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 environnantes).

La syntaxe utilisée à l'intérieur des accolades pour remplacer chaque ';' personnage avec un ' ' le personnage est appelé Expansion de paramètre.

Il y a quelques pièges courants:

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

743
2018-03-10 09:00



Si cela ne vous dérange pas de les traiter immédiatement, j'aime 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.


207
2018-05-28 02:09



Réponse compatible

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

En particulier, tableaux, tableau associatif, et substitution de motif sont purs les bashismes et ne peut pas travailler sous d'autres coquilles.

Sur mon Debian GNU / Linux, Il y a un la norme shell appelé , mais je connais beaucoup de gens qui aiment utiliser .

Enfin, dans une très petite situation, il existe un outil spécial appelé  avec son propre interprète shell ().

Chaîne demandée

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

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

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 cette chaîne d'exemple:

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

Séparer la chaîne en fonction du délimiteur dans  (version> = 4,2)

En dessous de pur bash, nous pouvons utiliser tableaux et 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



Que diriez-vous de cette approche:

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

La source


80
2018-05-28 10:31



J'ai vu quelques réponses faisant référence à cut commande, mais ils ont tous été supprimés. C'est un peu étrange que personne n'ait développé cela, parce que je pense que c'est l'une des commandes les plus utiles pour faire ce genre de chose, en particulier pour l'analyse de fichiers journaux délimités.

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

Exemple:

$ 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

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

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

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

cut est très pratique pour être en mesure de cat ce fichier et sélectionnez un champ particulier pour un traitement ultérieur.


74
2018-04-27 18:20



Cela a fonctionné pour moi:

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



Cela fonctionne aussi:

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

Attention, cette solution n'est pas toujours correcte. Dans le cas où vous passez "bla@some.com" seulement, il l'assignera à ADD1 et ADD2.


57
2017-09-08 05:01



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

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

va donner

bla@some.com john@home.com

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


32
2018-01-14 06:33