【问题标题】:Validating unique values of a column in shell验证shell中列的唯一值
【发布时间】:2018-01-19 13:05:38
【问题描述】:

我得到一个输入文件 vendor.csv,其中有一列名为“retailer”。 我有一个预定义的有效零售商值列表,它们是 a、b、c。如果“d”出现在零售商列中,我将不得不采取一些措施,主要是将其回显到日志中并停止处理并通知用户。

到目前为止,我已经完成了以下操作

f1=/stage/Scripts/ecommerce/vendor/final*.csv
k=`cut -d, -f1 $f1 |sort -u`
echo $k

这给了我

a b c d

上面的o/p不是逗号分隔的

对于上述情况,我可以将有效值 a b c 存储在文件或字符串中

我现在如何进行检查?这是解决这个问题的最佳方法吗?

有效值为ALB/SFY Total Ecom TA Peapod Total Ecom TA Target Total Ecom TA

现有数据包含以下唯一数据点 ALB/SFY Total Ecom TA Hy-Vee Total Ecom TA Peapod Total Ecom TA Target Total Ecom TA

所以“Hy-Vee Total Ecom TA”是一个无效值。

这是我对 grep 的尝试

$ echo $s
ALB/SFY Total Ecom TA Peapod Total Ecom TA Target Total Ecom TA

 echo $k
ALB/SFY Total Ecom TA Hy-Vee Total Ecom TA Peapod Total Ecom TA Target Total Ecom TA

grep -v "$s" "$k"

它给了我一个错误

grep: ALB/SFY Total Ecom TA
Hy-Vee Total Ecom TA
Peapod Total Ecom TA
Target Total Ecom TA: No such file or directory

一些解决方案为我指明了正确的方向,在 R 中,我将完成上述任务

valid_values = ['a','b','c']
invalid_retailer = unique(vendorfile$retailer) %not% in valid_values 

我试图在 shell 中复制相同的过程,因此我使用了 cut 和 grep。

【问题讨论】:

  • 您要求“采取一些行动,主要是回应并通知用户”。很不清楚。你能提供更多细节吗?另外,发布您的输入的小样本?
  • in your previous question 一样,您不引用echo 的值会让自己感到困惑。你会发现输出实际上是换行符分隔的。
  • grep 的第一个参数必须是正则表达式。如果值 "$s" 实际上包含换行符,你应该没问题。但第二个参数是文件名,不是字符串。要检查字符串,printf "%s\n" "$k" | grep -v "$s" 或(如果您的 shell 是 Bash)grep -v "$s" <<<"$k"
  • 由于您收到的答案只有大约 33% 的接受率,因此不鼓励我花时间设计解决此问题的方法。你似乎也很难接受那些清楚知道自己在做什么的人的建议。
  • @shellter 感谢您的更新,很抱歉懒得自己检查。

标签: bash shell unique cut


【解决方案1】:

试试 awk 命令,这个命令非常精致。

awk -F',' '{if (($1 == "a") || ($1 == "b") || ($1 == "c") || ($1 == "d")) print $0 }' /stage/Scripts/ecommerce/vendor/final*.csv

其他方式::

我们可以在单独的文件中逐行提供所有零售商 ID,例如在 retailer.txt 中。 retailer.txt的内容是这样的

a
b

要打印与retailer.txt 中的零售商 ID 匹配的那些行的第一个字段(由 , 分隔),请使用以下命令:

awk -F',' 'FNR==NR{$1=a[$1];next} ($1 in a)' retailer.txt final*.csv

【讨论】:

  • @riyan 问题是文件很大,我正在寻找唯一的零售商并验证我没有得到任何不正确的数据点,您的代码将检查每条记录,这可能会很慢跨度>
  • 呃,不是吗?它将有效标签读入一个关联数组,因此只要您有足够的内存将它们全部保存在核心中,它就应该尽可能快(或者更确切地说,可能是 I/O 限制而不是 CPU 限制)。
  • (但逻辑是相反的;您可能想要打印那些无效有效的。这会打印有效的并忽略无效的。)
  • 是的,同意@tripleee,但您可以进一步优化命令,如下awk -F',' 'FNR==NR{$1=a[$1]} ($1 in a)' retailer.txt final*.csv而不是逐行检查,它将作为一个整体检查文件。
  • $1=a[$1] 是错字吗?它可能有点意外,但是修改然后立即忘记第一个文件中每一行的第一个字段的目的是什么?
【解决方案2】:

大概是这样的吧?

awk -F, 'NR==FNR { ++a[$1]; next }
    !a[$1] { print FILENAME ":" FNR ": Invalid label " $1 >>"/dev/stderr" }' valid.txt final*.csv

valid.txt 包含您的有效标签,每行一个。

awk 'NR==FNR { ++a[$1] }' 的一般模式是将一组文件中的第一个文件读入内存中的数组,然后在脚本的其余部分执行某种连接(在数据库意义上)与其他输入文件。 awk 一次只处理一行,因此其他文件可以任意大。不过,您确实需要能够将第一个文件中的数据存储在内存中。

与您的基本cut+grep 尝试相比的优势在于,我们可以打印整个输入行,而不仅仅是告诉您哪些标签无效,然后让您返回并手动找出文件实际包含的行违规。

切线地,您的grep 尝试存在许多问题。首先,如果您处理的不仅仅是玩具数据,那么您希望避免将数据存储在 shell 变量中。其次,您可能想要调整您的选项以告诉 grep 您要按字面意思匹配文本(-F - 没有这个,a.c 匹配 abc,因为点是正则表达式通配符,例如)和您希望匹配覆盖整行(-x -- 没有这个,b 匹配 abc 因为它是一个子字符串)。

cut -d, -f1 final*.csv | sort -u |
grep -vxFf valid.txt

-f filename 选项表示从文件中读取模式,如果没有其他文件名,grep 会处理标准输入(在这种情况下,来自管道)。

【讨论】:

  • 我会尝试复制的,请原谅我之前使用变量的错误方式,shell中表达变量的方式太多了我只是感到困惑,有时感觉很容易,有时却没有那么多,这都是因为我猜是我在 R 开始后正在研究它
【解决方案3】:

grep 不能为所欲为?

如果我理解,请在您的 csv 文件上调用grep,使用正确的正则表达式可以打印错误零售商的所有行。 您需要选择一个强大的正则表达式来防止误报匹配,但我需要输入示例来帮助您...

或者,如果正则表达式不能防止误报,您可以在 cut 命令后使用 grep,如下所示:

for bad_retailer in $(cut -d, -f1 $f1 | grep d) ; do echo $bad_retailer ; done

用 d 表示不良零售商的名称。

如果您要跟踪超过 1 家不良零售商,您可以使用 grep -E "d|g|h",以及不良零售商的 d 和 g 和 h 名称。

【讨论】:

  • shellcheck.net 的输出显示了您需要在此处修复的多个问题。试试看。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-11
  • 1970-01-01
  • 2014-11-01
  • 1970-01-01
  • 2011-06-19
  • 2012-10-10
相关资源
最近更新 更多