Eine Reihe von sed-Befehlen funktioniert in der Befehlszeile, aber nicht in einem Skript

8

Ich arbeite mit der .csv -Ausgabe von diese SE-Datenabfrage , die so aussieht (nur bei 5022 Einträgen):

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

(Und es hat die Zeile ^M endings zwischen [number] und "" title ""). Ich muss es so aussehen:

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

Ich habe das in einem bestimmten Texteditor behoben, der ganz einfach namenlos bleiben soll, aber ich wollte ein Skript machen, damit ich es nicht jedes Mal wiederholen muss, wenn die Abfrage aktualisiert wird & amp; damit andere es benutzen können. Ich habe sed ...

benutzt

Diese Reihe von Befehlen funktioniert einwandfrei (obwohl es möglicherweise ineffizient ist; es ist nur eine Trial-and-Error-Lösung):

# 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

Also, warum nicht? Nur die ^M und {} werden entfernt, und alles andere ist noch da.

#!/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

Ich bin sicher, mein Fehler ist wirklich offensichtlich ...

    
Zanna 18.09.2016, 00:37

6 Antworten

11

Die Verwendung von cat -v zum Umwandeln von CR-Zeichen in literale ^M -Sequenzen erscheint mir grundsätzlich hässlich - wenn Sie DOS-Zeilenendungen entfernen müssen, verwenden Sie dos2unix , tr oder sed 's/\r$// '

Wenn Sie darauf bestehen, sed zu verwenden, dann schlage ich vor, dass Sie die Bits drucken, die Sie tun wollen, anstatt zu versuchen, alle zufälligen Bits zu löschen, die Sie nicht haben - zum Beispiel

$ 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

Sie könnten sich etwas einfallen lassen und die Quote-Entfernung in die Schlüsselwert-Extraktion rollen lassen, indem Sie an jedem Ende der Wertefolge null oder mehr Anführungszeichen finden.

$ 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

Sie könnten wirklich erhalten und die paste in sed emulieren, indem Sie zunächst Paare von Zeilen in der ,\r$ -Ende verbinden und dann die Schlüssel / Wert-Paare multiplizieren ( g ) und nicht gierig

$ 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

(Ich persönlich würde den KISS-Ansatz bevorzugen und den ersten verwenden).

FWIW, da Ihre Eingabe überbewertetes JSON zu sein scheint, würde ich vorschlagen, einen geeigneten JSON-Parser wie jq

zu installieren
sudo apt-get install jq

Sie können dann etwas wie

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

entfernt die überflüssigen Anführungszeichen und verwendet dann jq , um die interessanten Felder zu extrahieren. Beachten Sie, dass jq anscheinend die Zeilenenden im DOS-Stil zu behandeln scheint. Daher müssen Sie keine speziellen Schritte zum Entfernen der Felder ausführen / p>

Wechseln Sie zu jq '.[]' , um alle Attribut-Wert-Paare auszugeben.

Credit for inspiration und grundlegende jq -Syntax aus Überwinden von Zeilenumbrüchen mit grep -o

    
steeldriver 18.09.2016, 01:28
5

Ich habe es dank steeldriver & amp; weiter basteln. Nicht raffiniert, aber funktioniert.

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

Übersetzung:
s/"{// Entfernen "{
s/}"// Entfernen }"
s/^"// Entfernen " vom Start der Zeile
/,\r/{N;/\n.*title.*:\s/{s/,\r\n.*title.*:\s/,\ /}} match ,\r on eine Zeile und [whatever]title[whatever]: in der nächsten Zeile, ersetzen Sie all das durch ,
s/""//g Entfernen Sie alle verbleibenden doppelten Anführungszeichen
s/^\s\+// Entfernen Leerzeichen am Zeilenanfang
/^\s*$/d Entfernen Leerzeilen
s/^id:\ // Entfernen von id: und Leerzeichen nach dem Befehl
s/\//g Entfernen von Backslashes (Escapezeichen für "hinzugefügt zu einigen Titelfeldern)
tee "" Geben Sie ein Ausgabedatei an, wenn Sie das Skript ausführen, z Beispiel ./queryclean newquery.csv

    
Zanna 18.09.2016 09:47
4

Während die Frage nach sed fragt, könnte man seds Probleme mit Python umgehen:

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('""'," "))

Dieser Code ist sowohl mit python2 als auch mit python3 kompatibel, also wird entweder

funktionieren

Beispiellauf:

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 
    
Sergiy Kolodyazhnyy 18.09.2016 13:43
4

Drei weitere Ansätze:

  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= if /id"":\s*(\d+)/; 
                 if(/title"":\s*""(.*)""/){print "$id,"}' file 
    281952,Flash 11.2 No Longer Supported by Google Play
    281993,Netbeans won't open in Ubuntu
    
  3. GNU grep mit Perl-kompatiblen Regexes und einfachen Perl:

    $ 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
    
terdon 18.09.2016 13:46
4

Dies ist nicht genau Ihre Frage zu beantworten oder Ihr Problem zu lösen, aber um die unerwünschten Zeichen loszuwerden, können Sie tr :

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

und Sie erhalten:

    
kcdtv 18.09.2016 01:18
1

Dies ist ein weiteres in Ruby geschriebenes Skript. Es behält die Kommas im Titel bei, die in jedes Tabellenkalkulationsprogramm leicht importiert werden können, ohne die Spalten zu brechen.

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

Nachdem das Programm ausgeführt wurde, sieht die erzeugte Ausgabe wie folgt aus:

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

Tags und Links