在Unix中获取逗号分隔的不同值

3
我有一个Unix文件Err_Call_sipregtracking.csv,内容如下:
colnum~filename~date~fieldnum~name~value
15~YYYYMMDD_BDACA_SELFRELATIVE_ARN~30MAR2016:00:00:00~1~BDA_CA_Code~1
15~YYYYMMDD_BDACA_SELFRELATIVE_ARN~30MAR2016:00:00:00~2~ARN_Code~2
15~YYYYMMDD_BDACA_SELFRELATIVE_ARN~30MAR2016:00:00:00~544~ALL~0
15~YYYYMMDD_BDACA_SELFRELATIVE_ARN~30MAR2016:00:00:00~544~ALL~0

这里的分隔符是~

我想将名字列的不同值存储到一个变量中。

我的期望输出是:

'BDA_CA_Code','ARN_Code','ALL'

请帮助我实现这个功能。我尝试使用以下方法:

cat Err_Call_sipregtracking.csv | awk -F'~' '{print $5}' | uniq

输出结果为:
name
BDA_CA_Code
ARN_Code
ALL

"但是我不想在结果中包含标题,并且我希望它们用引号和逗号分隔。"

请注意,cat file | awk 'things' 不是必需的。 awk 'things' file 就足够了。 - fedorqui
6个回答

5
重点在于将数值存储在一个数组中,然后打印所有元素:
$ awk -F'~' 'NR>1{item[$5]} END {for (i in item) print i}' file
ARN_Code
BDA_CA_Code
ALL

请注意使用NR>1跳过标题。
然后,您可以使用printf "\047%s\047\n", i打印用单引号括起来的元素,因为print "\047hello\047"会打印出'hello'
$ awk -F'~' 'NR>1{item[$5]} END {for (i in item) printf "\047%s\047\n", i}' file
'ARN_Code'
'BDA_CA_Code'
'ALL'

要将它们合并成逗号分隔的项目列表,只需从第二个项目开始每个项目前面打印一个逗号(感谢Ed Morton):

for (i in item) printf "%s\047%s\047", (++c>1 ? "," : ""), i
print ""

看它实际运作:

$ awk -F'~' 'NR>1{item[$5]} END {for (i in item) printf "%s\047%s\047", (++c>1 ? "," : ""), i; print ""}' file
'ARN_Code','BDA_CA_Code','ALL'

@sjsam 怎么做?我看不到它。 - fedorqui
1
@fedorqui--非常感谢。 - Pavani Srujana
1
这是一个非常合理的方法,但需要注意内存使用和输出顺序。不过为了表明我的立场,我会将 END 部分写成 awk -F'~' 'NR>1{item[$5]} END{for (i in item) printf "%s\047%s\047", (++c>1 ? "," : ""), i; print ""}' file。在我看来,print "'\''" i "'\''" 更清晰地写作 print "\047" i "\047" - Ed Morton
1
你应该始终使用八进制转义码,而不是十六进制转义码 - 请参见http://awk.freeshell.org/PrintASingleQuote。 - Ed Morton
1
@EdMorton 立即收藏了该页面。awk 'BEGIN{print "\x27foo!\x27"}' 已经非常清晰明了 :) - fedorqui
显示剩余7条评论

3

awk 是你的好帮手:

$ var=$(awk  -v FS="~" 'NR>1 && !($5 in field){printf "\047%s\047,",$5;field[$5]}' Err_Call_sipregtracking.csv)
$ var="${var%,}" #Stripping the trailing comma
$ echo "$var"
'BDA_CA_Code','ARN_Code','ALL'

注意事项


我喜欢你使用-vq="'"来打印那些单引号。看起来更容易阅读。 - fedorqui
1
  1. -v 和变量名之间不留空格会使脚本过于依赖 gawk。2) 测试唯一性的惯用方法是使用一个名为 !seen[$5]++ 的数组进行填充。3) 没有终止换行符,输出不符合 POSIX 标准,因此会引起任何解析工具的未定义行为。4) 不要添加然后再删除东西(例如逗号),因为这容易出错。5) 您不需要执行 shell 操作来更改 awk 输出,只需将其保留在 awk 中即可。6) 要在 awk 脚本中获取单引号,请使用八进制 \047 - 比使用变量更简单。
