Серия sed управляет работой над командной строкой, но не в сценарии

Я работаю с .csv вывод этого запроса данных SE, который похож на это (только с 5 022 записями):

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

Я уверен, что моя ошибка действительно очевидна...

9
задан 18 September 2016 в 04:29

6 ответов

Используя cat -v превратить символы CR в литерал ^M последовательности кажутся существенно ужасными мне - если необходимо удалить окончания строки DOS, использовать dos2unix, tr, или sed 's/\r$//'

Если Вы настаиваете на том, чтобы использовать sed, то я предлагаю, чтобы Вы распечатали биты, которые Вы действительно хотите, вместо того, чтобы пытаться удалить все случайные биты, которые Вы не делаете - например,

$ 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

11
ответ дан 23 November 2019 в 04:46

Еще три подхода:

  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 с жемчугом совместимый regexes и простой жемчуг:

    $ 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
ответ дан 23 November 2019 в 04:46

Это точно не отвечает на Ваш вопрос или решает Вашу проблему, но избавиться от нежелательных символов, которые можно использовать tr:

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

и Вы доберетесь:

Enter image description here

4
ответ дан 23 November 2019 в 04:46

Я зафиксировал его благодаря 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 Удалите обратные косые черты (символы Escape для того, "добавил к некоторым полям заголовка),
tee "$1" укажите outfile при запущении скрипта, например ./queryclean newquery.csv

5
ответ дан 23 November 2019 в 04:46

В то время как вопрос просит 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
ответ дан 23 November 2019 в 04:46

Это - другой сценарий, записанный в Ruby. Это сохранит запятые в заголовке, который может быть легко импортирован в любую программу электронной таблицы, не повреждая столбцы.

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"
1
ответ дан 23 November 2019 в 04:46

Другие вопросы по тегам:

Похожие вопросы: