awk search e replace string em uma coluna específica do arquivo CSV

3

Eu tenho um arquivo csv com 17 colunas e milhões de linhas. Eu quero procurar por uma seqüência específica na coluna 16 e substituir todas as instâncias dessa seqüência com outra seqüência de caracteres. Como meu resto do programa usa o script bash, pensei em usar o awk em vez do Python search & substituir. Meu sistema operacional atual é Rhel6.

A seguir, a saída de amostra dos meus dados:

SUBSCRIBER_ID|ACCOUNT_CATEGORY|ACCOUNT_ACTIVATION_DATE|PACKAGE_NAME|PACKAGE_TYPE|DURATION|ACTIVE_DATE|INACTIVE_DATE|STB_NO|PRIMARY_SECONDARY|MODEL_TYPE|VC_NO|MULTIROOM|STB_TYPE|IPKG|SERVICE_STATE|CURRENT_STATUS
1001098068|ResidentialRegular|01/20/2007|Annual package 199 May17 pack|Basic Package|Annual|08/28/2017||027445053518|Primary|Pace - 31|000223871682|Yes|AMP|Package 199 pack|Market1|Active
1001098068|ResidentialRegular|01/20/2007|Annual Pack|Premium Package|Annual|08/28/2017||027445053518|Primary|Pace - 31|000223871682|Yes|AMP|English Movies pack|Market1|Active
1001098068|ResidentialRegular|01/20/2007|Annual SingleUnit Jun17 Pack|Secondary Pack|Annual|08/28/2017||032089364015|Secondary|Kaon|000017213968|Yes|AMP|SingleUnit|Market2|Active

Nesta coluna 16 é Market, onde eu quero mudar o Market1 para MarketPrime . O nome do arquivo é marketinfo_2018-06-26.csv

Eu tentei o seguinte código:

awk -F '| +' '{gsub("Market1","MarketPrime",$16); print}' OFS="|" marketinfo_2018-06-26.csv > marketinfo_2018-06-26.csv

Isso é executado sem nenhuma saída, mas a string Market1 ainda permanece.

    
por Apricot 27.06.2018 / 08:21

3 respostas

8
awk -F '|' -v OFS='|' '$16 == "Market1" { $16 = "MarketPrime" }1' file.csv >new-file.csv

O único problema real em seu código é que você defina o separador de arquivos de entrada para não apenas | , mas também para espaços. Isso fará com que os espaços sejam contados como separadores de campo nos dados e será incrivelmente difícil descobrir qual é o número do campo correto (já que alguns campos contêm um número variável de espaços).

Você também não pode redirecionar para o mesmo nome de arquivo usado para ler. Isso faria com que o shell truncasse primeiro (vazio) o arquivo de saída, e seu programa awk não teria dados para ler.

Seu código faz uma substituição de expressão regular. Está tudo bem, mas você precisa estar ciente de que se o 16º campo for algo como Market12 ou TheMarket1 , ele acionaria a substituição devido aos pontos de ancoragem ausentes. Seria mais seguro usar ^Market1$ como a expressão a ser substituída ou usar uma comparação de string.

O comando awk acima usa apenas | como um separador de campo e faz uma comparação com o 16º campo. Se esse campo for Market1 , ele será definido como MarketPrime .

O trailing 1 no final do código awk faz com que todos os registros (modificados ou não) sejam impressos.

    
por 27.06.2018 / 08:29
2

O problema é com o separador de campo de entrada.

Como você deseja usar vários separadores de campo (o que não é obrigatório), o número de campos em cada linha é diferente, conforme mostrado abaixo.

$ awk -F '[| +]' '{print NF}' test.csv
17
26
23
21

se você usar apenas | como IFS, seu código funcionará. Como cada linha tem 17 campos, conforme mostrado abaixo.

awk -F "|" '{print NF}' test.csv
17
17
17
17 

Solução 1 : com vários IFS.

awk -F '[| +]' '{gsub("Market1","MarketPrime",$(NF-1)); print}' OFS="|" test.csv

SUBSCRIBER_ID|ACCOUNT_CATEGORY|ACCOUNT_ACTIVATION_DATE|PACKAGE_NAME|PACKAGE_TYPE|DURATION|ACTIVE_DATE|INACTIVE_DATE|STB_NO|PRIMARY_SECONDARY|MODEL_TYPE|VC_NO|MULTIROOM|STB_TYPE|IPKG|SERVICE_STATE|CURRENT_STATUS
1001098068|ResidentialRegular|01/20/2007|Annual|package|199|May17|pack|Basic|Package|Annual|08/28/2017||027445053518|Primary|Pace|-|31|000223871682|Yes|AMP|Package|199|pack|MarketPrime|Active
1001098068|ResidentialRegular|01/20/2007|Annual|Pack|Premium|Package|Annual|08/28/2017||027445053518|Primary|Pace|-|31|000223871682|Yes|AMP|English|Movies|pack|MarketPrime|Active
1001098068|ResidentialRegular|01/20/2007|Annual SingleUnit Jun17 Pack|Secondary Pack|Annual|08/28/2017||032089364015|Secondary|Kaon|000017213968|Yes|AMP|SingleUnit|Market2|Active

Solução 2 : com campo fixo 16

awk -F '|' '{gsub("Market1","MarketPrime",$16); print}' OFS="|" test.csv

SUBSCRIBER_ID|ACCOUNT_CATEGORY|ACCOUNT_ACTIVATION_DATE|PACKAGE_NAME|PACKAGE_TYPE|DURATION|ACTIVE_DATE|INACTIVE_DATE|STB_NO|PRIMARY_SECONDARY|MODEL_TYPE|VC_NO|MULTIROOM|STB_TYPE|IPKG|SERVICE_STATE|CURRENT_STATUS
1001098068|ResidentialRegular|01/20/2007|Annual package 199 May17 pack|Basic Package|Annual|08/28/2017||027445053518|Primary|Pace - 31|000223871682|Yes|AMP|Package 199 pack|MarketPrime|Active
1001098068|ResidentialRegular|01/20/2007|Annual Pack|Premium Package|Annual|08/28/2017||027445053518|Primary|Pace - 31|000223871682|Yes|AMP|English Movies pack|MarketPrime|Active
1001098068|ResidentialRegular|01/20/2007|Annual SingleUnit Jun17 Pack|Secondary Pack|Annual|08/28/2017||032089364015|Secondary|Kaon|000017213968|Yes|AMP|SingleUnit|Market2|Active
    
por 27.06.2018 / 08:29
0

Por uma questão de clareza para outras pessoas que possam enfrentar problemas semelhantes:

Ambas as respostas funcionaram para este cenário:

Resposta de Kusalananda:

awk -F '|' -v OFS='|' '$16 == "Market1" { $16 = "MarketPrime" }1' file.csv >new-file.csv

Minha resposta revisada é baseada na resposta de Kusalananda:

awk -F '|' '{gsub("Market1","MarketPrime",$16); print}' OFS="|" marketinfo_2018-06-26.csv > marketinfo_2018-06-26.csv
    
por 27.06.2018 / 08:43