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


9

Ich arbeite mit der .csvAusgabe dieser SE-Datenabfrage, die so aussieht (nur mit 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 Zeilenenden ^Mzwischen [Nummer] und "" Titel ""). Ich brauche es, um so auszusehen:

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

Ich habe dies in einem bestimmten Texteditor behoben, der recht einfach namenlos bleiben soll, aber ich wollte ein Skript erstellen, damit ich es nicht jedes Mal erneut ausführen muss, wenn die Abfrage aktualisiert wird und andere es verwenden können. Ich habe sed...

Diese Befehlsreihe funktioniert einwandfrei (obwohl sie möglicherweise ineffizient ist; es handelt sich lediglich um 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

Warum nicht? Nur das ^Mund wird {}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 ...

Antworten:


11

Mit cat -vCR - Zeichen in wörtliche drehen ^MSequenzen scheint mich grundsätzlich hässlich - wenn Sie DOS - Zeilenende entfernen, verwenden dos2unix, troder sed 's/\r$//'

Wenn Sie darauf bestehen, sed zu verwenden, empfehle ich Ihnen, die gewünschten Bits zu drucken , anstatt zu versuchen, alle zufälligen Bits zu löschen, die Sie nicht haben - zum Beispiel

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

Sie könnten Lust bekommen und die Entfernung von Anführungszeichen in die Schlüsselwertextraktion einfließen lassen, indem Sie an jedem Ende der Wertesequenz null oder mehr Anführungszeichen abgleichen

$ sed -rn 's/(.*): \"*([^"]*)\"*\r/\2/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 wirklich Lust und emulieren die pastein sedvon ersten Paaren von Linien auf der Verbindungs ,\r$endet und dann die Schlüssel-Wert - Paare passende multiplizieren ( g) und nicht gierig

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

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


FWIW, da Ihre Eingabe JSON überzitiert zu sein scheint, würde ich empfehlen, einen geeigneten JSON-Parser wie z jq

sudo apt-get install jq

Sie können dann so etwas 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"

Dadurch werden die überflüssigen Anführungszeichen entfernt und anschließend die jqinteressierenden Felder extrahiert. Beachten Sie, dass jqdie Zeilenenden im DOS-Stil behandelt werden müssen, sodass keine besonderen Schritte erforderlich sind, um diese zu entfernen.

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

Dank für Inspiration und grundlegende jqSyntax aus der Überwindung von Zeilenumbrüchen mit grep -o


1
ugh yeah, idk warum ich es vergessen habe \r. jqbrach in der ersten Zeile, in der das Titelfeld einen Doppelpunkt hatte (die erste Zeile). Ich bin sedmir immer noch nicht sicher, warum ich mich hasse, aber ich habe einige der Zitate und \rin dieser Zeile getötet /,\r*/{N;/\n.*title.*:\s/{s/,\r*\n.*title.*:\s/,\ /}}und schließlich funktioniert es so . Vielen Dank ^ _ ^
Zanna

1
Das ist VIEL besser (aber ich möchte keines der Zitate so sed -rn -e 's/\"\"//g' -e 's/^(.*): (.*)\r$/\2/p' QueryR* | paste -d '' - - und wie von Zauberhand gemacht)
Zanna

5

Ich habe es dank Stahlfluss und weiterem Basteln behoben. Unraffiniert funktioniert aber.

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"

Übersetzung:
s/"{//Entfernen "{
s/}"//Entfernen }"
s/^"//Entfernen "vom Anfang der Zeile
/,\r/{N;/\n.*title.*:\s/{s/,\r\n.*title.*:\s/,\ /}}Übereinstimmung ,\rin einer Zeile und [whatever]title[whatever]:in der nächsten Zeile Ersetzen Sie alles durch ,
s/""//gEntfernen Sie alle verbleibenden doppelten doppelten Anführungszeichen.
s/^\s\+//Entfernen Sie Leerzeichen vom Zeilenanfang.
/^\s*$/dEntfernen Sie leere Zeilen.
s/^id:\ //Entfernen Sie id:und setzen Sie ein Leerzeichen danach.
s/\\//gEntfernen Sie Backslashes (Escape-Zeichen für "zu einigen
tee "$1"Titelfeldern hinzugefügt) Geben Sie beispielsweise beim Ausführen des Skripts eine Outfile an./queryclean newquery.csv


4

Während die Frage gestellt wird sed, könnte man die Probleme von sed 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, sodass beide funktionieren

Probelauf:

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 

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=$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 mit Perl-kompatiblen regulären Ausdrücken und einfachem 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

4

Dies ist nicht genau die Beantwortung Ihrer Frage oder die Lösung Ihres Problems, aber um die unerwünschten Zeichen loszuwerden, können Sie tr verwenden :

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

und du wirst bekommen:

Geben Sie hier die Bildbeschreibung ein


danke, ich muss lernen zu benutzen tr:)
Zanna

Es ist nicht so mächtig wie sed oder awk, aber für solche Dinge ist es sehr einfach. Prost :)
kcdtv

1

Dies ist ein weiteres in Ruby geschriebenes Skript. Die Kommas im Titel bleiben erhalten, die problemlos in jedes Tabellenkalkulationsprogramm importiert werden können, ohne die Spalten zu beschädigen.

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

Nach dem Ausführen des Programms sieht die erzeugte Ausgabe folgendermaßen aus

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

Das ist sehr schön :)
Zanna

Wie wäre es mit Titeln mit :darin?
Sнаđошƒаӽ

@ Sнаđошƒаӽ oops! Danke für den Zeiger. Jetzt behoben!
Anwar
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.