bash,如何选择文件中三个由空格分隔的字段,然后选择由另一个符号分隔的其他字段?

时间:2019-04-16 20:04:02

标签: bash awk grep

我有一个这种格式的文件:

aaa bbb ccc ddd eee|fff|ggg|hhh|iii|lll|mmm|nnn|ooo|ppp
aaa1 bbb1 ccc1 ddd1 eee1|fff1|ggg1|hhh1|iii1|lll1|mmm1|nnn1|ooo1|ppp1
aaa2 bbb2 ccc2 ddd2 eee2|fff2|ggg2|hhh2|iii2|lll2|mmm2|nnn2|ooo2|ppp2

如您所见,前三个字段由空格分隔,而其他字段由|分隔。标志。 我想选择前3个字段,然后选择第8个和第9个字段。

我想要以下输出:

aaa bbb ccc hhh iii
aaa1 bbb1 ccc1 hhh1 iii1
aaa2 bbb2 ccc2 hhh2 iii2

如您所见,我应该过滤两个定界符:空间和管道。

如何进行bash操作?

我尝试使用awk,但是无法使用两个不同的定界符来运行它。

6 个答案:

答案 0 :(得分:2)

如果您的代码对性能不太敏感,无法使awk成为更好的选择,则下面的代码在本机bash中进行有问题的解析,并且即使以竖线分隔的字段也能获得正确的结果除了第一个包含空格之外的内容:

while IFS='|' read -r -a psep_fields; do          # read into pipe-separated fields
  read -r -a space_fields <<<"${psep_fields[0]}"  # read 1st field & parse by spaces
  printf '%s %s %s %s %s\n' \
    "${space_fields[0]}" "${space_fields[1]}" "${space_fields[2]}" \
    "${psep_fields[3]}" "${psep_fields[4]}"
done

https://ideone.com/zCjpDP的输入上看到此代码,并作为输出返回:

aaa bbb ccc hhh iii
aaa1 bbb1 ccc1 hhh1 iii1
aaa2 bbb2 ccc2 hhh2 iii2

答案 1 :(得分:2)

如果您的输入可能在管道的前4个字段中有管道或在管道字符串中有空格,那么最好使用此awk使用|作为分隔符来分割第5个字段:

awk 'NF>3{s = $1 OFS $2 OFS $3; sub(/^[ \t]*([^ \t]+[ \t]+){4}/, "");
if (split($0, a, "|") > 4) s = s OFS a[4] OFS a[5]; print s}' file

aaa bbb ccc hhh iii
aaa1 bbb1 ccc1 hhh1 iii1
aaa2 bbb2 ccc2 hhh2 iii2

答案 2 :(得分:1)

一种略有不同的方法-

while read a b c d e; do
   IFS="|" read -a f <<< "$e"
   echo "$a $b $c ${f[3]} ${f[4]}"
done < input.txt
aaa bbb ccc hhh iii
aaa b|b|b ccc hhh "i i i"
aaa1 bbb1 ccc1 hhh1 iii1
aaa1 bbb1 c|c|c|1 hhh1 " i i i 1"
aaa2 bbb2 ccc2 hhh2 iii2
aaa2 bbb2 ccc2 "h h h 2" iii2

读取加载的字段按通常的$IFS字符拆分,这会将所有由管道分隔的最后一批放入e中。这将保留嵌入在a-d中的所有竖线字符。由于e是最后一个变量,因此该行的其余部分都存储在此处,即使它具有嵌入的空格也是如此。

e仅在管道上显式拆分为名为f的数组。这样会保留嵌入在e字段中的所有空格字符。

不过,与下面的Charles的解决方案并没有太大区别。

答案 3 :(得分:1)

这将完全满足您的要求,而不管头(以空格分隔)部分中的字段包含LSTMStateTuple还是尾部(|分隔区域)中的字段包含空格。 / p>

使用GNU awk作为第三个参数,以match()和|的简写形式:

\S/\s

以及任何awk:

$ cat tst.awk
match($0,/^((\S+\s+){3})(.*)/,a) {
    split(a[1],h,/\s+/)
    split(a[3],t,/[|]/)
    print h[1], h[2], h[3], t[4], t[5]
}

$ awk -f tst.awk file
aaa bbb ccc hhh iii
aaa1 bbb1 ccc1 hhh1 iii1
aaa2 bbb2 ccc2 hhh2 iii2

以上假设您是正确的,并且只有前3个字段用空格隔开,因此正则表达式中的$ cat tst.awk match($0,/^([^[:space:]]+[[:space:]]+){3}/) { split(substr($0,RSTART,RLENGTH),h,/[[:space:]]+/) split(substr($0,RSTART+RLENGTH),t,/[|]/) print h[1], h[2], h[3], t[4], t[5] } $ awk -f tst.awk file aaa bbb ccc hhh iii aaa1 bbb1 ccc1 hhh1 iii1 aaa2 bbb2 ccc2 hhh2 iii2 。如果您弄错了,它实际上是4(看起来像是在您发布的样本输入中一样),那么显然只需将{3}更改为{3}。仅当您要访问第四个空格分隔的字段时,这才重要。

答案 4 :(得分:0)

Here is one awk solution. Too simple so I am not sure what edge cases I am missing but I get the desired output

awk -v FS="[ |]"  '{print $1 OFS $2 OFS $3 OFS $8 OFS $9}' inputFile

result

aaa bbb ccc hhh iii
aaa1 bbb1 ccc1 hhh1 iii1
aaa2 bbb2 ccc2 hhh2 iii2

Explanation:

I separated the fields with regex by either a space or a pipe [ |] and printed the asked fields.

答案 5 :(得分:0)

if your data in 'd' file, try gnu awk:

awk -F'[ |]' '{print $1,$2,$3,$8,$9 } ' d
awk 'BEGIN{FPAT="\\w{3,}"}{print $1,$2,$3,$8,$9 } ' d

the last is far better as far greater control on field search