A série de comandos sed funciona na linha de comando, mas não em um script

10

Estou trabalhando com a saída .csv de esta consulta de dados SE que se parece com isso (somente com 5022 entradas):

"{
  ""id"": 281952,
  ""title"": ""Flash 11.2 No Longer Supported by Google Play""
}"
"{
  ""id"": 281993,
  ""title"": ""Netbeans won't open in Ubuntu""
}"

(E tem linha ^M termina entre [número] e "" título ""). Eu preciso que pareça assim:

281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu

Eu consertei isso em um certo editor de texto que deve permanecer sem nome facilmente, mas eu queria fazer um script para que eu não precise fazer isso novamente toda vez que a consulta for atualizada & para que outros possam usá-lo. Eu usei sed ...

Esta série de comandos funciona perfeitamente (embora possa ser ineficiente; é apenas uma solução de tentativa e erro):

# Print the ^M and remove them, write to a new file:
cat -v QueryR* | sed 's/\^M//' > QueryNew
# remove all the other junk:
sed -i 's/{//' QueryNew
sed -i 's/}//' QueryNew
sed -i 's/""//g' QueryNew
sed -i 's/^"//' QueryNew
sed -i '/,/{N;/\n.*title:\s/{s/,\n.*title:\s/,\ /}}' QueryNew
sed -i 's/^\s\+//' QueryNew
sed -i '/^\s*$/d' QueryNew
sed -i 's/^id:\ //' QueryNew
sed -i 's/,\ /,/' QueryNew
sed -i 's/\//g' QueryNew

Então, por que isso não acontece? Somente o ^M e o {} são removidos e todo o restante ainda está lá.

#!/bin/bash
cat -v QueryR* | sed 's/\^M//' > QueryNew
sed -i '{
       s/{//
       s/}//
       s/""//g
       s/^"//
       /,/{N;/\n.*title:\s/{s/,\n.*title:\s/,\ /}}
       s/^\s\+//
       /^\s*$/d
       s/^id:\ //
       s/,\ /,/
       s/\//g
}' QueryNew

Tenho certeza de que meu erro é realmente óbvio ...

    
por Zanna 18.09.2016 / 00:37

6 respostas

11

Usar cat -v para transformar caracteres CR em sequências ^M literais parece fundamentalmente feio para mim - se você precisar remover as terminações de linha do DOS, use dos2unix , tr ou sed 's/\r$// '

Se você insistir em usar sed, então sugiro que você imprima os bits que você faz deseja, em vez de tentar excluir todos os bits aleatórios que você não deseja - por exemplo

$ sed -rn -e 's/\"//g' -e 's/(.*): (.*)\r//p' QueryR | paste -d '' - -
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu

Você pode adotar a opção de extração de valores-chave combinando zero ou mais aspas em cada extremidade da sequência de valores

$ sed -rn 's/(.*): \"*([^"]*)\"*\r//p' QueryR | paste -d '' - -
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu

Você pode ter realmente fantasia e emular o paste em sed unindo pares de linhas na ,\r$ final e, em seguida, os pares de chave-valor coincidem ( g ) e não avidamente

$ sed -rn '/,\r$/ {N; s/([^:]*): \"*([^:"]*)\"*\r\n?//gp}' QueryR
281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu

(Pessoalmente, eu favoreceria a abordagem do KISS e usaria a primeira).

FWIW, como sua entrada parece estar com o JSON com as últimas citações, sugiro instalar um analisador JSON adequado, como jq

sudo apt-get install jq

Você pode fazer algo como

$ sed -e 's/["]["]/"/g' -e 's/"{/{/' -e 's/}"/}/' QueryR | jq '.id, .title' | paste -d, - -
281952,"Flash 11.2 No Longer Supported by Google Play"
281993,"Netbeans won't open in Ubuntu"

que remove as aspas supérfluas e depois usa jq para extrair os campos de interesse - note que jq parece lidar com as terminações de linha no estilo DOS, portanto não há necessidade de tomar medidas especiais para removê-las. / p>

Altere para jq '.[]' para despejar todos os pares de valores de atributo.

Crédito para inspiração e sintaxe jq básica tirada de Superando novas linhas com grep -o

    
por steeldriver 18.09.2016 / 01:28
5

Eu consertei graças ao steeldriver & mais mexer. Não refinado, mas funciona.

