Unix: trova e sostituisci virgole consecutive in pipeline consecutive
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
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 contestidefined($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
-g
sta perglobal
(sostituisce tutte le occorrenze) ee
fa 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
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.
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
Userei GNU AWK
per questo modo. Lascia che il file.txt
contenuto 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)