带有输入的 Bash 函数失败 awk 命令
Posted
技术标签:
【中文标题】带有输入的 Bash 函数失败 awk 命令【英文标题】:Bash function with input fails awk command 【发布时间】:2021-09-02 23:52:57 【问题描述】:我正在 BASH shell 脚本中编写一个函数,它应该从带有标题的 csv 文件返回行,逗号比标题多。这可能会发生,因为这些文件中有可能包含逗号的值。为了质量控制,我必须识别这些线以便以后清理它们。我目前拥有的:
#!/bin/bash
get_bad_lines ()
local correct_no_of_commas=$(head -n 1 $1/$1_0_0_0.csv | tr -cd , | wc -c)
local no_of_files=$(ls $1 | wc -l)
for i in $(seq 0 $(( $no_of_files-1 )))
do
# Check that the file exist
if [ ! -f "$1/$1_0_$i_0.csv" ]; then
echo "File: $1_0_$i_0.csv not found!"
continue
fi
# Search for error-lines inside the file and print them out
echo "$1_0_$i_0.csv has over $correct_no_of_commas commas in the following lines:"
grep -o -n '[,]' "$1/$1_0_$i_0.csv" | cut -d : -f 1 | uniq -c | awk '$1 > $correct_no_of_commas print'
done
get_bad_lines products
get_bad_lines users
这个程序的输出现在是所有文件中所有行号的所有逗号计数,
我怀疑这是由于输入$1
(文件夹名,即产品和用户)与参考$1
的awk
的调用冲突(我希望获取第一列是逗号的计数)循环中当前文件中的那一行)。
这是问题吗?如果是这样,是否可以通过使用不同的变量名而不是使用$1
引用第一列或文件夹名称来解决?
示例,当前输出:
5 6667
5 6668
5 6669
5 6670
(应该只显示包含超过 5 个逗号的文件的行)。
在 awk 调用中也尝试了变量声明,效果相同 (如Awk field variable *** with function argument 接受的答案) :
get_bad_lines ()
local table_name=$1
local correct_no_of_commas=$(head -n 1 $table_name/$table_name_0_0_0.csv | tr -cd , | wc -c)
local no_of_files=$(ls $table_name | wc -l)
for i in $(seq 0 $(( $no_of_files-1 )))
do
# Check that the file exist
if [ ! -f "$table_name/$table_name_0_$i_0.csv" ]; then
echo "File: $table_name_0_$i_0.csv not found!"
continue
fi
# Search for error-lines inside the file and print them out
echo "$table_name_0_$i_0.csv has over $correct_no_of_commas commas in the following lines:"
grep -o -n '[,]' "$table_name/$table_name_0_$i_0.csv" | cut -d : -f 1 | uniq -c | awk -v table_name="$table_name" '$1 > $correct_no_of_commas print'
done
【问题讨论】:
谢谢,@Zilog80。我有点不清楚:CSV 文件中的值不包含引号,但单个值可能包含一个或多个逗号。如果没有人工检查,很难知道哪个值对应哪个字段。 在格式正确的 CSV 文件中,字段内的逗号(或换行符)不是问题,因为 CSV 格式为此类情况提供了引用规则,它们应该不会造成任何麻烦。例如,CSV 行FOO,"BAR,BAZ",BOOM
有 3 个字段,第二个字段是 BAR,BAZ。
@user1934428 ,你是对的。不幸的是,我收到格式不正确的 CSV 文件,没有引号,例如FOO,BAR,BAZ,BOOM
,其中BAR,BAZ
对应单个字段
@GustavRasmussen:如果您知道此类字段的格式总是不正确,您可以简单地计算行中的逗号并选择那些数字不正确的行。如果某些行可能格式不正确,而其他行的逗号字段格式正确,则 IMO 最简单的方法是使用 CSV 解析器。
grep -o -n '[,]'
命令将为您期望该行中所有匹配逗号的每一行返回第一个逗号匹配。由于您需要严格计算逗号的数量,您应该将awk
与awk -v table_name="$table_name" -v num_comma=$correct_no_of_commas '/,/ if (gsub(/,/, ",")>num_comma) print($0);' "$table_name/$table_name_0_$i_0.csv"
结合起来。如果文件遇到不正确的逗号数,您也可以签入awk
脚本,然后仅输出相关文件。
【参考方案1】:
您可以完全使用awk
来实现:
get_bad_lines ()
find "$1" -maxdepth 1 -name "$1_0_*_0.csv" | while read -r my_file ; do
awk -v table_name="$1" '
NR==1 num_comma=gsub(/,/, "");
/,/ if (gsub(/,/, ",", $0) > num_comma) wrong_array[wrong++]=NR":"$0;
END if (wrong > 0)
print(FILENAME" has over "num_comma" commas in the following lines:");
for (i=0;i<wrong;i++) print(wrong_array[i]);
' "$my_file"
done
为什么你原来的 awk 命令不能只给出带有太多逗号的行,那是因为你在单引号 awk
语句 ('$1 > $correct_no_of_commas print'
) 中使用了一个 shell 变量 correct_no_of_commas。因此,shell 没有替换,awk
按原样读取 "$correct_no_of_commas",并将其视为未定义的变量。更准确地说,awk
查找在 awk
脚本中未定义的变量 correct_no_of_commas,因此它是一个空字符串。 awk
将执行 $1 > $""
作为匹配条件,并且由于 $"" 是 $0 等价物,awk
会将 $1 中的计数与完整输入进行比较线。从数字的角度来看,完整的输入行具有<tab><count><tab><num_line>,
的形式,因此awk
为0。因此,$1 > $correct_no_of_commas
将始终为真。
【讨论】:
谢谢,@Zilog80,这就是我想要的。似乎有一些语法问题,通过https://www.shellcheck.net/
运行,输出:$ shellcheck myscript Line 1: get_bad_lines () ^-- SC1009: The mentioned syntax error was in this function. ^-- SC1073: Couldn't parse this brace group. Fix to allow more checks. Line 2: awk -v table_name="$1" ( ^-- SC1036: '(' is invalid here. Did you forget to escape it? ^-- SC1056: Expected a ''. If you have one, try a ; or \n in front of it. ^-- SC1072: Missing ''. Fix any mentioned problems and try again.
我的错,有一个错字,括号应该是第一行的单引号。您还可以使用 function get_bad_lines ()
来表示 bash 或保留 get_bad_lines ()
以符合 POSIX 标准。
谢谢@Zilog80,这解决了掉毛问题。现在我得到了另一个文件名通配符:awk: fatal: cannot open file
medications/medications_0_*_0.csv' 用于读取(没有这样的文件或目录)`
用 for 循环遍历函数体周围的所有文件来修复它。
@GustavRasmussen 更新了关于多个文件的答案。【参考方案2】:
您可以使用单个 awk 命令识别所有坏行
awk -F, 'FNR==1print FILENAME; headerCount=NF; NF>headerCountprint ENDFILEprint "#######\n"' /path/here/*.csv
如果您还想打印行号,请使用此
awk -F, 'FNR==1print FILENAME"\nLine#\tLine"; headerCount=NF; NF>headerCountprint FNR"\t"$0 ENDFILEprint "#######\n"' /path/here/*.csv
【讨论】:
收到错误:awk: can't open file /my_path/*.csv source line number 1
。通配符*
不会扩展为文件名。为什么会这样?如何解决?
您能提供您尝试运行的完整命令吗?我已经检查过它在 MacOS 和 Ubuntu 上的工作情况。
folders=(my_path my_path_2); for folder in $folders do awk -F, 'FNR==1print FILENAME; headerCount=NF; NF>headerCountprint ENDFILEprint "#######\n"' /$folder/*.csv done
好的,我找到了问题。文件夹名称前面有一个正斜杠。删除它后,它现在可以工作了。 (将/$folder/*.csv
更改为$folder/*.csv
)
当前代码会查找列数多于标题的行。您可能还需要考虑较少的列数并将条件更改为NF != headerCount
以上是关于带有输入的 Bash 函数失败 awk 命令的主要内容,如果未能解决你的问题,请参考以下文章
使用 bash 命令 awk sed 等从脚本中提取参数字段
使用 awk 解析 nm 命令的输出 - Linux Bash