【问题标题】:What defines a "column" in bash? In awk?什么定义了 bash 中的“列”?在 awk?
【发布时间】:2015-10-05 02:14:19
【问题描述】:

我正在查看这个问题:Bash - Take nth column in a text file

我想创建一个写入文本文件的函数,然后我可以使用上述方法对其进行解析。因此,例如,我希望我的函数在第一列中写入“日期”,在第二列中写入“ID”,在第三列中写入“地址”。然后,一旦我有了这个,例如,用户可以通过查询第二列来查看文件中是否存在某个 ID,然后查看那里的每个项目。用户可以使用上述问题中讨论的方法来执行此操作。

什么定义了列?它只是一个空格分隔符吗?是标签吗?

如果我想按上述方式输出此信息,我写入文件的方法会是什么样子?到目前为止,我有:

cat "$DATE $ID $ADDRESS \n" > myfile.data

【问题讨论】:

  • awk 不是 bash,awk 关于什么是或不是列的想法与 bash 无关。如果您从 C 程序、Python 程序或任何其他非 shell 程序中调用列,awk 对列的概念仍然相同。
  • 因此,这个问题应该询问有关 bash 的问题,或者应该询问有关 awk 的问题,但不应将这两个主题混为一谈,似乎同时询问两者。
  • 顺便说一句,cat "$DATE $ID $ADDRESS \n" 试图读取一个由$DATE $ID $ADDRESS \n 扩展命名的文件;当然,这不是你想要的。
  • (另外,你不应该在shell脚本中使用全大写的变量名;shell变量与环境变量共享一个命名空间,环境变量名的指导见@987654322第四段@)
  • 要明确术语:bash 中没有列,awk 中也没有。在 bash 和 awk 中都有 FIELDS,这就是为什么 bash 有输入字段分隔符 (IFS) 变量而 awk 有输入(这次小 i 不是变量名的一部分)字段分隔符 (FS)和输出字段分隔符 (OFS)。顺便说一句,不要在您引用的问题中使用公认的答案,因为其中的 bash 和 shell 解决方案都是完全错误的。

标签: bash awk cat


【解决方案1】:

如果我们谈论的是awk,那么空格字符就是默认的列分隔符。

awk 解析文件时,很容易更改用作“字段分隔符”(FS) 的内容:awk '{FS=",";print $2}'。将使用逗号作为分隔符(注意:不尊重引号和诸如 csv 解析器之类的东西)。

要写入文件,我会使用echo 和双胡萝卜>>

>> 追加,而> 重写文件。 echo -e 会让 echo 识别 \n 和类似的特殊字符

所以命令是

echo -e "$DATE $ID $ADDRESS \n" >> myfile.data

【讨论】:

  • 我认为echo -e 在这里是个坏建议——如果值包含反斜杠文字怎么办?此外,任何支持-eecho 实现都违反了pubs.opengroup.org/onlinepubs/009604599/utilities/echo.html 给出的POSIX 规范(不是扩展它,违反它;符合POSIX 的echo在其命令行上给定-e 必须在其输出上打印-e)。
  • ...如果在环境中设置了POSIXLY_CORRECT=1,即使是 GNU echo 也会如此。
  • 另外,echo 已经在每行输出后放置了一个换行符;你确定两个换行符(一个显式,一个隐式)是期望的行为吗?
  • (另外,在单个命令上使用>> 写入一行会打开文件,写入该行,然后关闭文件;除非您要写的只有一行,否则它是效率极低)。
【解决方案2】:

如果您要使用awk,则列由字段分隔符 分隔。详情请参阅man awk 中的FS

大多数工具都支持更改列分隔符的一些方法:

cut -f
sort -t

bash 本身使用IFS 变量(内部字段分隔符)进行分词。

cat 等待文件作为参数。要输出字符串,请改用echo

【讨论】:

    【解决方案3】:

    bash 中,与 awk 不同,列由 IFS 中的字符分隔。

    也就是说,如果你设置:

    IFS=$'\t'
    

    ...然后列,如read first second rest 等bash 内置函数所理解的,将由制表符分隔。在输出端,printf '%s\n' "${array[*]}" 将打印数组array 中的项目,由IFS 的第一个字符分隔。

    IFS 的默认值等同于$' \t\n' -- 即空格、制表符和换行符。


    要编写一个带有您选择的分隔符的文件,并且(可能)不止一行(将while read 替换为您实际上正在获取数据的内容,或者仅使用循环内部,如果您是只写一行):

    while read -r date id address; do
      printf '%s\t' "$date" "$id" "$address" >&3; printf '\n' >&3
    done 3>filename
    

    ...或者,如果您不希望上面留下尾随标签:

    IFS=$'\t' # use a tab as the field separator for output
    while IFS=$' \t\n' read -r date id address; do
      entry=( "$date" "$id" "$address" )
      printf '%s\n' "${entry[*]}" >&3
    done 3>filename
    

    3>filename 放在循环的外部比在每一行上的>>filename 更有效,每行写入一次重新打开输出文件。

    【讨论】:

      猜你喜欢
      • 2010-11-20
      • 1970-01-01
      • 2020-10-03
      • 2012-08-01
      • 1970-01-01
      • 2019-07-03
      • 1970-01-01
      • 1970-01-01
      • 2017-03-05
      相关资源
      最近更新 更多