Comment puis-je effectuer les opérations suivantes sur un fichier CSV à l'aide de sed
ou awk
?
- Supprimer une colonne
- Dupliquer une colonne
- Déplacer une colonne
J'ai une grande table avec plus de 200 lignes, et je ne connais pas très bien sed
.
Comment puis-je effectuer les opérations suivantes sur un fichier CSV à l'aide de sed
ou awk
?
J'ai une grande table avec plus de 200 lignes, et je ne connais pas très bien sed
.
Réponses:
Outre la façon de couper et de réorganiser les champs (traités dans les autres réponses), il y a le problème des champs CSV originaux.
Si vos données entrent dans cette catégorie "décalée", un peu de pré et post filtrage peut s'en occuper. Les filtres ci - dessous ont besoin les caractères \x01
, \x02
, \x03
, \x04
apparaissent nulle part dans vos données.
Voici les filtres enroulés autour d'un simple awk
vidage de champ.
Remarque: le champ-cinq a une disposition de "champ entre guillemets" invalide / incomplète, mais elle est bénigne à la fin d'une ligne (en fonction de l'analyseur CSV). Mais, bien entendu, cela entraînerait des résultats inattendus problématiques s'il était échangé de sa position de fin de ligne actuelle .
Mise à jour; user121196 a signalé un bogue lorsqu'une virgule précède une citation finale . Voici le correctif.
Les données
cat <<'EOF' >file
field one,"fie,ld,two",field"three","field,\",four","field,five
"15111 N. Hayden Rd., Ste 160,",""
EOF
Le code
sed -r 's/^/,/; s/\\"/\x01/g; s/,"([^"]*)"/,\x02\1\x03/g; s/,"/,\x02/; :MC; s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g; tMC; s/^,// ' file |
awk -F, '{ for(i=1; i<=NF; i++) printf "%s\n", $i; print NL}' |
sed -r 's/\x01/\\"/g; s/(\x02|\x03)/"/g; s/\x04/,/g'
Le résultat:
field one
"fie,ld,two"
field"three"
"field,\",four"
"field,five
"15111 N. Hayden Rd., Ste 160,"
""
Voici le pré filtre , développé avec des commentaires.
Le post-filtre n'est qu'un renversement de \x01
. \x02
, \x03
,\x04
sed -r '
s/^/,/ # add a leading comma delimiter
s/\\"/\x01/g # obfuscate escaped quotation-mark (\")
s/,"([^"]*)"/,\x02\1\x03/g # obfuscate quotation-marks
s/,"/,\x02/ # when no trailing quote on last field
:MC # obfuscate commas embedded in quotes
s/\x02([^\x03]*),([^\x03]*)/\x02\1\x04\2/g
tMC
s/^,// # remove spurious leading delimiter
'
Cela dépend si votre fichier CSV utilise des virgules uniquement pour les délimiteurs, ou si vous avez une folie comme:
champ un, "champ, deux", champ trois
Cela suppose que vous utilisez un simple fichier CSV:
Vous pouvez vous débarrasser d'une seule colonne de plusieurs façons; J'ai utilisé la colonne 2 comme exemple. La façon la plus simple est probablement d'utiliser cut
, qui vous permet de spécifier un délimiteur -d
et les champs que vous souhaitez imprimer -f
; cela lui dit de se séparer par des virgules et le champ de sortie 1 et les champs 3 jusqu'à la fin:
$ cut -d, -f1,3- /path/to/your/file
Si vous avez réellement besoin d'utiliser sed
, vous pouvez écrire une expression régulière qui correspond aux premiers n-1
champs, au n
champ th et au reste, et ignorer la sortie du n
th (ici n
2, donc le premier groupe correspond à l' 1
heure :) \{1\}
:
$ sed 's/\(\([^,]\+,\)\{1\}\)[^,]\+,\(.*\)/\1\3/' /path/to/your/file
Il existe plusieurs façons de le faire awk
, aucune d'entre elles n'est particulièrement élégante. Vous pouvez utiliser une for
boucle, mais gérer la virgule de fin est une douleur; en ignorant que ce serait quelque chose comme:
$ awk -F, '{for(i=1; i<=NF; i++) if(i != 2) printf "%s,", $i; print NL}' /path/to/your/file
Je trouve plus facile de sortir le champ 1, puis substr
de tout retirer après le champ 2:
$ awk -F, '{print $1 "," substr($0, length($1)+length($2)+3)}' /path/to/your/file
C'est ennuyeux pour les colonnes plus loin
Dans sed
c'est essentiellement la même expression que précédemment, mais vous capturez également la colonne cible et incluez ce groupe plusieurs fois dans le remplacement:
$ sed 's/\(\([^,]\+,\)\{1\}\)\([^,]\+,\)\(.*\)/\1\3\3\4/' /path/to/your/file
Dans awk
le cas de la boucle for, ce serait quelque chose comme (en ignorant à nouveau la virgule de fin):
$ awk -F, '{
for(i=1; i<=NF; i++) {
if(i == 2) printf "%s,", $i;
printf "%s,", $i
}
print NL
}' /path/to/your/file
Le substr
chemin:
$ awk -F, '{print $1 "," $2 "," substr($0, length($1)+2)}' /path/to/your/file
(tcdyl a trouvé une meilleure méthode dans sa réponse )
Je pense que la sed
solution découle naturellement des autres, mais elle commence à devenir ridiculement longue
awk
est votre meilleur pari. awk
imprime les champs par numéro, donc ...
awk 'BEGIN { FS=","; OFS=","; } {print $1,$2,$3}' file
Pour supprimer une colonne, pas l'imprimer:
awk 'BEGIN { FS=","; OFS=","; } {print $1,$3}' file
Pour modifier la commande:
awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file
Redirigez vers un fichier de sortie.
awk 'BEGIN { FS=","; OFS=","; } {print $3,$1,$2}' file > output.file
awk
peut également formater la sortie.
Étant donné un fichier délimité par des espaces au format suivant:
1 2 3 4 5
Vous pouvez supprimer le champ 2 avec awk comme ceci:
awk '{ sub($2,""); print}' file
qui revient
1 3 4 5
Remplacez la colonne 2 par la colonne n, le cas échéant.
Pour dupliquer la colonne 2,
awk '{ col = $2 " " $2; $2 = col; print }' file
qui revient
1 2 2 3 4 5
Pour commuter les colonnes 2 et 3,
awk '{temp = $2; $2 = $3; $3 = temp; print}'
qui revient
1 3 2 4 5
awk est généralement très bon pour gérer le concept de champs . Si vous avez affaire à un fichier CSV et non à un fichier délimité par des espaces, vous pouvez simplement utiliser
awk -F,
pour définir votre champ comme une virgule, au lieu d'un espace (qui est la valeur par défaut). Il existe un certain nombre de bonnes ressources awk en ligne, dont une que je liste comme source ci-dessous.
Source pour # 3
awk
, mais il semble produire un espace séparé, même si le séparateur de champ est ,
(le séparateur de champ contrôle simplement la façon dont il gère les entrées)
Cela fonctionnera pour la suppression
awk '{$2="";$0=$0;$1=$1}1'
Contribution
a b c d
Sortie
a c d