Unix: trova e sostituisci virgole consecutive in pipeline consecutive

Jan 08 2021

Sto convertendo un CSV con virgolette doppie in un file txt delimitato da pipeline in Unix. Ho usato il seguente comando sed per sostituire "," in | quindi rimuovere le virgolette doppie iniziali e finali.

sed -e 's/","/|/g' -e 's/"//g' filenm.csv > filenm.txt

Ma il file sembra contenere virgole consecutive senza virgolette doppie e non vengono sostituite.

Col1|col2|col3|col4|col5|col6|col7|col8
Val1|val2|val3,,,,val7|val8

Ora voglio convertire tutte queste virgole consecutive in pipeline consecutive poiché indicano campi vuoti o nulli.

E anche altri campi hanno virgole all'interno dei valori dei campi che non dovrebbero essere modificati.

Ho provato a utilizzare di seguito per quello, ma non funziona.

sed -e 's/,{1,\}/|{1,\}/g' filenm.csv > filenm.txt

file CSV di esempio aperto nel blocco note:

"ID","Name","DOB","Age","Address","City","State","Country","Phone number"
"123","ABC","12/20/2020","15","No.38,3rd st, RRR NNN, TRT",,,,"9999999999"
"456","DEF","12/20/2020",,,,,"test-country","9999999999"
"465","XYZ",,,"No.38,3rd st, RRR NNN, TRT",,,,"9999999999"

Spero che questo aiuti a riprodurre il problema e risolverlo.

Grazie in anticipo....

Risposte

2 WiktorStribiżew Jan 08 2021 at 22:37

Puoi usare perl:

perl -pe 's/"([^"]*)"|,/defined($1) ? $1 : "|"/ge' filenm.csv > filenm.txt

Dettagli :

  • "([^"]*)"|,- il pattern regex che corrisponde ", quindi acquisisce nel Gruppo 1 qualsiasi zero o più caratteri diversi da "e quindi corrisponde a ", o corrisponde solo a a ,in tutti gli altri contesti
  • defined($1) ? $1 : "|"- RHS, sostituzione, che sostituisce la partita con il valore del Gruppo 1 (se il Gruppo 1 è stato abbinato) o con una |(se è ,stato abbinato)
  • ge- gsta per global(sostituisce tutte le occorrenze) e efa in modo che Perl tratti l'RHS come un'espressione Perl.

Guarda un test online :

#!/bin/bash
s='"ID","Name","DOB","Age","Address","City","State","Country","Phone number"
"123","ABC","12/20/2020","0","No.38,3rd st, RRR NNN, TRT",,,,"9999999999"'
perl -pe 's/"([^"]*)"|,/defined($1) ? $1 : "|"/ge' <<< "$s"

Produzione:

ID|Name|DOB|Age|Address|City|State|Country|Phone number
123|ABC|12/20/2020|0|No.38,3rd st, RRR NNN, TRT||||9999999999
4 potong Jan 08 2021 at 23:05

Questo potrebbe funzionare per te (GNU sed):

sed -E ':a;s/^(("[^",]*",+)*"[^",]*),/\1\n/;ta;y/,\n/|,/' file

Sostituisci in modo iterativo ,'s tra "' s con newline, quindi traduci ,'s per |' se newlines per ,'s.

1 RamanSailopal Jan 09 2021 at 00:35

Utilizzando awk:

awk -F \" '{ for(i=1;i<=NF;i++) { if ($i ~ /^[,]{2,}$/) { $i="," } } OFS="\"";gsub("\",\"","\"|\"",$0)}1' sample.csv

Spiegazione:

awk -F \" '{  # Set the field delimiter to double quote
             for(i=1;i<=NF;i++) { 
               if ($i ~ /^[,]{2,}$/) { 
                  $i="," # Loop through each field and if is contains 2 or more commas, set that field to one comma } } OFS="\""; gsub("\",\"","\"|\"",$0) # Substitute "," for "|"
           }1' sample.csv
1 Daweo Jan 09 2021 at 01:50

Userei GNU AWKper questo modo. Lascia che il file.txtcontenuto sia

"ID","Name","DOB","Age","Address","City","State","Country","Phone number"
"123","ABC","12/20/2020","15","No.38,3rd st, RRR NNN, TRT",,,,"9999999999"
"456","DEF","12/20/2020",,,,,"test-country","9999999999"
"465","XYZ",,,"No.38,3rd st, RRR NNN, TRT",,,,"9999999999"

poi

awk 'BEGIN{FS="\"";OFS=""}{for(i=1;i<=NF;i+=2){$i=gensub(/,/,"|","g",$i)};print $0}' file.txt

produzione

ID|Name|DOB|Age|Address|City|State|Country|Phone number
123|ABC|12/20/2020|15|No.38,3rd st, RRR NNN, TRT||||9999999999
456|DEF|12/20/2020|||||test-country|9999999999
465|XYZ|||No.38,3rd st, RRR NNN, TRT||||9999999999

Presumo che la prima e l'ultima colonna non siano mai vuote. Uso "come separatore di campo e quindi in ogni campo dispari (questi contengono esclusivamente ,) cambio tutto ,in |. Finalmente stampo tutta la linea così alterata.

(testato in GNU Awk 5.0.1)