我有一个数据文件,其中多个数据块包含在特定关键字 ( DATA
, END
) 之间。我用来awk
根据从所述块中获取的文件名将数据块提取到单独的文件中。由于某些数据块共享相同的名称,如果文件(“ blockname
”)已经存在,我将使用递增的整数重命名每个输出文件:
#cat input.file
useless stuff1
DATA blockname1
data1
data1
END
useless stuff2
DATA blockname2
data2
data2
END
useless stuff3
DATA blockname1
data3
data3
END
useless stuff4
预期将是三个输出文件blockname1
、blockname2
和blockname1_1
(注意最后一个文件如何分配一个整数)
#cat blockname1
DATA blockname1
data1
data1
END
(其他人相应地...)
现在以下脚本可以按我想要的方式工作:
awk '/DATA/,/END/ {
if ( $1 ~ /DATA/ )
{ block=$2 ; i=0 ; file=block
while ( system("test ! -e " file ) )
{ i++ ; file=block"_"i ; print file }
}
print $0 > file
}' input.file
我的问题在于while
循环及其系统调用:
我希望system("test -e " file)
当存在时为 TRUE,如果尚不存在file
则为 FALSE ,即循环仅在存在时才开始运行,如果(新的)尚不存在则中断。file
while
file
file
但是,如果我使用system("test -e " file)
(并使用 使其变得冗长print file
),我将有一个同名的无限循环,并且带有递增的整数后缀,相反system("test !-e " file)
会给出所需的结果。
所以这与我的预期完全相反。
答案1
好的,我想:问题在于 的退出状态test
和while
循环条件之间 TRUE 和 FALSE 的不同定义awk
。
正test
命令导致退出代码0
为 TRUE,负命令导致退出代码1
为 FALSE。
但是,在awk
循环中while
解释0
为 FALSE 和1
TRUE,因此定义完全相反。
举个例子:
awk '{ while ( 0 ) ; { print "0" } }' file
不会产生任何输出,而
awk '{ while (1) ; { print "1" } }' file
将打印无限1
s。
因此,最佳实践是在这种组合中明确
while ( system("command") == 0 )
或者
while ( system("command") == 1 )
分别。
所以就我而言
while ( system("test -e " file ) == 0 )
显示预期的行为。
答案2
awk
system()
返回您运行的命令的退出状态 - 0 表示成功,如果不成功则返回 != 0。对于简单的示例,您可以尝试运行:
v = system("date");
v 将是 0
如果你运行:
v = system("dat");
v 可能是 127 或不同于 0 的值,如果 dat 命令丢失或未找到,则操作系统返回错误。
答案3
如果我理解你的意思,目标是将 input.file 的内容提取到各种文件中,避免丢失同名的块。
如果是这种情况,并且如果目标目录在提取之前始终为空,那么有一个更好(更快)的解决方案:
awk '
/DATA/{
block=$2;
n = blocks[block]++;
file=block (n? "_" n: "");
}
/DATA/,/END/{
print > file
}' input.file
这样 awk 不需要执行 N 次新的 shell 来测试文件是否存在。
笔记:
- 不需要 BEGIN 块,因为 awk 的字段分隔符已经是空格。
'\'
行尾不需要,因为单引号已经是多行了。