Một loạt các lệnh sed hoạt động trên dòng lệnh, nhưng không phải trong một tập lệnh


9

Tôi đang làm việc với .csvđầu ra của truy vấn dữ liệu SE này trông giống như thế này (chỉ với 5022 mục):

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

(Và nó có ^Mkết thúc dòng giữa [số] và "" tiêu đề ""). Tôi cần nó để trông như thế này:

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

Tôi đã sửa lỗi này trong một trình soạn thảo văn bản nhất định mà vẫn không có tên khá dễ dàng, nhưng tôi muốn tạo một tập lệnh để tôi không phải làm lại mỗi lần truy vấn được làm mới & để người khác có thể sử dụng nó. Tôi đã sử dụng sed...

Chuỗi lệnh này hoạt động hoàn hảo (mặc dù nó có thể không hiệu quả; nó chỉ là một giải pháp thử và lỗi):

# 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

Vậy tại sao không? Chỉ có ^M{}được gỡ bỏ, và mọi thứ khác vẫn còn đó.

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

Tôi chắc chắn rằng sai lầm của tôi thực sự rõ ràng ...

Câu trả lời:


11

Sử dụng cat -vđể biến nhân vật CR vào literal ^Mchuỗi dường như căn bản xấu xí với tôi - nếu bạn cần phải loại bỏ dòng cuối của hệ điều hành DOS, sử dụng dos2unix, trhoặc sed 's/\r$//'

Nếu bạn nhấn mạnh vào việc sử dụng sed, sau đó tôi đề nghị bạn in các bit bạn làm muốn, chứ không phải cố gắng để xóa tất cả các bit ngẫu nhiên bạn không - ví dụ

$ 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

Bạn có thể có được sự ưa thích và cuộn việc loại bỏ trích dẫn vào trích xuất giá trị khóa bằng cách khớp 0 hoặc nhiều trích dẫn ở mỗi đầu của chuỗi giá trị

$ 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

Bạn có thể nhận được thực sự ưa thích và bắt chước các pastetrong sedbằng cách đầu tiên tham gia cặp dòng trên ,\r$kết thúc và sau đó phù hợp với các cặp khóa-giá trị nhân ( g) và không tham lam

$ 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

(Cá nhân tôi thích cách tiếp cận KISS và sử dụng phương pháp đầu tiên).


FWIW, vì đầu vào của bạn dường như được trích dẫn quá mức JSON, tôi khuyên bạn nên cài đặt một trình phân tích cú pháp JSON thích hợp, chẳng hạn như jq

sudo apt-get install jq

Sau đó bạn có thể làm một cái gì đó như

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

trong đó loại bỏ các trích dẫn thừa và sau đó sử dụng jqđể trích xuất các trường quan tâm - lưu ý rằng jqdường như xử lý các kết thúc dòng kiểu DOS, vì vậy không cần phải thực hiện các bước đặc biệt để loại bỏ các trường đó.

Thay đổi để jq '.[]'kết xuất tất cả các cặp giá trị thuộc tính.

Tín dụng cho cảm hứng và jqcú pháp cơ bản được lấy từ Vượt qua các dòng mới với grep -o


1
ugh yeah, idk tại sao tôi quên \r. jqđã phá vỡ dòng đầu tiên trong đó trường tiêu đề có dấu hai chấm (dòng đầu tiên). Tôi vẫn không chắc tại sao sedghét tôi, nhưng tôi đã giết một số trích dẫn và \rtrong dòng này /,\r*/{N;/\n.*title.*:\s/{s/,\r*\n.*title.*:\s/,\ /}}và cuối cùng nó hoạt động như thế này . Cảm ơn rất nhiều ^ _ ^
Zanna

1
Điều đó tốt hơn nhiều (nhưng tôi không muốn bất kỳ trích dẫn nào sed -rn -e 's/\"\"//g' -e 's/^(.*): (.*)\r$/\2/p' QueryR* | paste -d '' - - được thực hiện như ma thuật)
Zanna

5

Tôi đã sửa nó nhờ vào Steeldo và tiếp tục mày mò. Chưa tinh chế nhưng hoạt động.

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"

dịch:
s/"{//Xóa "{
s/}"//Xóa }"
s/^"//Xóa "khỏi bắt đầu
/,\r/{N;/\n.*title.*:\s/{s/,\r\n.*title.*:\s/,\ /}}khớp dòng ,\rtrên một dòng và [whatever]title[whatever]:trên dòng tiếp theo, thay thế tất cả những điều đó bằng ,
s/""//gXóa tất cả các dấu ngoặc kép kép còn lại
s/^\s\+//Xóa khoảng trắng khỏi đầu dòng
/^\s*$/dXóa các dòng trống
s/^id:\ //Xóa id:và khoảng trắng sau khi
s/\\//gXóa dấu gạch chéo ngược (thoát ký tự cho "được thêm vào một số trường tiêu đề)
tee "$1"chỉ định một tệp ngoại lệ khi chạy tập lệnh, ví dụ:./queryclean newquery.csv


4

Trong khi câu hỏi yêu cầu sed, người ta có thể giải quyết các vấn đề của sed với 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('""'," "))

Mã này tương thích với cả python2 và python3, vì vậy hoặc sẽ hoạt động

Chạy mẫu:

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

Ba cách tiếp cận khác:

  1. ôi

    $ 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 với các biểu thức tương thích perl và perl đơn giản:

    $ 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

Đây không phải là trả lời chính xác câu hỏi của bạn hoặc giải quyết vấn đề của bạn, nhưng để loại bỏ các ký tự không mong muốn, bạn có thể sử dụng tr :

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

và bạn sẽ nhận được:

Nhập mô tả hình ảnh ở đây


cảm ơn, tôi cần học cách sử dụng tr:)
Zanna

Nó không mạnh mẽ như sed hay awk nhưng nó rất đơn giản cho những thứ đó. Chúc mừng :)
kcdtv

1

Đây là một kịch bản khác được viết bằng Ruby. Nó sẽ giữ lại dấu phẩy trong tiêu đề, có thể dễ dàng nhập vào bất kỳ chương trình bảng tính nào mà không phá vỡ các cột.

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

Sau khi chương trình được chạy, đầu ra được sản xuất sẽ trông như thế này

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

Điều đó thật tuyệt :)
Zanna

Làm thế nào về các tiêu đề với :bên trong chúng?
Sнаđошƒаӽ

@ Sнаđошƒаӽ ôi! Cảm ơn con trỏ. Đã sửa ngay!
Anwar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.