一系列sed命令可在命令行上运行,但不能在脚本中运行


9

我正在使用此SE数据查询.csv输出,它看起来像这样(仅包含5022个条目):

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

(它的行^M尾在[number]和““ title”“之间)。我需要它看起来像这样:

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

我确定我的错误确实很明显...

Answers:


11

使用cat -v转CR字符转换成文字^M序列似乎根本丑我-如果你需要删除DOS行结束,使用dos2unixtrsed '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

您可以通过以下方式真正看中并模仿pastein 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 '.[]'转储所有属性值对。

感谢使用grep -o克服换行符获得的灵感和基本jq语法


1
恩,我为什么忘记了\rjq在标题字段带有冒号的第一行(第一行)中断。我仍然不确定为什么sed讨厌我,但是我取消了一些引号,\r在这一行中/,\r*/{N;/\n.*title.*:\s/{s/,\r*\n.*title.*:\s/,\ /}},最后它像这样工作。非常感谢^ _ ^
Zanna

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 -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. 具有perl兼容正则表达式和简单perl的GNU grep:

    $ 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

它不如sedawk强大,但是对于这种事情非常简单。干杯:)
kcdtv

1

这是另一个用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"

很好:)
Zanna

:里面的标题怎么样?
Sнаđошƒаӽ

@Sнаđошƒаӽ哎呀!感谢您的指导。立即修复!
安瓦尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.