有一个日志文件,其中包含以下数据:
2019-07-18 12:00:00,000 login1 abc-def-geh
2019-07-18 12:00:00,001 login2 abc-def-geh,bcd-efg-hij
我正在尝试使用 sed(或任何其他 bash 文本处理工具)处理该文件以获得以下输出:
2019-07-18 12:00:00,000 login1 abc-def-geh
2019-07-18 12:00:00,001 login2 abc-def-geh
2019-07-18 12:00:00,001 login2 bcd-efg-hij
或者反过来:
- 我需要捕获一行的一部分(从开始到登录信息)
- 我需要在","上拆分该行的其余部分,以分隔ID
- 每个新行都需要包含捕获的部分(日期时间登录)+ ID
awk '$NF~/,/{split($NF,a,",");$NF="";for(x in a)print $0a[x];next}7' file
这条线应该会有所帮助。
是的,最多可以有数百个这样的部件 (ID)
- 它也会起作用。
Perl是实现目标的更慷慨的工具。试试这个:
perl -nle 'm/(.*) (S+) (S+)$/; print "$1 $2 $_" foreach split ",", $3'
sed ': c; s/^([^ ]+ [^ ]+ [^ ]+) ([^n,]+),(.*)/1 3n1 2/; t c; s/([^n]*)n(.*)/2n1/'
: c
声明标签c
("继续"中的简短助记符)^([^ ]+ [^ ]+ [^ ]+)
- 匹配前三个部分([^n,]+)
- 匹配第一部分直到逗号。找到换行符时也停止匹配,这在以后会很重要......,
- 如果我们停在换行符处,我们应该停止处理。如果我们停在逗号处,我们应该匹配一个逗号。(.*)
- 记住逗号后面的其余部分。1 3n1 2
- 在输入末尾添加匹配的部分。这样在下一次迭代中,我们可以再次匹配(一次又一次......我们停止 matchine,然后3
它不再有任何逗号,那么([^n,]+),
正则表达式部分将失败,因为不会有逗号。t c
- 如果上一个s///
命令成功,则分支到标签c
。s///
将成功,直到逗号在字符串中。s/([^n]*)n(.*)/2n1/
- 将第一行移动到最后一行。没有它,最后一个模式将是第一个。只需匹配第一行并移动它。
测试对象(随机键入以获取更多输入数据):
cat <<EOF |
2019-07-18 12:00:00,000 login1 abc-def-geh
2019-07-18 12:00:00,001 login2 abc-def-geh,bcd-efg-hij
2019-07-18 12:00:00,001 login2 abc-def-geh,bcd-efg-hij,bfdsabfasdh
2019-07-18 12:00:00,001 login2 abc-def-geh,bcd-efg-hij,bfdsabfasdh,fdsmfasfda,f,da,dfas,fd,asf,das,fsd,af,a,fdsafasdfsda,fasd
EOF
sed ': c; s/^([^ ]+ [^ ]+ [^ ]+) ([^n,]+),(.*)/1 3n1 2/; t c; s/([^n]*)n(.*)/2n1/'
将输出:
2019-07-18 12:00:00,000 login1 abc-def-geh
2019-07-18 12:00:00,001 login2 abc-def-geh
2019-07-18 12:00:00,001 login2 bcd-efg-hij
2019-07-18 12:00:00,001 login2 abc-def-geh
2019-07-18 12:00:00,001 login2 bcd-efg-hij
2019-07-18 12:00:00,001 login2 bfdsabfasdh
2019-07-18 12:00:00,001 login2 abc-def-geh
2019-07-18 12:00:00,001 login2 bcd-efg-hij
2019-07-18 12:00:00,001 login2 bfdsabfasdh
2019-07-18 12:00:00,001 login2 fdsmfasfda
2019-07-18 12:00:00,001 login2 f
2019-07-18 12:00:00,001 login2 da
2019-07-18 12:00:00,001 login2 dfas
2019-07-18 12:00:00,001 login2 fd
2019-07-18 12:00:00,001 login2 asf
2019-07-18 12:00:00,001 login2 das
2019-07-18 12:00:00,001 login2 fsd
2019-07-18 12:00:00,001 login2 af
2019-07-18 12:00:00,001 login2 a
2019-07-18 12:00:00,001 login2 fdsafasdfsda
2019-07-18 12:00:00,001 login2 fasd
请注意,在替换替换列表中使用 sed 内部的n
是一个 gnu 扩展。
...
sed
可以做这种处理。使用(…)
您可以在搜索模式中创建组,可以使用N
替换访问这些组,其中N
是组的编号。
如果最多可以有两个 ID,则sed
命令很简单:
sed -E 's/(.*,.* )(.*),(.*)/12n13/'
。但
如果最后可以有多个ID,那么您将不得不摆弄sed
的保持空间,因为您必须更换刚刚更换的部件。此时,切换到另一个工具更有意义。
$ awk '$NF~/,/{n=split($NF,p,/,/); sub(/[^[:space:]]+$/,""); for (i=1; i<=n; i++) print $0 p[i]; next} 1' file
2019-07-18 12:00:00,000 login1 abc-def-geh
2019-07-18 12:00:00,001 login2 abc-def-geh
2019-07-18 12:00:00,001 login2 bcd-efg-hij
此解决方案与@Kent解决方案之间的区别在于:
- 它不会导致 awk 为处理的行重新编译 $0(因此它不会更改字段之间的空格)。
- 这将导致 awk 将 $0 重新拆分为这些行的字段。
- 它将以与它们相同的顺序输出最后一个字段的各个部分 发生在输入中,而不是以"随机"(可能是哈希)顺序发生。
这可能对你有用(GNU sed):
sed -E 's/^(((S+s){3})[^,]*),/1n2/;P;D' file
将前三个字段后面的逗号替换为换行符,将前三个字段替换为打印、删除和重复。