我想删除只有标题而没有数据的文件。我尝试了下面的命令,它工作正常,唯一的问题是它给出的退出状态为 1,应该为零。
wc -l /Path/File_* | while read CNT FN; do [ $CNT -lt 2 ] && [ "$FN" != total ] && rm "$FN"; done
我正在 Datastage ETL 工具上运行此命令,但我的工作失败了。
有没有办法修改命令以获取退出状态为0
答案1
为了避免必须读取整个文件(就像wc
这样做),当您知道第一行之后文件至少有 2 行时,我会这样做(在 GNU 系统上):
LC_ALL=C gawk -v ORS='\0' '
FNR == 2 {nextfile}
ENDFILE {if (FNR < 2) print FILENAME}' /path/File_* |
xargs -r0 rm -f
这也更有效,因为它最大限度地减少了正在运行的命令数量。
更可靠,因为它适用于任意文件名。
与基于 - 的解决方案的功能差异在于wc
:它不会删除包含一个分隔行后跟一个非分隔行的文件。
如果文件无法删除(并且一开始就存在),则该文件仅返回非零退出状态。
您的问题是该管道的退出状态是其中最右侧命令的退出状态(只要您不使用该pipefail
选项)。
这里最右边的命令是while
循环。循环的退出状态是循环中最后运行的命令的退出状态身体循环的。在您的情况下,它将是[ "$FN" != total ]
在输入的最后一行运行的命令,该命令将非零,除非只有一个/path/File_*
文件(在这种情况下wc
不打印总数)。
如果你把它改成:
[ "$CNT" -gt 1 ] || [ "$FN" = total ] || rm -f -- "$FN"
如果最后一次,您只会获得非零退出状态标头无法删除文件。
答案2
您的条件false
在循环结束时返回:
# false # false = false
[ $CNT -lt 2 ] && [ "$FN" != total ]
例如,如果wc -l /path
返回
2 total
然后你运行命令
# true # true = true
wc -l * | while read CNT FN; do [ $CNT = 2 ] && [ "$FN" = total ] && echo $CNT "$FN"; done
echo $?
0
但如果你跑
# false # true = false
wc -l * | while read CNT FN; do [ $CNT = 3 ] && [ "$FN" = total ] && echo $CNT "$FN"; done
echo $?
1
换句话说:
#false # false = false
[ 2 -eq 3 ] && [ bar = foo ]
echo $?
1
#true # false = false
[ 3 -eq 3 ] && [ bar = foo ]
echo $?
1
# false # true = false
[ 2 -eq 3 ] && [ foo = foo ]
echo $?
1
# true # true = true
[ 3 -eq 3 ] && [ foo = foo ]
echo $?
0
我会使用 for 循环而不是 while 循环,并使用一个函数在未找到匹配项时返回一个值:
foo() {
ret=1 # no matches found
for file in *.txt; do # your extension
[[ $(wc -l "$file" | cut -d' ' -f1) -eq 1 ]] && echo "rm $file" && ret=0
done
return $ret
}