web-dev-qa-db-fra.com

Supprimer plusieurs lignes dans un fichier csv

Je travaille sur cette affectation pour supprimer des lignes d'un fichier CSV avec différents clients. J'ai compris comment supprimer un client spécifique à l'aide de ce code:

delete() {
  awk -F "\"*;\"*" '$1 != '$@' {print $ALL}' input.csv > output.csv
}

delete $@

Cependant, je dois maintenant supprimer plusieurs clients en même temps. Je peux identifier un client par son numéro de client qui est stocké dans la première colonne du fichier csv. Je suis censé créer un tableau pour les différents numéros de client et créer une boucle while pour parcourir le tableau, mais je n'arrive pas à comprendre.

1
Lotte

Je ne suis pas sûr de savoir pourquoi vous intégrez cela dans une fonction de Shell - je suppose que c'est une exigence de votre mission.

Tout d’abord, notez que l’utilisation de "*;"* comme séparateur de champs dans Awk n’est pas un moyen robuste de gérer les champs CSV entre guillemets. Elle échouera par exemple si le premier ou le dernier champ d’une ligne est entre guillemets et a gagné. t Conservez les délimiteurs entre guillemets (c'est-à-dire les champs entre guillemets qui contiennent en fait un ;) qui ne comprend pas l'intérêt de citer des champs en CSV.

Deuxièmement, vous ne devriez pas essayer de passer les variables Shell (ou les paramètres de position) dans l'expression Awk de cette façon. La méthode correcte consiste à les exporter puis à y accéder via le tableau ENVIRON, ou d'utiliser l'option de ligne de commande -v. Donc, votre implémentation "client unique" serait mieux écrite

delcust() {
  awk -F '"*;"*' -v cust="$1" '$1 != cust' input.csv > output.csv
}
delcust "$1"

Bien que vous puissiez modifier cette option pour transmettre plusieurs paramètres de position, nous suggérons de transmettre la liste de clients via une entrée standard et de l’analyser sous la forme d’un fichier de valeurs; De cette façon, vous pouvez faire une recherche canonique sur Awk basée sur un tableau associatif (ou hachage):

delcusts() {
  printf '%s\n' "$@" | awk -F'"*;"*' 'NR==FNR {custs[$0]=1; next} !($1 in custs)' - input.csv > output.csv
}
delcusts "$@"

Notez que vous n'avez pas besoin d'un print explicite dans Awk puisque print est l'action par défaut si une règle évalue une valeur autre que zéro.

2
steeldriver

Il n'y a pas vraiment besoin d'un tableau. Vous pouvez définir votre fonction comme ceci:

delete() {
  awk -v customer="^($1)\$" -F ";" '$1 !~ customer {print $ALL}' input.csv >output.csv 
}

Je ne comprenais pas comment vous avez défini le séparateur de champs, je l'ai donc modifié pour pouvoir le tester. La partie pertinente consiste à utiliser une expression régulière annulée !~. De plus, j'ai utilisé le paramètre -v pour awk qui peut vous éviter beaucoup de maux de tête de shell.

Avec cela, vous pouvez utiliser un paramètre comme celui-ci pour supprimer plusieurs clients:

delete 'bla|foo'

Pour dans input.csv comme ceci:

bla;blu;bli
foo;faa;fii
blafoo;blufaa;blifii

il céderait

blafoo;blufaa;blifii

dans output.csv.

Si vous voulez vraiment utiliser un tableau, vous pouvez en plus définir une petite fonction d'assistance qui le préparera pour une utilisation avec la fonction delete() ci-dessus:

join() { local IFS=\|; echo "$*"; }

Avec cela, vous pouvez définir un tableau bash et le convertir en syntaxe alternative regex:

$ a=(bla blu)
$ join ${a[@]}
bla|blu

Ensuite, vous pouvez appeler delete() comme ceci:

$ a=(customer1 customer2)
$ delete "$(join ${a[@]})"

(Petite remarque pour les utilisateurs de zsh: la fonction join() n'est pas nécessaire pour zsh, vous pouvez simplement utiliser le paramètre de développement suivant: ${(j:|:)a} pour joindre tous les éléments du tableau avec le caractère |.)

0
Sebastian Stark