Jessaye, sans succès, dutiliser une commande awk
dans un for
boucle.
Jai une variable qui contient une série de chaînes que je veux couper avec awk
pour obtenir les données.
Je sais comment faire mais ce que je veux vraiment, cest couper les données successivement.
Jai donc cette variable:
var="data1,data2,data3"
Et ici où je suis en ce moment:
for ((i=1; i<=3; i++)) do echo $(awk -F, "{print $1}" <<< $var) done
Jessaye de remplacer le $1
par la boucle $i
mais sans succès.
Commentaires
Réponse
Vous pouvez accomplir ce que vous « êtes essayez de faire en utilisant des guillemets doubles dans le script awk pour y injecter la variable shell. Vous voulez toujours conserver un littéral $
, ce que vous pouvez faire en léchappant avec une barre oblique inverse :
echo $(awk -F, "{print \$$i}" <<<$var)
Cela étendra le $i
à 1
, 2
et 3
dans chacune des itérations, par conséquent awk verra $1
, $2
et $3
ce qui le fera étendre chacun des champs.
Une autre possibilité est dinjecter la variable shell comme un variable awk utilisant lindicateur -v
:
echo $(awk -F, -v i="$i" "{print $i}" <<<$var)
Cela assigne la variable awk i au contenu de la variable shell du même nom. Les variables dans awk nutilisent pas de $
, qui est utilisé pour les champs, donc $i
suffit pour faire référence au i -th champ si i est une variable dans awk.
Assigner une variable awk avec -v
est généralement plus sûr approche, en particulier quand il peut contenir des séquences arbitraires de caractères, dans ce cas, il y a moins de risque que le contenu soit exécuté comme du code awk contre vos intentions. Mais comme dans votre cas, la variable contient un seul entier, cest moins un problème.
Encore une autre option consiste à utiliser une boucle for
dans awk lui-même . Consultez la documentation awk (ou recherchez sur ce site) pour plus de détails sur la façon de procéder.
Commentaires
- … ou définissez lenregistrement dentrée séparateur de manière appropriée
awk -v RS='[,\n]' 1 <<< "$var"
(ou peut-être plus facilementprintf "$var" | awk -v RS=, 1
) - @steeldriver Ce script awk imprimera les trois champs ce qui est correct étant donné que lOP fait exactement cela … Jai fini par concentrer la réponse sur lextraction dun seul champ en supposant quils étaient intéressés à exécuter dautres commandes dans la boucle du shell (ce qui ont été la motivation pour en utiliser un en premier lieu …)
- Compris – que ‘ s pourquoi jai commenté le PO pour clarifier ce quils vraiment vouloir faire;)
- Le premier est une injection (et un problème de sécurité), le deuxième o ne (utiliser
-v
) nest pas une injection. - Merci, votre réponse a résolu mon problème! La première fois que je poste une question ici après des années de lecture de messages. Pas déçu. Une si grande communauté!
Réponse
Lutilisation de awk
semble excessif dans cette circonstance, que diriez-vous dun tr
et dune boucle while:
tr , "\n" <<<"$var" | while read; do echo $REPLY done
Résultat:
data1 data2 data3
Commentaires
- Excellent.
REPLY
étant largument par défautname
pourread
commande shell intégrée. - Notez que cela nécessite que le shell utilise une variable par défaut pour placer les données de
read
, ce quebash
fait , mais ce nest pas standard. De plus, votre commanderead
peut modifier les données si elle contient des barres obliques inverses et peut supprimer les espaces flanquants des valeurs. Il lit également les valeurs avec des retours à la ligne incorporés en tant que valeurs multiples. Vous avez également besoin de"$REPLY"
pour empêcher le shell de diviser la valeur et deffectuer une expansion de nom de fichier dessus.
Réponse
awk peut accepter à la fois j
(comme variable) et $j
(comme index de champ):
for i in 1 2 3; do echo "$var" | awk -v j=$i -F , "{print $j}"; done
$i
dans lexemple » confused « awk
lequel utiliser (shell ou sa propre variable – ayant priorité) car les deux sont désignés par le préfixe $
.
note
sh shell qui est standard pour les scripts « portables » ne prend pas en charge:
(( i=1; i<=3; i++; ))
et <<< $var
constructions
Vous pouvez également envisager dutiliser la commande seq
dans for
pour un contrôle plus fin de la génération de séquences de nombres, si disponible.
Commentaires
- Votre boucle ne fonctionnerait que si vous aviez trois fichiers appelés ,
2
et3
dans le répertoire courant. De plus, vous utilisez le développement de variables sans guillemets dans le shell, ce qui peut avoir des conséquences indésirables si les données contiennent des modèles de nom de fichier (comme*
). Leecho
peut en outre modifier les données si elles contiennent des barres obliques inverses.
Réponse
#!/bin/sh var="data1,data2,data3" unset data while [ "$var" != "$data" ]; do data=${var%%,*} # delete first comma and the bit after it var=${var#*,} # delete bit up to first comma (and the comma) printf "data = "%s"\n" "$data" done
Ici, nous utilisons des substitutions de variables pour obtenir chaque champ de données successif délimité par des virgules à partir de la valeur de la variable var
. La première affectation à data
dans la boucle supprimera tout de $var
après la première virgule. La variable var
est ensuite modifiée afin que le premier bit jusquà la première virgule soit supprimé.
Cela continue jusquà "$var" = "$data"
ce qui signifie que rien de plus ne peut être fait pour la chaîne.
Cette façon de faire nous permettrait de gérer des chaînes de données séparées par des virgules contenant des retours à la ligne intégrés:
var="line1 line2,data2,last bit goes here"
Avec les valeurs ci-dessus dans var
, le script ci-dessus afficherait
data = "line1 line2" data = "data2" data = "last bit goes here"
Ne se souciant pas des retours à la ligne intégrés; Vous devez très rarement faire une boucle sur les appels de awk
.
Notez que awk
est parfaitement heureux de lire votre chaîne comme un ensemble de champs délimités par des virgules, et quil est capable de boucler sur ceux-ci:
printf "%s\n" "$var" | awk -F "," "{ for (i=1; i<=NF; i++) print $i }"
Avec var="data1,data2,data3"
, cela afficherait
data1 data2 data3
Une autre solution shell qui utilise le IFS
pour diviser la valeur $var
en bits tout en utilisant également set -f
pour désactiver lexpansion du nom de fichier:
set -f oldIFS=$IFS; IFS="," set -- $var IFS=$oldIFS; unset oldIFS set +f for data do printf "data = "%s"\n" "$data" done
echo "${var//,/$'\n'}"
)