web-dev-qa-db-fra.com

Comment diviser une chaîne délimitée en un tableau dans awk?

Comment diviser la chaîne lorsqu'elle contient des symboles de canal |. Je veux les séparer pour être dans le tableau.

J'ai essayé

echo "12:23:11" | awk '{split($0,a,":"); print a[3] a[2] a[1]}'

Ce qui fonctionne bien. Si ma chaîne ressemble à "12|23|11" alors comment puis-je les diviser en un tableau?

131
Mohamed Saligh

As-tu essayé:

echo "12|23|11" | awk '{split($0,a,"|"); print a[3],a[2],a[1]}'
210

Pour scinder une chaîne en un tableau dans awk, nous utilisons la fonction split() :

 awk '{split($0, a, ":")}'
 #           ^^  ^  ^^^
 #            |  |   |
 #       string  |   delimiter
 #               |
 #               array to store the pieces

Si aucun séparateur n'est donné, il utilise la variable FS, qui prend par défaut l'espace:

$ awk '{split($0, a); print a[2]}' <<< "a:b c:d e"
c:d

Nous pouvons donner un séparateur, par exemple ::

$ awk '{split($0, a, ":"); print a[2]}' <<< "a:b c:d e"
b c

Ce qui équivaut à le définir via la FS:

$ awk -F: '{split($0, a); print a[1]}' <<< "a:b c:d e"
b c

Dans gawk, vous pouvez également fournir le séparateur sous forme d'expressions rationnelles:

$ awk '{split($0, a, ":*"); print a[2]}' <<< "a:::b c::d e" #note multiple :
b c

Et même voir ce que le délimiteur était sur chaque étape en utilisant son quatrième paramètre:

$ awk '{split($0, a, ":*", sep); print a[2]; print sep[1]}' <<< "a:::b c::d e"
b c
:::

Citons la page de manuel de GNU awk :

split (chaîne, tableau [ champsep [ seps]])

Divisez la chaîne en morceaux séparés par fieldsep et stockez les morceaux dans tableau et les chaînes de séparation dans le tableau seps . Le premier morceau est stocké dans array[1], le deuxième morceau dans array[2], et ainsi de suite. La valeur de chaîne du troisième argument, fieldsep , est une expression rationnelle décrivant le lieu de division de la chaîne (un peu comme FS peut être une expression rationnelle décrivant le lieu de fractionnement des enregistrements d'entrée). Si fieldsep est omis, la valeur de FS est utilisée. split() renvoie le nombre d'éléments créés. seps est une extension gawk, avec seps[i] représentant la chaîne de séparation entre array[i] et array[i+1] . Si fieldsep est un espace unique, alors tout espace au début est placé dans seps[0] et tout espace à la fin passe à seps[n], où n est la valeur de retour de split() (c'est-à-dire le nombre d'éléments dans un tableau).

95
fedorqui

S'il vous plaît être plus précis! Que voulez-vous dire par "ça ne marche pas"? Publiez la sortie exacte (ou le message d'erreur), votre version du système d'exploitation et awk:

% awk -F\| '{
  for (i = 0; ++i <= NF;)
    print i, $i
  }' <<<'12|23|11'
1 12
2 23
3 11

Ou, en utilisant split:

% awk '{
  n = split($0, t, "|")
  for (i = 0; ++i <= n;)
    print i, t[i]
  }' <<<'12|23|11'
1 12
2 23
3 11

Edit: on Solaris vous aurez besoin d'utiliser POSIX awk (/ usr/xpg4/bin/awk) pour traiter 4000 champs correctement.

15
Dimitre Radoulov
echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'
3
Schildmeijer

En fait, awk possède une fonctionnalité appelée 'Variable de séparation de champ de saisie' lien . Voici comment l'utiliser. Ce n'est pas vraiment un tableau, mais il utilise les variables internes $. Pour fendre une chaîne simple, c'est plus facile.

echo "12|23|11" | awk 'BEGIN {FS="|";} { print $1, $2, $3 }'
3
Sven

Je n'aime pas la solution echo "..." | awk ... car elle appelle des appels inutiles fork et execsystem.

Je préfère une solution de Dimitre avec un peu de torsion

awk -F\| '{print $3 $2 $1}' <<<'12|23|11'

Ou version un peu plus courte:

awk -F\| '$0=$3 $2 $1' <<<'12|23|11'

Dans ce cas, l'enregistrement de sortie mis en place est une condition vraie, il est donc imprimé.

Dans ce cas spécifique, la redirection stdin peut être épargnée avec le paramétrage d'une variable interne awk :

awk -v T='12|23|11' 'BEGIN{split(T,a,"|");print a[3] a[2] a[1]}'

J'ai utilisé ksh assez longtemps, mais dans bash , cela pourrait être géré par une manipulation interne de chaîne. Dans le premier cas, la chaîne d'origine est divisée par un terminateur interne. Dans le second cas, on suppose que la chaîne contient toujours des paires de chiffres séparées par un séparateur de un caractère.

T='12|23|11';echo -n ${T##*|};T=${T%|*};echo ${T#*|}${T%|*}
T='12|23|11';echo ${T:6}${T:3:2}${T:0:2}

Le résultat dans tous les cas est

112312
2
TrueY
echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

devrait marcher.

2
codaddict

Blague? :)

Que diriez-vous de echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'

Ceci est ma sortie:

p2> echo "12|23|11" | awk '{split($0,a,"|"); print a[3] a[2] a[1]}'
112312

donc je suppose que ça marche après tout ..

1
duedl0r