我正在做这项任务,从包含不同客户的 CSV 文件中删除行。我已经弄清楚了如何使用以下代码删除一个特定的客户:
delete() {
awk -F "\"*;\"*" '$1 != '$@' {print $ALL}' input.csv > output.csv
}
delete $@
但是,现在我必须同时删除多个客户。我可以通过存储在 csv 文件第一列中的客户编号来识别客户。我应该为不同的客户编号创建一个数组,并创建一个 while 循环来循环遍历该数组,但我似乎无法弄清楚。
答案1
我不确定你为什么要将它包装在 shell 函数中——我假设这是你任务的要求。
首先,请注意,"*;"*
在 Awk 中用作字段分隔符并不是处理带引号的 CSV 字段的可靠方法 - 例如,如果一行中的第一个字段或最后一个字段被引号引起来,它就会失败,并且它不会保留带引号的分隔符(即,带引号的字段实际上包含;
),这就错过了引用 CSV 字段的全部意义。
其次,您不应尝试以这种方式将 shell 变量(或位置参数)传递到 Awk 表达式中 - 正确的方法是导出它们,然后通过数组访问它们ENVIRON
,或者使用命令行选项-v
。因此,您的“单一客户”实现会写得更好
delcust() {
awk -F '"*;"*' -v cust="$1" '$1 != cust' input.csv > output.csv
}
delcust "$1"
当你可以修改它以传递多个位置参数,我建议通过标准输入传递客户列表并将其解析为值文件;这样您就可以基于关联数组(或哈希)执行规范的 Awk 查找:
delcusts() {
printf '%s\n' "$@" | awk -F'"*;"*' 'NR==FNR {custs[$0]=1; next} !($1 in custs)' - input.csv > output.csv
}
delcusts "$@"
请注意,您不需要print
在 Awk 中显式指定,因为print
如果规则评估结果为非零,则是默认操作。
答案2
实际上不需要数组。你可以像这样定义你的函数:
delete() {
awk -v customer="^($1)\$" -F ";" '$1 !~ customer {print $ALL}' input.csv >output.csv
}
我不明白您如何定义字段分隔符,因此我对其进行了更改以便能够进行测试。相关部分是使用否定正则表达式!~
。此外,我还使用了-v
awk 的参数,这可以为您省去很多 shell 引用的麻烦。
通过这个,您可以使用这样的参数来删除多个客户:
delete 'bla|foo'
对于 input.csv 这样的:
bla;blu;bli
foo;faa;fii
blafoo;blufaa;blifii
它会产生
blafoo;blufaa;blifii
在 output.csv 中。
如果您确实想使用数组,您可以另外定义一个小辅助函数来准备数组以供delete()
上述函数使用:
join() { local IFS=\|; echo "$*"; }
通过它,您可以定义一个 bash 数组并将其转换为正则表达式替代语法:
$ a=(bla blu)
$ join ${a[@]}
bla|blu
然后你可以delete()
像这样调用:
$ a=(customer1 customer2)
$ delete "$(join ${a[@]})"
(对于 zsh 用户的小提示:join()
zsh 不需要该函数,您可以简单地使用以下参数扩展:${(j:|:)a}
将所有数组元素与|
字符连接起来)