如何使用 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 个,就像您在样本输入中显示的那样。
我想合并文件中的一些行,以便这些行应包含由 ~ 分隔的 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 个,就像您在样本输入中显示的那样。