【问题标题】:How to get column index of field in unix shell如何在unix shell中获取字段的列索引
【发布时间】:2017-02-08 11:16:17
【问题描述】:

我有一个带有标题的 csv 文件:

a,b,c,d,e,f,g,h

我想做点什么

cat abc.csv | sed "something to split them" | grep "e"  

#position of "e"

有人可以指导我如何获取标题“e”所在的列 idx 吗?

【问题讨论】:

  • 所以给定a,b,c,d,e,f,g,h,您希望它告诉您e 在第5 列?
  • @MarkReed 是的,这就是我需要的

标签: shell csv unix sed


【解决方案1】:

假设你的目标是说“这个值在哪一列”,你有很多选择,但这是可行的:

sed -n $'1s/,/\\\n/gp' abc.csv | grep -nx 'e'
#output: 5:e

如果你只想从中得到数字:

sed -n $'1s/,/\\\n/gp' abc.csv | grep -nx 'e' | cut -d: -f1
#output: 5

解释:

由于标题位于文件的第一行,我们使用-n 选项告诉sed 默认情况下不要打印出所有行。然后我们给它一个以1开头的表达式,这意味着它只在第一行执行,并以p结尾,这意味着该行随后被打印出来。

表达式使用 ANSI 引号 ($'...') 只是为了更易于阅读:您可以使用 \n 在其中添加换行符,而不必包含文字换行符。无论如何,当 shell 完成它时,表达式$'1s/,/\\\n/gp' 将作为1s/,/\ /gp 传递给 sed,这告诉它用换行符替换第一行的每个逗号,然后打印出结果。您的示例中仅 sed 的输出将是:

a
b
c
d
e
f
g
h

(如果您的 CSV 文件有很多行,您可能需要将 ;q 添加到 sed 命令的末尾,以便它在第一行之后退出,而不是继续读取并且对其余部分不执行任何操作行。)

然后我们通过grep 命令查找e 来管道输出。我们传递了-x 选项,以便它只匹配由'e' 组成的行,而不仅仅是包含'e' 的任何行(感谢@Marcel 和@Sundeep),以及告诉它包含的-n 选项输出中匹配行的行数。在示例中,它输出5:e,其中5: 表示其余输出来自输入的第5 行。

然后我们可以通过cut 使用: 的字段分隔符(-d)来提取第一个字段(-f1),这是 sed 输出中的行号 - 这是原始文件中的字段编号。

【讨论】:

  • @aceminer,你需要使用 grep -n "^yourtext$" 否则你会遇到子字符串的问题。 “^”表示行首,“$”表示行尾。您可以查看我的答案以获取更多详细信息。
  • 或使用-x选项
  • 我不知道 -x @Sundeep。真是太美了!!!我也纳入了我的回答。谢谢!
  • 还有-w 选项来匹配整个单词.. 在大多数情况下避免需要\b... :)
  • 另一个建议,(在stackoverflow.com/questions/6958841/… 中找到) - 使用sed -n '/^e$/=' 仅获取行号.. 避免 grep + cut
【解决方案2】:
  1. head 正在选择第一行(标题);
  2. tr 正在替换换行符的分隔符;
  3. grep 正在选择包含您想要的字符串的行(忽略子字符串)并且行号也会显示。在示例中,我们将有 5:e;
  4. cut 使用 ':' 作为分隔符并选择第一列。所以只会显示行号。

head -n1 abc.csv | tr "," "\n" | grep -nx e |  cut -d":" -f1

文件内容:

a,b,c,d,e,f,g,h

你想要的字符串:

e

输出:

5

【讨论】:

  • 是的,马克。你说的对。我已经用“head -1”更新了答案。非常感谢。并为您的好答案 +1。
  • 建议使用code{} 图标)来格式化而不是引用。它会突出语法:)
  • 好点,@Sundeep。实际上,我试过了,但 { } 现在不适合我。就这一次o_O
  • 看起来它没有在项目符号或编号之后立即检测到代码格式...我添加了一个虚拟的 <b> 标签作为解决方法:D
  • nb:POSIXly 正确 head 采用 -n 1 而不仅仅是 -1
【解决方案3】:

这有点小技巧,但它会给你e的索引:

head -n1 abc.csv | grep -oE '^.*(,|^)e(,|$)' | tr -Cd , | wc -c

它的工作原理是提取第一行直到e的部分,然后删除除逗号之外的所有字符,最后计算逗号的数量。

【讨论】:

    【解决方案4】:

    以下命令将遍历所有字段并检查字符串“e”。如果找到,则打印该位置的索引。

    所有行:

    awk -F, '{for(i=1;i<=NF;i++) if($i=="e") print i}' input.csv
    

    如果仅限于标题/第一行,则:

    awk -F, 'NR==1{for(i=1;i<=NF;i++) if($i=="e") {print i;exit}}' input.csv
    

    例子:

    echo "a,b,c,d,e,f,g,h" |awk -F, '{for(i=1;i<=NF;i++) if($i=="e") {print i}'
    5
    

    【讨论】:

    • 真实文件可能有很多行;可能应该在该表达式上放置一个(NR==1) 保护并将print i 转换为{ print i; exit }
    • @MarkReed :嘿,马克,但这会使解决方案仅限于第一行。我们为什么要限制它?
    • OP 正在寻找“哪个列号是这个标题”。标题是第一行。
    【解决方案5】:
    $ cat ip.txt 
    a,b,c,d,e,f,g,h
    1,2,3,4,5,6,7,8
    

    perl

    $ # can also use: perl -F, -lane 'print grep {$F[$_-1] eq "e"} 1..$#F+1; exit'
    $ perl -F, -lane 'foreach (0..$#F){ print $_+1 if $F[$_] eq "e" } exit' ip.txt 
    5
    

    awk

    $ awk -F, '{ for(i=1; i<=NF; i++) if($i=="e"){print i} exit}' ip.txt 
    5
    

    在这两种情况下:

    • 输入行在, 上拆分,并针对每个元素进行比较以找到匹配索引
    • 如果找不到匹配项,则不输出
    • exit 用于读取第一行后立即退出

    【讨论】:

      【解决方案6】:
      $ awk '{print (index($0,"e")+1)/2}' file
      5
      

      【讨论】:

      • 假设数据看起来像示例 - 每个字段一个字符,只有一行。不是一般的解决方案。
      • 绝对正确,它解决了 OP 问题,就像我们所有的解决方案一样。例如,如果目标字段名称包含 RE 元字符,您的将失败。那么为什么要投反对票呢?
      猜你喜欢
      • 1970-01-01
      • 2012-05-30
      • 1970-01-01
      • 2022-10-15
      • 1970-01-01
      • 2017-04-09
      • 1970-01-01
      • 2015-03-25
      • 1970-01-01
      相关资源
      最近更新 更多