如何使用 awk 命令合并行,以便行中应该有特定字段

How to merge lines using awk command so that there should be specific fields in a line

我想合并文件中的一些行,以便这些行应包含由 ~ 分隔的 22 个字段。

输入文件如下所示。

200269~7414~0027001~VALTD~OM3500~963~~~~716~423~2523~Y~UN~~2423~223~~~~A~200423

2269~744~2701~VALD~3500~93~~~~76~423~223~Y~ UN~~243~223~~~~A~200123 209~7414~7001~VALD~OM30~963~~~

~76~23~2523~Y~UN~~223~223~~~~A~123

等等

第一行看起来不错。第 2 行和第 3 行需要合并,使其成为包含 22 个字段的行。第4、5、6行应该合并等等。

预期输出:

200269~7414~0027001~VALTD~OM3500~963~~~~716~423~2523~Y~UN~~2423~223~~~~A~200423

2269~744~2701~VALD~3500~93~~~~76~423~223~Y~UN~~243~223~~~~A~200123 209~7414~7001~VALD~OM30~963~~~~76~23~2523~Y~UN~~223~223~~~~A~123

该文件有 10 GB 的数据,但我编写的代码(使用 while 循环)执行时间过长。如何使用 awk/sed 命令解决这个问题?

使用的代码:

IFS=$'\n'
set -f

while read line
do
count_tild=`echo $line | grep -o '~' | wc -l`
if [ $count_tild == 21 ]
then
echo $line 
else
checkLine
fi
done < file.txt



function checkLine
{
current_line=$line
read line1
next_line=$line1
new_line=`echo "$current_line$next_line"`
count_tild_mod=`echo $new_line | grep -o '~' | wc -l`
if [ $count_tild_mod == 21 ]
then
echo "$new_line"
else
line=$new_line
checkLine
fi
}

为此仅使用 shell 速度慢、容易出错且令人沮丧。改用 Awk。

awk -F '~' 'NF==1 { next } # Hack; see below
     NF<22 {
         for(i=1; i<=NF; i++) f[++a]=$i }
    a==22 {
         for(i=1; i<=a; ++i) printf "%s%s", f[i], (i==22 ? "\n" : "~")
         a=0 }
    NF==22
    END {
         if(a) for(i=1; i<=a; i++) printf "%s%s", f[i], (i==a ? "\n" : "~") }' file.txt>file.new

这假设当您合并它们时,字段太少的连续行加起来总是恰好为 22。您可能想检查这个假设(或者可能接受这个答案并提出一个包含更多更好细节的新问题)。或者也许只是添加类似

的内容
a>22 {
    print FILENAME ":" FNR ": Too many fields " a >"/dev/stderr"
    exit 1 }

NF==1 块是一种绕过样本中完全空的第 5 行的怪异行为的 hack。

您的尝试包含多个错误且效率低下;首先,尝试 http://shellcheck.net/ 诊断其中的许多问题。

你可以试试这个 awk

awk '
  BEGIN {
    FS=OFS="~"
  }
  {
    while(NF<22) {
      if(NF==0)
        break
      a=[=10=]
      getline
      [=10=]=a[=10=]
    }
    if(NF!=0)
      print
  }
' infile

或这个 sed

sed -E '
  :A
    s/((.*~){21})([^~]*)//
    tB
    N
    bA
  :B
    s/\n//g
' infile
$ cat tst.awk
BEGIN { FS="~" }
{
    sub(/^[0-9]+\./,"")
    gsub(/[[:space:]]+/,"")

    [=10=] = prev [=10=]
    if ( NF == 22 ) {
        print ++cnt "." [=10=]
        prev = ""
    }
    else {
        prev = [=10=]
    }
}

$ awk -f tst.awk file
1.200269~7414~0027001~VALTD~OM3500~963~~~~716~423~2523~Y~UN~~2423~223~~~~A~200423
2.2269~744~2701~VALD~3500~93~~~~76~423~223~Y~UN~~243~223~~~~A~200123
3.209~7414~7001~VALD~OM30~963~~~~76~23~2523~Y~UN~~223~223~~~~A~123

上面的假设是您在 1 行上的字段永远不会超过 22 个,也不会在每个少于 22 个字段的连续行的任何串联中超过 22 个,就像您在样本输入中显示的那样。