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 afin d'avoir:
ADDR1="[email protected]"
ADDR2="[email protected]"
Je n'ai pas nécessairement besoin des variables ADDR1
et ADDR2
. 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 était ce que je recherchais:
#!/usr/bin/env bash
IN="[email protected];[email protected]"
mails=$(echo $IN | tr ";" "\n")
for addr in $mails
do
echo "> [$addr]"
done
Production:
> [bla@some.com]
> [john@home.com]
Il y avait une solution impliquant de définir Internal_field_separator (IFS) sur ;
. Je ne sais pas ce qui s'est passé avec cette réponse, comment pouvez-vous réinitialiser les IFS
paramètres par défaut?
RE: IFS
solution, j'ai essayé ça et ça marche, je garde l'ancien IFS
puis 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 obtenu la première chaîne que lors de l'impression en boucle, sans crochets, $IN
cela fonctionne.
local IFS=...
dans la mesure du possible; (b) -1 pourunset IFS
, cela ne réinitialise pas exactement IFS à sa valeur par défaut, bien que je pense qu'un IFS non défini se comporte de la même manière que la valeur par défaut d'IFS ($ '\ t \ n'), mais cela semble une mauvaise pratique de supposer aveuglément que votre code ne sera jamais invoqué avec IFS défini sur une valeur personnalisée; (c) une autre idée est d'invoquer un sous-shell:(IFS=$custom; ...)
lorsque le sous-shell quitte IFS retournera à ce qu'il était à l'origine.ruby -e "puts ENV.fetch('PATH').split(':')"
. Si vous voulez rester pur bash n'aidera pas, mais l'utilisation de tout langage de script ayant un split intégré est plus facile.for x in $(IFS=';';echo $IN); do echo "> [$x]"; done
\n
pour juste un espace. Donc, la dernière ligne estmails=($(echo $IN | tr ";" " "))
. Alors maintenant, je peux vérifier les éléments demails
en utilisant la notation de tableaumails[index]
ou simplement en itérant dans une boucleRéponses:
Vous pouvez définir la variable de séparateur de champ interne (IFS), puis la 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 laIFS
valeur de la variable dans un tableau, que nous pouvons ensuite parcourir.Il analysera une ligne d'éléments séparés par
;
, en la poussant dans un tableau. Trucs pour le traitement de l'ensemble$IN
, à chaque fois une ligne d'entrée séparée par;
:la source
IFS
sur la même ligne que leread
sans point-virgule ou autre séparateur, par opposition à dans une commande distincte, l'étend à cette commande - il est donc toujours "restauré"; vous n'avez rien à faire manuellement.$IN
être cité. Le bug est corrigé dansbash
4.3.Tiré du tableau de division du script shell Bash :
Explication:
Cette construction remplace toutes les occurrences de
';'
(le premier//
signifie le remplacement global) dans la chaîneIN
par' '
(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
';'
caractère par un' '
caractère est appelée Expansion des paramètres .Il existe quelques pièges courants:
IFS=':'; arrIN=($IN); unset IFS;
IFS=$'\n'; arrIN=($IN); unset IFS;
la source
IN="[email protected];[email protected];*;broken apart"
. En bref: cette approche ne fonctionnera pas si vos jetons contiennent des espaces et / ou des caractères intégrés. tels que*
cela arrive pour faire correspondre les noms de fichiers des jetons dans le dossier actuel.;*;
, alors le*
sera étendu à une liste de noms de fichiers dans le répertoire courant. -1Si cela ne vous dérange pas de les traiter immédiatement, j'aime faire ceci:
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 vous aidera.
la source
IN="[email protected];[email protected];*;broken apart"
. En bref: cette approche ne fonctionnera pas si vos jetons contiennent des espaces et / ou des caractères intégrés. tels que*
cela arrive pour faire correspondre les noms de fichiers des jetons dans le dossier actuel.Réponse compatible
Il existe de nombreuses façons de le faire dans frapper.
Cependant, il est important de noter d'abord qu'il
bash
a de nombreuses fonctionnalités spéciales (soi-disant bashismes ) qui ne fonctionneront dans aucune autrecoquille.En particulier, les tableaux , les tableaux associatifs et la substitution de modèles , qui sont utilisés dans les solutions de cet article ainsi que d'autres dans le fil, sont des bashismes et peuvent ne pas fonctionner sous d'autres shells que beaucoup de gens utilisent.
Par exemple: sur mon Debian GNU / Linux , il y a un shell standard appelétiret; Je connais beaucoup de gens qui aiment utiliser un autre shell appeléksh; et il y a aussi un outil spécial appeléoccupé avec son propre interprète shell (cendre).
Chaîne demandée
La chaîne à séparer dans la question ci-dessus est:
J'utiliserai une version modifiée de cette chaîne pour m'assurer que ma solution est robuste aux chaînes contenant des espaces blancs, ce qui pourrait casser d'autres solutions:
Fractionner la chaîne en fonction du délimiteur dans frapper (version> = 4.2)
En pur
bash
, on peut créer un tableau avec des éléments divisés par une valeur temporaire pour IFS (le séparateur de champ d'entrée ). L'IFS, entre autres, indiquebash
quel (s) caractère (s) il doit traiter comme un délimiteur entre les éléments lors de la définition d'un tableau:Dans les versions plus récentes de
bash
, le fait de préfixer une commande avec une définition IFS modifie l'IFS pour cette commande uniquement et la réinitialise à la valeur précédente immédiatement après. Cela signifie que nous pouvons faire ce qui précède en une seule ligne:Nous pouvons voir que la chaîne
IN
a été stockée dans un tableau nomméfields
, divisé sur les points-virgules:(Nous pouvons également afficher le contenu de ces variables en utilisant
declare -p
:)Notez que
read
c'est le moyen le plus rapide de faire le fractionnement car il n'y a pas de fourches ou de ressources externes appelées.Une fois le tableau défini, vous pouvez utiliser une simple boucle pour traiter chaque champ (ou, plutôt, chaque élément du tableau que vous avez maintenant défini):
Ou vous pouvez supprimer chaque champ du tableau après le traitement à l'aide d'un approche de décalage , que j'aime:
Et si vous voulez juste une simple impression du tableau, vous n'avez même pas besoin de le parcourir:
Mise à jour: récente frapper > = 4,4
Dans les versions plus récentes de
bash
, vous pouvez également jouer avec la commandemapfile
:Cette syntaxe préserve les caractères spéciaux, les nouvelles lignes et les champs vides!
Si vous ne souhaitez pas inclure de champs vides, vous pouvez procéder comme suit:
Avec
mapfile
, vous pouvez également sauter la déclaration d'un tableau et implicitement "boucler" sur les éléments délimités, en appelant une fonction sur chacun:(Remarque: la
\0
fin de la chaîne de format est inutile si vous ne vous souciez pas des champs vides à la fin de la chaîne ou s'ils ne sont pas présents.)Ou vous pouvez utiliser
<<<
, et dans le corps de la fonction, inclure un traitement pour supprimer la nouvelle ligne qu'il ajoute:Fractionner la chaîne en fonction du délimiteur dans coquille
Si vous ne pouvez pas utiliser
bash
, ou si vous voulez écrire quelque chose qui peut être utilisé dans de nombreux shells différents, vous ne pouvez souvent pas utiliser de bashismes - et cela inclut les tableaux que nous avons utilisés dans les solutions ci-dessus.Cependant, nous n'avons pas besoin d'utiliser des tableaux pour faire une boucle sur les "éléments" d'une chaîne. Il existe une syntaxe utilisée dans de nombreux shells pour supprimer les sous-chaînes d'une chaîne de la première ou de la dernière occurrence d'un modèle. Notez que
*
s'agit d'un caractère générique qui représente zéro ou plusieurs caractères:(L'absence de cette approche dans toute solution publiée jusqu'à présent est la principale raison pour laquelle j'écris cette réponse;)
Comme expliqué par Score_Under :
En utilisant la syntaxe ci-dessus, nous pouvons créer une approche où nous extrayons des "éléments" de sous-chaîne de la chaîne en supprimant les sous-chaînes jusqu'au délimiteur ou après.
Le bloc de code ci-dessous fonctionne bien dans frapper(y compris Mac OS
bash
),tiret, ksh, et occupéc'est cendre:S'amuser!
la source
#
,##
,%
et%%
substitutions ont ce qui est l' OMI une explication plus facile à retenir (pour combien ils supprimer):#
et%
supprimer la plus courte chaîne de correspondance possible, et##
et%%
supprimer la plus longue possible.IFS=\; read -a fields <<<"$var"
échoue sur les sauts de ligne et ajoute un saut de ligne de fin. L'autre solution supprime un champ vide de fin.for sep in "#" "ł" "@" ; do ... var="${var#*$sep}" ...
J'ai vu quelques réponses faisant référence à la
cut
commande, mais elles ont toutes été supprimées. C'est un peu étrange que personne n'ait développé cela, car je pense que c'est l'une des commandes les plus utiles pour faire ce genre de chose, en particulier pour analyser des fichiers journaux délimités.Dans le cas de la division de cet exemple spécifique en un tableau de scripts bash,
tr
est probablement plus efficace, maiscut
peut être utilisé, et est plus efficace si vous souhaitez extraire des champs spécifiques du milieu.Exemple:
Vous pouvez évidemment mettre cela en boucle et itérer le paramètre -f pour extraire chaque champ indépendamment.
Cela devient plus utile lorsque vous disposez d'un fichier journal délimité avec des lignes comme celle-ci:
cut
est très pratique pour pouvoir accéder àcat
ce fichier et sélectionner un champ particulier pour un traitement ultérieur.la source
cut
, c'est le bon outil pour le travail! Beaucoup plus clair que n'importe lequel de ces hacks shell.Cela a fonctionné pour moi:
la source
Que diriez-vous de cette approche:
La source
la source
IFS";" && Array=($IN)
$'...'
:IN=$'[email protected];[email protected];bet <d@\ns* kl.com>'
. Imprime ensuiteecho "${Array[2]}"
une chaîne avec la nouvelle ligne.set -- "$IN"
est également nécessaire dans ce cas. Oui, pour empêcher l'expansion globale, la solution doit inclureset -f
.Je pense qu'AWK est la commande la meilleure et la plus efficace pour résoudre votre problème. AWK est inclus par défaut dans presque toutes les distributions Linux.
va donner
Bien sûr, vous pouvez stocker chaque adresse e-mail en redéfinissant le champ d'impression awk.
la source
inode=
dans;
par exemplesed -i 's/inode\=/\;/g' your_file_to_process
, puis définir-F';'
quand appliquerawk
, l' espoir qui peut vous aider.la source
IN="this is first line; this is second line" arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )
, produira un tableau de 8 éléments dans ce cas (un élément pour chaque espace de mots séparé), plutôt que 2 (un élément pour chaque ligne point-virgule séparé)arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )
pour atteindre cet objectif, et aux conseils pour changer IFSIFS=$'\n'
pour ceux qui atterriront ici à l'avenir et doivent diviser une chaîne contenant des espaces. (et pour le restaurer ensuite). :)Cela fonctionne également:
Attention, cette solution n'est pas toujours correcte. Si vous passez "[email protected]" uniquement, il l'assignera à la fois à ADD1 et ADD2.
la source
Une approche différente de la réponse de Darron, voici comment je le fais:
la source
IFS=";"
affectation n'existe que dans la$(...; echo $IN)
sous - coque; c'est pourquoi certains lecteurs (dont moi) pensent initialement que cela ne fonctionnera pas. J'ai supposé que tout $ IN était absorbé par ADDR1. Mais nickjb est correct; ça marche. La raison en est que laecho $IN
commande analyse ses arguments en utilisant la valeur actuelle de $ IFS, mais les renvoie ensuite à stdout à l'aide d'un délimiteur d'espace, quel que soit le paramètre de $ IFS. L'effet net est donc comme si quelqu'un avait appeléread ADDR1 ADDR2 <<< "[email protected] [email protected]"
(notez que l'entrée est séparée par des espaces et non séparée par des espaces).*
dans leecho $IN
avec une expansion de variable non cotée.Dans Bash, une méthode à l'épreuve des balles, cela fonctionnera même si votre variable contient des retours à la ligne:
Regardez:
L'astuce pour que cela fonctionne est d'utiliser l'
-d
option deread
(délimiteur) avec un délimiteur vide, ce quiread
oblige à lire tout ce qui est alimenté. Et nous alimentonsread
exactement le contenu de la variablein
, sans retour à la ligne grâce àprintf
. Notez que nous mettons également le délimiteurprintf
pour nous assurer que la chaîne passée àread
a un délimiteur de fin. Sans cela,read
couperait les champs vides en fin potentiels:le champ vide de fin est conservé.
Mise à jour pour Bash≥4.4
Depuis Bash 4.4, la fonction intégrée
mapfile
(akareadarray
) prend en charge l'-d
option de spécifier un délimiteur. Par conséquent, une autre voie canonique est:la source
\n
, les espaces et*
simultanément. En outre, pas de boucles; La variable tableau est accessible dans le shell après exécution (contrairement à la réponse la plus élevée). Notez quein=$'...'
cela ne fonctionne pas avec les guillemets doubles. Je pense qu'il a besoin de plus de votes positifs.Que diriez-vous de cette doublure, si vous n'utilisez pas de tableaux:
la source
read -r ...
pour vous assurer que, par exemple, les deux caractères "\ t" dans l'entrée finissent comme les deux mêmes caractères dans vos variables (au lieu d'un seul caractère de tabulation).echo "ADDR1 $ADDR1"\n echo "ADDR2 $ADDR2"
à votre extrait de code produiraADDR1 [email protected] [email protected]\nADDR2
(\ n est une nouvelle ligne)IFS
et ici des chaînes qui a été corrigé dansbash
4.3. Citer$IN
devrait le réparer. (En théorie,$IN
n'est pas soumis à la séparation ou à la globalisation des mots après son expansion, ce qui signifie que les guillemets ne devraient pas être nécessaires. idée.)Sans paramétrer l'IFS
Si vous n'avez qu'un seul colon, vous pouvez le faire:
tu auras:
la source
Voici un 3-liner propre:
où
IFS
délimite les mots en fonction du séparateur et()
est utilisé pour créer un tableau . Puis[@]
est utilisé pour renvoyer chaque élément en tant que mot distinct.Si vous avez du code après cela, vous devez également restaurer
$IFS
, par exempleunset IFS
.la source
$in
caractères non cotés permet d'étendre les caractères génériques.La fonction Bash / zsh suivante divise son premier argument sur le délimiteur donné par le deuxième argument:
Par exemple, la commande
les rendements
Cette sortie peut, par exemple, être redirigée vers d'autres commandes. Exemple:
Par rapport aux autres solutions proposées, celle-ci présente les avantages suivants:
IFS
n'est pas redéfini: en raison de la portée dynamique des variables locales, même, la redéfinition d'IFS
une boucle provoque une fuite de la nouvelle valeur dans les appels de fonction effectués à partir 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 placée dans un script comme suit:
la source
help read
:-d delim continue until the first character of DELIM is read, rather than newline
vous pouvez appliquer awk à de nombreuses situations
vous pouvez également l'utiliser
la source
Il existe un moyen simple et intelligent comme celui-ci:
Mais vous devez utiliser gnu xargs, BSD xargs ne supporte pas -d delim. Si vous utilisez Apple Mac comme moi. Vous pouvez installer gnu xargs:
puis
la source
C'est la façon la plus simple de le faire.
la source
Il y a des réponses intéressantes ici (errator en particulier), mais pour quelque chose d'analogue à diviser dans d'autres langues - c'est ce que j'ai pris pour la question initiale - je me suis installé sur ceci:
Maintenant
${a[0]}
,${a[1]}
etc, sont comme vous vous en doutez. Utilisez${#a[*]}
pour le nombre de termes. Ou pour itérer, bien sûr:NOTE IMPORTANTE:
Cela fonctionne dans les cas où il n'y a pas d'espace à s'inquiéter, ce qui a résolu mon problème, mais peut ne pas résoudre le vôtre. Allez avec la
$IFS
(les) solution (s) dans ce cas.la source
IN
contient plus de deux adresses de messagerie. Veuillez vous référer à la même idée (mais fixe) dans la réponse de palindrom${IN//;/ }
(double barre oblique) pour qu'il fonctionne également avec plus de deux valeurs. Attention, tout caractère générique (*?[
) sera développé. Et un champ vide de fin sera supprimé.Production
Système: Ubuntu 12.04.1
la source
read
ici et peut donc perturber le reste du code, le cas échéant.S'il n'y a pas d'espace, pourquoi pas ça?
la source
Utilisez le
set
intégré pour charger la$@
baie:Ensuite, laissez la fête commencer:
la source
set -- $IN
pour éviter certains problèmes avec "$ IN" commençant par le tiret. Néanmoins, l'expansion non citée de$IN
étendra les caractères génériques (*?[
).Deux alternatives bourne-ish où aucune ne nécessite de tableaux bash:
Cas 1 : Restez simple et agréable: utilisez un NewLine comme séparateur d'enregistrement ... par exemple.
Remarque: dans ce premier cas, aucun sous-processus n'est bifurqué pour aider à la manipulation de la liste.
Idée: Peut-être que cela vaut la peine d'utiliser NL de manière intensive en interne , et de convertir uniquement en un RS différent lors de la génération du résultat final en externe .
Cas 2 : utilisation d'un ";" comme séparateur d'enregistrement ... par exemple.
Dans les deux cas, une sous-liste peut être composée au sein de 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. {ps reste calme et continue B-)}
la source
Outre les réponses fantastiques qui ont déjà été fournies, s'il s'agit simplement d'imprimer les données, vous pouvez envisager d'utiliser
awk
:Cela définit le séparateur de champs sur
;
, afin qu'il puisse parcourir les champs avec unefor
boucle et imprimer en conséquence.Tester
Avec une autre entrée:
la source
Dans le shell Android, la plupart des méthodes proposées ne fonctionnent tout simplement pas:
Ce qui fonctionne, c'est:
où
//
signifie remplacement global.la source
Production:
Explication: L'affectation simple à l'aide de parenthèses () convertit la liste séparée par des points-virgules en un tableau à condition que vous disposiez de l'IFS correct pour ce faire. 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" entre guillemets, c'est-à-dire avec des ticks simples.
IFS doit être enregistré et restauré car Bash ne traite pas une affectation de la même manière qu'une commande. Une autre solution consiste à encapsuler l'affectation à l'intérieur d'une fonction et à appeler cette fonction avec un IFS modifié. Dans ce cas, une sauvegarde / restauration séparée d'IFS n'est pas nécessaire. Merci pour "Bize" de l'avoir signalé.
la source
!"#$%&/()[]{}*? are no problem
bien ... pas tout à fait:[]*?
sont des caractères glob. Qu'en est-il de la création de ce répertoire et de ce fichier: `mkdir '!" # $% &'; Touch '! "# $% & / () [] {} Vous avez hahahaha - pas de problème' et exécutez votre commande? simple peut être beau, mais quand il est cassé, il est cassé.mkdir '!"#$%&'; touch '!"#$%&/()[]{} got you hahahaha - are no problem'
. Ils ne créeront qu'un répertoire et un fichier, avec des noms étranges, je dois l'admettre. Ensuite , exécutez vos commandes avec l'exacte queIN
vous avez donné:IN='[email protected];[email protected];Charlie Brown <[email protected];!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'
. Vous verrez que vous n'obtiendrez pas la sortie que vous attendez. Parce que vous utilisez une méthode soumise à des extensions de chemin pour diviser votre chaîne.*
,?
,[...]
et même, siextglob
est réglé,!(...)
,@(...)
,?(...)
,+(...)
sont des problèmes avec cette méthode!D'accord les gars!
Voici ma réponse!
Pourquoi cette approche est "la meilleure" pour moi?
Pour deux raisons:
[]
la source
/etc/os-release
et/etc/lsb-release
sont censés provenir, et non analysés. Votre méthode est donc vraiment fausse. De plus, vous ne répondez pas tout à fait à la question de la rotation d'une chaîne sur un délimiteur.Une ligne pour séparer une chaîne séparée par ';' dans un tableau est:
Cela ne définit IFS que dans un sous-shell, vous n'avez donc pas à vous soucier de l'enregistrement et de la restauration de sa valeur.
la source
0: [email protected];[email protected]\n 1:
(\ n est la nouvelle ligne)$IN
est cité de sorte qu'il n'est pas soumis au fractionnement IFS. 3. La substitution de processus est divisée par des espaces, mais cela peut corrompre les données d'origine.Peut-être pas la solution la plus élégante, mais fonctionne avec
*
et les espaces:Les sorties
Autre exemple (délimiteurs au début et à la fin):
Fondamentalement, il supprime tous les caractères autres que la
;
création,delims
par exemple.;;;
. Ensuite, ilfor
boucle de1
ànumber-of-delimiters
comme compté par${#delims}
. La dernière étape consiste à obtenir en toute sécurité la$i
pièce en utilisantcut
.la source