用bash从文本文件中提取单词

时间:2014-03-04 20:42:59

标签: linux bash sed awk

我有一个类似于下面的文本文件:

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有没有办法执行此操作?

4 个答案:

答案 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

这会打破=的每一行,并获取创建新行所需的部分。