2
votes

Ignorer la virgule après la barre oblique inverse dans une ligne d'un fichier texte en utilisant awk ou sed

J'ai un fichier texte contenant plusieurs lignes du format suivant:

science, social
tennis, ping_pong, chess

J'ai besoin d'analyser le fichier texte et d'imprimer la sortie des champs en ignorant les virgules. Ici, ce seront les champs 2 ou 3 comme ceci:

name,list_of_subjects,list_of_sports,school
Eg1: john,science\,social,football,florence_school
Eg2: james,painting,tennis\,ping_pong\,chess,highmount_school

Je ne sais pas comment ignorer les caractères échappés. Comment puis-je le faire avec awk ou sed dans le terminal?


0 commentaires

7 Réponses :


2
votes

Vous pouvez remplacer les séquences \, par un autre caractère qui n'apparaîtra pas dans votre texte, diviser le texte autour des virgules restantes puis remplacer le caractère choisi par des virgules:

sed $'s/\\\,/\31/g' input | awk -F, '{ printf "Name: %s\nSubjects : %s\nSports: %s\nSchool: %s\n\n", $1, $2, $3, $4 }' | tr $'\31' ','


0 commentaires

3
votes

Remplacez \, par un caractère que vos enregistrements ne contiennent pas normalement (par exemple \ n ) et restaurez-le avant l'impression. Par exemple:

$ awk 'BEGIN{ FS=OFS="," } NR>1{ if(gsub(/\\,/,"\n")) for(i=1;i<=NF;++i) gsub(/\n/,"\\,",$i); print $2,$3 }' file
science\,social,football
painting,tennis\,ping_pong\,chess

Puisque le premier gsub est exécuté sur l'ensemble de l'enregistrement (c'est-à-dire $ 0 ), awk est obligé de recalculer les champs. Mais le second n'est effectué que sur le deuxième champ (c'est-à-dire $ 2 ), donc il n'affectera pas les autres champs. Voir: Modification des champs .

Pour pouvoir extraire plusieurs champs avec des virgules correctement échappées, vous devez insérer des gsub \ n s dans tous les champs avec une boucle for comme dans l'exemple suivant:

$ awk -F',' 'NR>1{ if(gsub(/\\,/,"\n")) gsub(/\n/,",",$2); print $2 }' file
science,social
painting


8 commentaires

Comment ne pas imprimer de lignes vides si la liste est vide dans ces colonnes? À l'heure actuelle, cette solution imprime une ligne vide si la liste est vide.


Input Line1: john, science \, social, football, florence_school Line2: james, painting, tennis \, ping_pong \, chess, highmount_school Line3: robert , snooker, ridgemont Line4: jim, géographie , Oakmont


La deuxième ligne de sortie (troisième ligne d'entrée) n'est-elle pas supposée être tennis, ping_pong, échecs ?


@potong ce n'est pas un exemple de sortie. Comme vous pouvez le voir, le 2e champ de la 2e ligne et le 3e champ de la 3e ligne contiennent des virgules échappées et OP dit qu'ils doivent être analysés comme ça


@oguzismail votre logique de code peut échouer si deux contre-obliques précèdent une virgule, si l'op veut échapper à la contre-oblique.


@jxc pourquoi OP voudrait-il échapper au caractère d'échappement?


@oguzismail Il n'est pas rare dans le traitement de données réel d'échapper au caractère d'échappement. Probablement pas un souci d'opération, mais mieux vaut connaître les problèmes potentiels.


@jxc Ok, si c'est un souci pour OP et qu'il me le fait savoir je vais mettre à jour ma réponse.



0
votes

Pourquoi awk et sed quand bash avec coreutils est juste suffisant:

list_of_subjects : science social
list_of_sports   : football
list_of_subjects : painting
list_of_sports   : tennis ping_pong chess

affichera:

# Sorry my cat. Using `cat` as input pipe
cat <<EOF |
name,list_of_subjects,list_of_sports,school
Eg1: john,science\,social,football,florence_school
Eg2: james,painting,tennis\,ping_pong\,chess,highmount_school
EOF
# remove first line!
tail -n+2 |
# substitute `\,` by an unreadable character:
sed 's/\\\,/\xff/g' |
# read the comma separated list
while IFS=, read -r name list_of_subjects list_of_sports school; do
     # read the \xff separated list into an array
     IFS=$'\xff' read -r -d '' -a list_of_subjects < <(printf "%s" "$list_of_subjects")
     # read the \xff separated list into an array
     IFS=$'\xff' read -r -d '' -a list_of_sports < <(printf "%s" "$list_of_sports")

     echo "list_of_subjects : ${list_of_subjects[@]}"
     echo "list_of_sports   : ${list_of_sports[@]}"
done

Notez que ce sera probablement plus lent que la solution en utilisant awk.

Notez que le principe de fonctionnement est le même que dans les autres réponses - remplacez \, chaîne par un autre caractère unique, puis utilisez ce caractère pour parcourir les deuxième et troisième éléments de champ.


1 commentaires

wrt Pourquoi awk et sed quand bash avec coreutils est juste assez - parce que le faire avec une boucle bash prendrait plus de code, serait plus compliqué, plus difficile à écrire de manière robuste, serait moins portable et beaucoup plus lent que le faire avec awk. Les gars qui ont inventé shell ont également inventé awk pour shell à appeler pour manipuler du texte - ils avaient leurs raisons ...



0
votes

Vous pouvez peut-être joindre des colonnes avec une fonction.

{
    for (col=1; col<=NF; col++) {
        if ($col ~ /\\$/) {
            joincol(col)
        }
    }
}

Cela peut être utilisé ainsi:

function joincol(col,    i) {
    $col=$col FS $(col+1)
    for (i=col+1; i<NF; i++) {
        $i=$(i+1)
    }
    NF--
}

Notez que la décrémentation de NF n'est pas définie comportement dans POSIX. Il peut supprimer le dernier champ, ou il peut ne pas l'être, et toujours conforme POSIX. Cela fonctionne pour moi dans BSDawk et Gawk. YMMV. Peut contenir des noix.


0 commentaires

0
votes

Utilisez le FPAT de gawk :

awk -v FPAT='(\\\\.|[^,\\\\]*)+' '{print gensub("\\\\", "", "g", $3)}' file
#list_of_sports
#football
#tennis,ping_pong,chess

puis utilisez gnusub pour remplacer les contre-obliques:

awk -v FPAT='(\\\\.|[^,\\\\]*)+' '{print $3}' file
#list_of_sports
#football
#tennis\,ping_pong\,chess


1 commentaires

Attention : FPAT et gensub sont des fonctionnalités spécifiques à Gawk.



0
votes

Utilisation de Perl. Remplacez le \, par un caractère de contrôle, dites \ x01 , puis remplacez-le à nouveau par ,

$ cat laxman.txt
john,science\,social,football,florence_school
james,painting,tennis\,ping_pong\,chess,highmount_school
$ perl -ne ' s/\\,/\x01/g and print ' laxman.txt  | perl -F, -lane ' for(@F) { if( /\x01/ ) { s/\x01/,/g ; print } } '
science,social
tennis,ping_pong,chess

p >


0 commentaires

0
votes

Cela pourrait fonctionner pour vous (GNU sed):

sed -E 's/\\,/\n/g;y/,\n/\n,/;s/^[^,]*$//Mg;s/\n//g;/^$/d' file

Remplacez les virgules entre guillemets par des retours à la ligne, puis rétablissez les nouvelles lignes en virgules et les virgules en nouvelles lignes. Supprimez toutes les lignes qui ne contiennent pas de virgule. Supprimer les lignes vides.


0 commentaires