sed  '{
       s/"{//
       s/}"//
       s/^"//
       /,\r/{N;/\n.*title.*:\s/{s/,\r\n.*title.*:\s/,/}}
       s/""//g
       s/^\s\+//
       /^\s*$/d
       s/^id:\ //
       s/\//g
}' QueryR* | tee "$1"

tradução:
s/"{// Remover "{
s/}"// Remover }"
s/^"// Remover " do início da linha% /,\r/{N;/\n.*title.*:\s/{s/,\r\n.*title.*:\s/,\ /}} match ,\r on uma linha e [whatever]title[whatever]: na próxima linha, substitua tudo isso por ,
s/""//g Remova todas as aspas duplas restantes% s/^\s\+// Remova o espaço em branco do início das linhas
/^\s*$/d Remova linhas vazias
s/^id:\ // Remover id: e espaço após ele s/\//g Remover barras invertidas (caracteres de escape para "adicionados a alguns campos de título)
tee "$1" especificar um arquivo de saída ao executar o script, por exemplo ./queryclean newquery.csv

    
por Zanna 18.09.2016 / 09:47
4

Enquanto a pergunta pede sed , pode-se contornar os problemas do sed com o Python:

from __future__ import print_function
import sys

with open(sys.argv[1]) as f:
     for line in f:
         if '""id""' in line:
            print(line.strip().split(':')[1],end="")
         if '""title""' in line:
            title = " ".join(line.strip().split(':')[1:])
            print(title.replace('""'," "))

Este código é compatível com o python2 e o python3, portanto, ambos funcionarão

Execução da amostra:

bash-4.3$ cat questions.txt 
"{
  ""id"": 281952,
  ""title"": ""Flash 11.2 No Longer Supported by Google Play""
}"
"{
  ""id"": 281993,
  ""title"": ""Netbeans won't open in Ubuntu""
}"
bash-4.3$ python3 parse_questions.py questions.txt 
 281952,  Flash 11.2 No Longer Supported by Google Play 
 281993,  Netbeans won't open in Ubuntu 
    
por Sergiy Kolodyazhnyy 18.09.2016 / 13:43
4

Mais três abordagens:

  1. awk

    $ awk -F'": ' '/\"id\"/{id=$NF;} 
                  /\"title\"/{
                    t=$NF; 
                    sub(/^""/,"",t); 
                    sub(/""$/,"",t); 
                    print id,t
                  }' OFS="" file 
    281952,Flash 11.2 No Longer Supported by Google Play
    281993,Netbeans won't open in Ubuntu
    
  2. Perl

    $ perl -lne '$id=$1 if /id"":\s*(\d+)/; 
                 if(/title"":\s*""(.*)""/){print "$id,$1"}' file 
    281952,Flash 11.2 No Longer Supported by Google Play
    281993,Netbeans won't open in Ubuntu
    
  3. GNU grep com regexes compatíveis com perl e perl simples:

    $ grep -oP '(id"":\s*\K.*)|(title"":\s*""\K.*(?=""))' file | 
        perl -pe 'chomp if $.%2'
    281952,Flash 11.2 No Longer Supported by Google Play
    281993,Netbeans won't open in Ubuntu
    
por terdon 18.09.2016 / 13:46
4

Isso não corresponde exatamente à sua pergunta ou à solução do problema, mas para eliminar os caracteres indesejados, você pode usar tr :

cat QueryR | tr -d '}{:"' 

e você terá:

    
por kcdtv 18.09.2016 / 01:18
1

Este é outro script escrito em Ruby. Ele reterá as vírgulas no título, que podem ser facilmente importadas para qualquer programa de planilhas sem quebrar as colunas.

csvfile = File.open('query-fixed.csv', 'w')

File.open('QueryResults2.csv') do |f|
    content = f.read
    content.gsub!(/\r\n?/, "\n")
    content.each_line do |line|
        id, title = '', ''
        if line.match('\"id\"')
            id = line.split(':')[1].strip[0..-2]
            csvfile.write(id + ',')
        end
        if line.match('\"title\"')
            title = line.partition(':')[2].scan(/"(.*)"/)[0][0]
            csvfile.write(title + "\n")
        end
    end
end

Após o programa ser executado, a saída produzida será semelhante a esta

281952,"Flash 11.2 No Longer Supported by Google Play"
281993,"Netbeans won't open in Ubuntu"
    
por Anwar 28.09.2016 / 20:35