Серія команд sed працює в командному рядку, але не в сценарії


9

Я працюю з .csvрезультатом цього запиту даних SE, який виглядає приблизно так (лише з 5022 записами):

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

(І він має ^Mзакінчення рядків між [числом] та "" заголовком ""). Мені потрібно, щоб це виглядало так:

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

Я зафіксував це в певному текстовому редакторі, який залишиться безіменним досить легко, але я хотів зробити сценарій так, що мені не доведеться робити це повторно кожного разу, коли запит оновлюється, і щоб інші могли його використовувати. Я використовував sed...

Ця серія команд працює чудово (хоча це може бути неефективним; це лише рішення проб і помилок):

# 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

То чому б це не зробити? Тільки ^Mі {}знімайте, а все інше все ще є.

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

Я впевнений, що моя помилка дійсно очевидна ...

Відповіді:


11

Використання cat -vперетворити символи CR в літерні ^Mпослідовності здається принципово некрасиво мені - якщо вам потрібно видалити закінчення DOS рядки, використання dos2unix, trабо sed 's/\r$//"

Якщо ви наполягаєте на використанні СЕД, то я пропоную вам надрукувати біти ви дійсно хочете, а не намагатися видалити всі випадкові біти ви немає - наприклад ,

$ 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

Ви можете пофантазувати і згорнути вилучення цитат у вилучення ключа-значення, дорівнюючи нуль або більше лапок на кожному кінці послідовності значень

$ 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

Ви можете отримати дійсно фантазії і наслідувати pasteв sedспочатку приєднання пар ліній на ,\r$закінчення , а потім відповідні пари ключ-значення багаторазово ( g) і не жадібністю

$ 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

(Особисто я віддаю перевагу підходу KISS і використовую перший).


FWIW, оскільки ваш вклад здається завищеним котируванням JSON, я б запропонував встановити правильний аналізатор JSON, такий як jq

sudo apt-get install jq

Потім ви можете зробити щось на кшталт

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

яке видаляє зайві лапки, а потім використовує jqдля вилучення цікавих полів - зауважте, що, jqздається, обробляються закінчення ліній стилю DOS, тому для їх видалення не потрібно вживати спеціальних заходів.

Змініть, щоб jq '.[]'скинути всі пари атрибутів-значень.

Подяка за натхнення та базовий jqсинтаксис, узятий із подолання нових рядків з grep -o


1
Так, ідк, чому я забув \r. jqпрорвався на першому рядку, де в заголовковому полі була двокрапка (перший рядок). Я до сих пір не знаю , чому sedмене ненавидить, але я вбив деяких з цитат і \rв цій лінії , /,\r*/{N;/\n.*title.*:\s/{s/,\r*\n.*title.*:\s/,\ /}}і , нарешті , вона працює , як це . Велике спасибі ^ _ ^
Занна

1
Це набагато краще (але я не хочу, щоб жодна цитата була так sed -rn -e 's/\"\"//g' -e 's/^(.*): (.*)\r$/\2/p' QueryR* | paste -d '' - - зроблена, як магія)
Zanna

5

Я виправив це завдяки steeldriver та подальшому майструванню. Нерафінований, але працює.

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"

переклад:
s/"{//Видалити "{
s/}"//Видалити }"
s/^"//Видалити "з початку
/,\r/{N;/\n.*title.*:\s/{s/,\r\n.*title.*:\s/,\ /}}матчу рядка ,\rна одному рядку та [whatever]title[whatever]:в наступному рядку, замінити все на ,
s/""//gВидалити всі решти подвійних подвійних лапок
s/^\s\+//Видалити пробіли від початку рядків
/^\s*$/dВидалити порожні рядки
s/^id:\ //Видалити id:та пробіл після нього
s/\\//g Видалити зворотні косої риски (знаки втечі для "додано до деяких заголовних полів), наприклад,
tee "$1"вкажіть вихідний файл під час запуску сценарію./queryclean newquery.csv


4

Поки питання задається sed , можна було б вирішити проблеми sed з 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('""'," "))

Цей код сумісний і з python2, і з python3, тому будь-який буде працювати

Проба зразка:

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

Ще три підходи:

  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 з сумісними регулярними виразами perl та простим 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

Це не зовсім відповідь на ваше запитання чи вирішення проблеми, але щоб позбутися від небажаних символів, ви можете використовувати tr :

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

і ви отримаєте:

Введіть тут опис зображення


дякую, мені потрібно навчитися користуватися tr:)
Zanna

Це не настільки потужно, як sed або awk, але дуже просто для таких речей. Ура :)
kcdtv

1

Це ще один сценарій, написаний на Рубі. Він збереже коми в заголовку, які можна легко імпортувати в будь-яку програму електронних таблиць, не порушуючи стовпці.

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

Після запуску програми отриманий результат буде виглядати приблизно таким

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

Це дуже приємно :)
Zanna

Як щодо заголовків з :ними всередині?
Snađошƒаӽ

@ Snađошƒаӽ ой! Дякуємо за вказівник. Виправлено зараз!
Анвар
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.