Serangkaian perintah sed bekerja pada baris perintah, tetapi tidak dalam skrip

9

Saya bekerja dengan .csvoutput dari kueri data SE ini yang terlihat seperti ini (hanya dengan 5022 entri):

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

(Dan itu memiliki ^Mujung garis antara [angka], dan "" judul ""). Saya perlu terlihat seperti ini:

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

Saya memperbaikinya dalam editor teks tertentu yang akan tetap tanpa nama dengan mudah, tetapi saya ingin membuat skrip sehingga saya tidak perlu melakukannya lagi setiap kali kueri disegarkan & agar orang lain dapat menggunakannya. Saya menggunakan sed...

Rangkaian perintah ini bekerja dengan sempurna (meskipun mungkin tidak efisien; ini hanya solusi coba-coba):

# 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

Jadi, mengapa ini tidak dilakukan? Hanya ^Mdan {}dihapus, dan segala sesuatu yang lain masih ada.

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

Saya yakin kesalahan saya sangat jelas ...

Zanna
sumber

Jawaban:

11

Menggunakan cat -vuntuk mengubah karakter CR menjadi literal ^Murutan tampaknya fundamental jelek bagi saya - jika Anda perlu untuk menghapus ujung DOS line, penggunaan dos2unix, tratau sed 's/\r$//'

Jika Anda bersikeras menggunakan sed, maka saya sarankan Anda mencetak bit Anda lakukan inginkan, daripada mencoba untuk menghapus semua bit acak Anda tidak - misalnya

$ 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

Anda bisa mendapatkan mewah dan melempar penghapusan kuotasi ke dalam ekstraksi nilai kunci dengan mencocokkan nol atau lebih kutipan di setiap akhir urutan nilai

$ 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

Anda bisa benar - benar mewah dan meniru pastemasuk seddengan terlebih dahulu menggabungkan pasangan garis pada ,\r$akhir dan kemudian mencocokkan pasangan nilai kunci multiply ( g) dan non-rakus

$ 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

(Secara pribadi saya lebih suka pendekatan KISS dan menggunakan yang pertama).


FWIW, karena masukan Anda tampaknya JSON terlalu banyak dikutip, saya sarankan untuk menginstal parser JSON yang tepat seperti jq

sudo apt-get install jq

Anda kemudian dapat melakukan sesuatu seperti

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

yang menghilangkan tanda kutip yang berlebihan dan kemudian menggunakan jquntuk mengekstrak bidang yang diminati - perhatikan yang jqtampaknya menangani akhiran gaya-DOS, jadi tidak perlu mengambil langkah-langkah khusus untuk menghapusnya.

Ubah ke jq '.[]'untuk membuang semua pasangan atribut-nilai.

Penghargaan untuk inspirasi dan jqsintaks dasar yang diambil dari Mengatasi baris baru dengan grep -o

Steeldriver
sumber
1
ugh ya, idk kenapa aku lupa \r. jqputus pada baris pertama di mana bidang judul memiliki titik dua (baris pertama). Saya masih tidak yakin mengapa sedmembenci saya, tetapi saya membunuh beberapa kutipan dan \rdi baris ini /,\r*/{N;/\n.*title.*:\s/{s/,\r*\n.*title.*:\s/,\ /}}dan akhirnya berfungsi seperti ini . Terima kasih banyak ^ _ ^
Zanna
1
Itu JAUH lebih baik (tapi saya tidak ingin ada tanda kutip yang sed -rn -e 's/\"\"//g' -e 's/^(.*): (.*)\r$/\2/p' QueryR* | paste -d '' - - dilakukan & dilakukan seperti sulap)
Zanna
5

Saya memperbaikinya berkat steeldriver & mengutak-atik lebih lanjut. Tidak dimurnikan tetapi berfungsi.

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"

terjemahan:
s/"{//Hapus "{
s/}"//Hapus }"
s/^"//Hapus "dari awal
/,\r/{N;/\n.*title.*:\s/{s/,\r\n.*title.*:\s/,\ /}}pertandingan baris ,\rpada satu baris dan [whatever]title[whatever]:pada baris berikutnya, ganti semua dengan ,
s/""//gHapus semua tanda kutip ganda yang tersisa
s/^\s\+//Hapus spasi putih dari awal baris
/^\s*$/dHapus baris kosong
s/^id:\ //Hapus id:dan spasi setelah itu
s/\\//gHapus garis miring terbalik (escape chars for "ditambahkan ke beberapa bidang judul)
tee "$1"tentukan sebuah file outfile saat menjalankan skrip, misalnya./queryclean newquery.csv

Zanna
sumber
4

Sementara pertanyaan diajukan sed, orang dapat mengatasi masalah sed dengan 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('""'," "))

Kode ini kompatibel dengan python2 dan python3, jadi keduanya akan berfungsi

Contoh dijalankan:

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
sumber
4

Tiga pendekatan lagi:

  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 dengan regex perl yang kompatibel dan perl sederhana:

    $ 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
sumber
4

Ini bukan menjawab pertanyaan Anda atau menyelesaikan masalah Anda, tetapi untuk menghilangkan karakter yang tidak diinginkan, Anda dapat menggunakan tr :

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

dan Anda akan mendapatkan:

Masukkan deskripsi gambar di sini

kcdtv
sumber
terima kasih, saya harus belajar menggunakan tr:)
Zanna
Ini tidak sekuat sed atau awk tetapi sangat mudah untuk hal-hal semacam itu. Cheers :)
kcdtv
1

Ini adalah skrip lain yang ditulis dalam Ruby. Itu akan mempertahankan koma dalam judul, yang dapat dengan mudah diimpor ke program spreadsheet apa pun tanpa melanggar kolom.

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

Setelah program dijalankan, output yang dihasilkan akan terlihat seperti ini

281952,"Flash 11.2 No Longer Supported by Google Play"
281993,"Netbeans won't open in Ubuntu"
Anwar
sumber
Itu sangat bagus :)
Zanna
Bagaimana dengan judul :di dalamnya?
Sнаđошƒаӽ
@ Sнаđошƒаӽ oops! Terima kasih untuk penunjuknya. Diperbaiki sekarang!
Anwar