我有一个类似于下面的文本文件:
text src=127.0.0.1 text dst=127.0.0.1 text text proto=23
text text text src=192.168.1.254 text text dst=192.168.1.40 text proto=3389
text src=10.213.18.254 text dst=192.168.15.3 text text proto=389
text text text src=192.168.1.254 text text dst=192.168.1.40 text proto=3389
...
我想要一个类似于这个的输出文件(使用bash):
src=127.0.0.1 dst=127.0.0.1 proto=23
src=192.168.1.254 dst=192.168.1.40 proto=3389
src=10.213.18.254 dst=192.168.15.3 proto=389
src=192.168.1.254 dst=192.168.1.40 proto=3389
不幸的是,信息并不总是在同一列上(这使得awk无用)。 bash有没有办法执行此操作?
答案 0 :(得分:4)
纯粹的bash:
#!/bin/bash
# read each line into an array of words
while read -r -a words_in; do
# copy only words containing '=' into an output array
words_out=()
for word in "${words_in[@]}"; do
[[ $word = *=* ]] && words_out+=( "$word" )
done
# use first character of $IFS to join contents of output array when printing.
printf '%s\n' "${words_out[*]}"
done
如果你想要一个不同的标准,修改内循环的内容应该是直截了当的。例如,要仅传递以src=
,dst=
或proto=
开头的字词:
for word in "${words_in[@]}"; do
case $word in
src=*|dst=*|proto=*) words_out+=( "$word" ) ;;
esac
done
答案 1 :(得分:4)
每当您输入包含name=value
对的数据时,请考虑创建一个数组,将每个=
左侧的字段名称映射到右侧,然后只打印字段名:
$ awk '{
for (i=1;i<=NF;i++) {
split($i,t,/=/)
map[t[1]] = t[2]
}
print "src="map["src"], "dst="map["dst"], "proto="map["proto"]
}' file
src=127.0.0.1 dst=127.0.0.1 proto=23
src=192.168.1.254 dst=192.168.1.40 proto=3389
src=10.213.18.254 dst=192.168.15.3 proto=389
src=192.168.1.254 dst=192.168.1.40 proto=3389
如果您愿意,可以添加一项功能来简化和删除打印中的冗余:
$ awk 'function m(str) { return(str"="map[str]) }
{
for (i=1;i<=NF;i++) {
split($i,t,/=/)
map[t[1]] = t[2]
}
print m("src"), m("dst"), m("proto")
}' file
src=127.0.0.1 dst=127.0.0.1 proto=23
src=192.168.1.254 dst=192.168.1.40 proto=3389
src=10.213.18.254 dst=192.168.15.3 proto=389
src=192.168.1.254 dst=192.168.1.40 proto=3389
答案 2 :(得分:1)
使用sed
sed 's/text\s//g' filename
答案 3 :(得分:1)
awk
可以毫无问题地执行此操作。
awk '{for (i=1;i<=NF;i++) if ($i~/=[0-9]/) printf "%s ",$i;print ""}' file
src=127.0.0.1 dst=127.0.0.1 proto=23
src=192.168.1.254 dst=192.168.1.40 proto=3389
src=10.213.18.254 dst=192.168.15.3 proto=389
src=192.168.1.254 dst=192.168.1.40 proto=3389
这将打印包含=
和后面的数字的所有字段。
另一个版本:
awk '{for (i=1;i<=NF;i++) if ($i~/(src|dst|proto)=/) printf "%s ",$i;print ""}' file
src=127.0.0.1 dst=127.0.0.1 proto=23
src=192.168.1.254 dst=192.168.1.40 proto=3389
src=10.213.18.254 dst=192.168.15.3 proto=389
src=192.168.1.254 dst=192.168.1.40 proto=3389
这会打印包含src=
或dst=
或proto=
这将删除上面解决方案中最后一个数据后面的额外空格:
awk '{for (i=1;i<=NF;i++) if ($i~/(src|dst|proto)=/) s=s" "$i;sub(/ /,"",s);print s;s=""}' file
使用awk
而不使用循环的另一种方法:
awk 's {printf s"="$1($NF=="src"?"\n":FS);s=0} {s=$NF}' RS="=" file
src=127.0.0.1 dst=127.0.0.1 proto=23
src=192.168.1.254 dst=192.168.1.40 proto=3389
src=10.213.18.254 dst=192.168.15.3 proto=389
src=192.168.1.254 dst=192.168.1.40 proto=3389
这会打破=
的每一行,并获取创建新行所需的部分。