Unix: encontre e substitua vírgulas consecutivas em pipelines consecutivos

Jan 08 2021

Estou convertendo um CSV entre aspas para um arquivo txt delimitado por pipeline no Unix. Usei o seguinte comando sed para substituir "," em | em seguida, remova aspas duplas iniciais e finais.

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

Mas o arquivo parece ter vírgulas consecutivas sem aspas duplas e não estão sendo substituídas.

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

Agora, quero converter todas essas vírgulas consecutivas em pipelines consecutivos, pois indicam campos vazios ou nulos.

E outros campos também têm vírgulas dentro dos valores dos campos que não devem ser alterados.

Tentei usar abaixo para isso, mas não funcionou.

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

exemplo de arquivo csv aberto no bloco de notas:

"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"

Espero que isso ajude a reproduzir o problema e resolvê-lo.

Desde já, obrigado....

Respostas

2 WiktorStribiżew Jan 08 2021 at 22:37

Você pode usar perl:

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

Detalhes :

  • "([^"]*)"|,- o padrão regex que corresponde e ", em seguida, captura no Grupo 1 qualquer zero ou mais caracteres diferentes de "e, em seguida, corresponde a um ", ou apenas corresponde a ,em todos os outros contextos
  • defined($1) ? $1 : "|"- RHS, substituição, que substitui a correspondência pelo valor do Grupo 1 (se o Grupo 1 foi correspondido) ou por um |(se o ,foi correspondido)
  • ge- gsignifica global(substitui todas as ocorrências) e efaz o Perl tratar o RHS como uma expressão Perl.

Veja um teste 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"

Resultado:

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

Isso pode funcionar para você (GNU sed):

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

Substitua iterativamente ,'s entre "' s por novas linhas e, em seguida, traduza ,'s por |' se as novas linhas por ,'s.

1 RamanSailopal Jan 09 2021 at 00:35

Usando o awk:

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

Explicação:

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

Eu usaria o GNU AWKda seguinte maneira. Deixe o file.txtconteúdo ser

"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"

então

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

resultado

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

Presumi que a primeira e a última coluna nunca estão vazias. Eu uso "como separador de campo e, em seguida, em todos os campos ímpares (eles contêm apenas ,), altero todos ,para |. Finalmente imprimo toda essa linha alterada.

(testado em GNU Awk 5.0.1)