- Ed Morton
2
@fedorqui 在脚本中使用变量 ' 会使编写脚本变得更加困难。例如,要查找 'foo.bar',需要使用 $0 ~ (q "foo\\.bar" q),而不是 /\047foo\.bar\047/。请注意第一个表达式中必须额外转义字符,还需要显式地添加 $0 ~ 前缀,并且它使用了字符串连接,这会导致速度变慢。 - Ed Morton
1
不客气。不是空字符(\0),而是换行符(\n或者更少见的UNIX应用程序中的\r\n)。printf ""将不会产生这两个字符,但是print ""将会生成适当的换行符(根据ORS设置)。我只是想指出换行符可能是\n或者\r\n,以此来说明为什么你应该使用print ""(它使用当前/适当的ORS设置),而不是硬编码你认为的换行符,例如printf "\n",以防你考虑这样做。 - Ed Morton
2
老实说,我不知道是否需要使用 POSIX shell 才能处理从没有终止换行符的输入中设置变量。我怀疑不需要,因为根据 POSIX 的规定,一行是“零个或多个非 <newline> 字符的序列 加上一个终止 <newline> 字符”。但我不确定。POSIX 文章只是规范,请参见 https://dev59.com/b3RB5IYBdhLWcg3wET1J 上的讨论。 - Ed Morton
显示剩余3条评论

3
$ awk -F'~' 'NR>1 && !seen[$5]++{printf "%s\047%s\047", (NR>2 ? "," : ""), $5} END{print ""}' file
'BDA_CA_Code','ARN_Code','ALL'

2

这可能不是非常优化,但可以工作:

tail -n+2 Newfile.csv | awk -F'~' '{$5="\""$5"\""; print $5}' | uniq | tr '\n' ',' | sed 's/\,$/\n/'

如果你想要单引号而不是双引号:
tail -n+2 Newfile.csv | awk -F'~' '{a = "'"'"'"; print a $5 a}' | uniq | tr '\n' ',' | sed 's/\,$/\n/'

说明:

  • tail -n+2 Newfile.csv 去除第一行
  • awk -F'~' '{$5="\""$5"\""; print $5}' 提取第五列,并用引号包围(对于单引号的处理显得有些复杂,可能可以绕过此问题)
  • uniq 去除重复项
  • tr '\n' ',' 将换行符替换为逗号
  • sed 's/\,$/\n/' 移除最后一个逗号并将其替换为换行符(以便更易读的输出)

3
这很有趣,但请注意awk可以在内部处理许多内容。通常情况下,管道连接过多的命令被认为不是一个好的做法,因为它需要更多的CPU时间。 - fedorqui
2
这是正确的;然而,我发现逐步解释管道更容易;此外,这些命令远远超出了awk和文本处理的范围,对于大多数用途来说,CPU在这里不应该是一个问题。话虽如此,我理解你的观点。 - pie3636
2
是的,我想这是一个平衡问题,事实上,小命令的好处就在于_每个命令只做一件事_。我的当前答案有点过于复杂,因为我想只使用awk。然而,在你的答案中,例如第一个管道可以通过简单的NR>1移除,同时通过将项目放入数组中来移除uniq - fedorqui
该语句“这些命令远远超出了awk和文本处理的范围”是错误的。这些命令在文本处理中非常平凡,并且在awk中经常使用。 - Ed Morton

0
您的命令是正确的,但需要稍作修改,如下所示:

cat Err_Call_sipregtracking| awk -F'~' '{print $5}' | uniq|sed 1d | sed -n -e 'H;${x;s/\n/,/g;s/^,//;p;}'


0

你可以使用sed 1d跳过第一行,使用cut获取第五个字段,并使用printf格式化唯一排序结果:

printf "'%s'\n" $(sed 1d Err_Call_sipregtracking.csv | cut -d~ -f5 | sort -u)

这不符合您的要求,无法将其作为单行返回:

printf "'%s'," $(sed 1d Err_Call_sipregtracking.csv | cut -d~ -f5 | sort -u)|sed 's/,$//'

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接