一系列 sed 命令在命令行上工作,但不在脚本中

Zan*_*nna 9 command-line bash scripts sed

我正在处理这个 SE 数据查询.csv输出,它看起来像这样(只有 5022 个条目):

"{
  ""id"": 281952,
  ""title"": ""Flash 11.2 No Longer Supported by Google Play""
}"
"{
  ""id"": 281993,
  ""title"": ""Netbeans won't open in Ubuntu""
}"
Run Code Online (Sandbox Code Playgroud)

(并且它^M在 [number] 和 ""title"" 之间有行结尾)。我需要它看起来像这样:

281952,Flash 11.2 No Longer Supported by Google Play
281993,Netbeans won't open in Ubuntu
Run Code Online (Sandbox Code Playgroud)

我在某个文本编辑器中修复了这个问题,该编辑器将很容易保持无名,但我想制作一个脚本,这样每次刷新查询时我都不必再次执行此操作,以便其他人可以使用它。我用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
Run Code Online (Sandbox Code Playgroud)

那么,为什么不呢?只有^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
Run Code Online (Sandbox Code Playgroud)

我确定我的错误非常明显......

ste*_*ver 11

使用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
Run Code Online (Sandbox Code Playgroud)

您可以通过在值序列的每一端匹配零个或多个引号,将引号删除滚动到键值提取中

$ 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
Run Code Online (Sandbox Code Playgroud)

你可以得到真正的花哨和仿效的pastesed通过第一连接线对,\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
Run Code Online (Sandbox Code Playgroud)

(我个人更喜欢 KISS 方法并使用第一种方法)。


FWIW,由于您的输入似乎被高估了 JSON,我建议安装适当的 JSON 解析器,例如 jq

sudo apt-get install jq
Run Code Online (Sandbox Code Playgroud)

然后你可以做类似的事情

$ 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"
Run Code Online (Sandbox Code Playgroud)

它删除了多余的引号,然后用于jq提取感兴趣的字段 - 请注意,它jq似乎处理了 DOS 样式的行结尾,因此无需采取特殊步骤来删除它们。

更改为jq '.[]'转储所有属性值对。

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


Zan*_*nna 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"
Run Code Online (Sandbox Code Playgroud)

翻译:
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