Awk considère la chaîne entre guillemets comme un seul jeton et ignore l'espace entre les deux
Fichier de Données - données.txt:
ABC "I am ABC" 35 DESC
DEF "I am not ABC" 42 DESC
cat data.txt | awk '{print $2}'
Entraînera le " I " au lieu de la chaîne entre guillemets
Comment faire awk pour qu'il ignore l'espace dans la citation et pense que c'est un seul jeton?
7 réponses
Oui, cela peut être fait bien dans awk. Il est facile d'obtenir tous les champs sans aucun hacks sérieux.
(Cet exemple fonctionne à la fois dans le seul vrai Awk et dans gawk.)
{
split($0, a, "\"")
$2 = a[2]
$3 = $(NF - 1)
$4 = $NF
print "and the fields are ", $1, "+", $2, "+", $3, "+", $4
}
Essayez ceci:
$ cat data.txt | awk -F\" '{print $2}'
I am ABC
I am not ABC
Une Autre alternative serait d'utiliser la FPAT
variable, qui définit une expression régulière décrivant le contenu de chaque champ.
Enregistrez ce script AWK sous parse.awk
:
#!/bin/awk -f
BEGIN {
FPAT = "([^ ]+)|(\"[^\"]+\")"
}
{
print $2
}
Le Rendre exécutable avec chmod +x ./parse.awk
et analyser votre fichier de données comme ./parse.awk data.txt
:
"I am ABC"
"I am not ABC"
J'ai rassemblé une fonction qui re-divise $ 0 dans un tableau appelé B. Les espaces entre guillemets doubles n'agissent pas comme des séparateurs de champs. Fonctionne avec n'importe quel nombre de champs, un mélange d'cotées et non cotées celles. Voici:
#!/usr/bin/gawk -f
# Resplit $0 into array B. Spaces between double quotes are not separators.
# Single quotes not handled. No escaping of double quotes.
function resplit( a, l, i, j, b, k, BNF) # all are local variables
{
l=split($0, a, "\"")
BNF=0
delete B
for (i=1;i<=l;++i)
{
if (i % 2)
{
k=split(a[i], b)
for (j=1;j<=k;++j)
B[++BNF] = b[j]
}
else
{
B[++BNF] = "\""a[i]"\""
}
}
}
{
resplit()
for (i=1;i<=length(B);++i)
print i ": " B[i]
}
J'espère que ça aide.
La première réponse à cette question ne fonctionne que pour les lignes avec un seul champ entre guillemets. Quand j'ai trouvé cette question, j'avais besoin de quelque chose qui pourrait fonctionner pour un nombre arbitraire de champs entre guillemets.
Finalement, je suis tombé sur une réponse de Wintermute dans un autre fil , et il a fourni une bonne solution généralisée à ce problème. Je viens de le modifier pour supprimer les guillemets. Notez que vous devez appeler awk avec -F\"
lors de l'exécution du programme ci-dessous.
BEGIN { OFS = "" } {
for (i = 1; i <= NF; i += 2) {
gsub(/[ \t]+/, ",", $i)
}
print
}
Cela fonctionne par en observant que tous les autres éléments du tableau seront à l'intérieur des guillemets lorsque vous vous séparez par le caractère" -, et il remplace l'espace en divisant ceux qui ne sont pas entre guillemets par une virgule.
Vous pouvez ensuite facilement enchaîner une autre instance d'awk pour faire tout le traitement dont vous avez besoin (utilisez simplement le Commutateur Séparateur de champs, -F,
).
Notez que cela pourrait casser si le premier champ est cité-Je ne l'ai pas testé. Si c'est le cas, cependant, il devrait être facile à corriger en ajoutant un if instruction pour démarrer à 2 plutôt que 1 si le premier caractère de la ligne est un ".
OK, si vous voulez vraiment les trois champs, vous pouvez les obtenir, mais cela prend beaucoup de tuyauterie:
$ cat data.txt | awk -F\" '{print $1 "," $2 "," $3}' | awk -F' ,' '{print $1 "," $2}' | awk -F', ' '{print $1 "," $2}' | awk -F, '{print $1 "," $2 "," $3}'
ABC,I am ABC,35
DEF,I am not ABC,42
Par le dernier tuyau, vous avez les trois champs pour faire ce que vous voulez avec.
Voici quelque chose comme ce que j'ai finalement obtenu de travailler qui est plus générique pour mon projet. Notez qu'il n'utilise pas awk.
someText="ABC \"I am ABC\" 35 DESC '1 23' testing 456"
putItemsInLines() {
local items=""
local firstItem="true"
while test $# -gt 0; do
if [ "$firstItem" == "true" ]; then
items="$1"
firstItem="false"
else
items="$items
$1"
fi
shift
done
echo "$items"
}
count=0
while read -r valueLine; do
echo "$count: $valueLine"
count=$(( $count + 1 ))
done <<< "$(eval putItemsInLines $someText)"
Quelles sorties:
0: ABC
1: I am ABC
2: 35
3: DESC
4: 1 23
5: testing
6: 456