【问题标题】:How to ignore white space and comma when reading from a file从文件读取时如何忽略空格和逗号
【发布时间】:2016-01-27 18:09:56
【问题描述】:

我有一个逗号分隔的文件,我需要从每一行中提取第三个字段。文件 test.txt 有以下内容:

6,STRING TO DECIMAL WITHOUT DEFAULT,cast($src_fld as DECIMAL(15,2) $tgt_fld 
7,STRING TO INTERGER WITHOUT DEFAULT,cast($src_fld as integer) $tgt_fld                
10,DEFAULT NO RULE,'$default' $tgt_fld

猫测试.txt | awk -F, '{打印 $3}'

如果我写上面的命令,我得到一个不正确的输出:

> cast($src_fld as DECIMAL(15
> cast($src_fld as integer) $tgt_fld
> '$default' $tgt_fld

谁能告诉我如何实现它。我需要将它写在一个循环中,以便以后可以进行进一步的处理。请注意,每个第三个字段可能包含空格和逗号(,)。

【问题讨论】:

  • 我不明白。 awk 是否逐行遍历文件?
  • 我现在已经用正确的输出编辑了我的问题。
  • 不,你没有。所有问题显示的是你得到你不想要的输出,而不是你想要的输出。

标签: shell unix awk ksh


【解决方案1】:

如果如您所说,前两个字段不包含逗号,您可以使用 cut 和逗号作为字段分隔符:

$ cut -d ',' -f 3- test.txt 
cast($src_fld as DECIMAL(15,2) $tgt_fld 
cast($src_fld as integer) $tgt_fld                
'$default' $tgt_fld

【讨论】:

  • 感谢您的回复。您能否告诉我如何在 for 循环中使用此命令。因为我需要在提取后处理第三个字段。如果我把它写成 for i in cut -d ',' -f 3- test.txt;do echo $i;done;,它不会提供正确的输出。
  • @user1768029 你可以用while IFS= read -r line; do echo $line; done < <(cut -d ',' -f 3- test.txt) 来循环它,但根据你想要做什么,它可能并不理想。
  • @user1768029,不要使用 shell 循环来操作文本(例如,参见 unix.stackexchange.com/q/169716/133219
【解决方案2】:

awk 来救援!

不是通用解决方案,但适用于您的格式

$ awk -F, '{for(i=4;i<=NF;i++) $3 = $3 FS $i} {print $3}' badcsv

cast($src_fld as DECIMAL(15,2) $tgt_fld
cast($src_fld as integer) $tgt_fld
'$default' $tgt_fld

说明您正在打印基于 FS="," 的第二个字段之后的文本部分。该脚本在打印前将其余字段附加到 $3 上。

【讨论】:

    【解决方案3】:

    如果前两个字段中有逗号,则您的任务是不可能的。

    1,second,field,with,commas,third,field,with,commas
    

    您无法知道第二个字段的结束位置和第三个字段的开始位置。

    您确实必须使用实际的 CSV 语法,并使用 CSV 解析器解析文件。

    1,"second,field,with,commas","third,field,with,commas"
    

    如果您可以确定前两个字段中没有逗号,您可以这样做:

    sed 's/^[^,]\+,[^,]\+,//' file
    

    【讨论】:

    • 感谢您的快速回复。我确信前两个字段中不会有任何逗号。你能解释一下上面的 sed 命令是如何工作的吗?我可以从上面的命令中只提取第三个字段吗?
    • 它删除前两个逗号分隔的字段,只留下第三个。 [^,]\+ 是一个或多个非逗号字符。
    【解决方案4】:

    当你想使用循环时,你可以使用

    while IFS=, read -r field1 field2 rest_of_line; do
       echo "Field 3: ${rest_of_line}" 
    done < test.txt
    

    【讨论】:

      【解决方案5】:

      你没有告诉我们正确的输出是什么,只是它不是什么,所以这是你可能想要的猜测,但如果不是这样,你应该能够从中找出你需要什么完全正确:

      $ cat tst.awk
      BEGIN { FS="," }
      {
          $0 = gensub(/([(][^()]+),([^()]+[)])/,"\\1"RS"\\2","g",$0)
          for (i=1; i<=NF; i++) {
              gsub(RS,FS,$i)
              print NR, NF, i, $i
          }
          print "----"
      }
      
      $ awk -f tst.awk file
      1 3 1 6
      1 3 2 STRING TO DECIMAL WITHOUT DEFAULT
      1 3 3 cast($src_fld as DECIMAL(15,2) $tgt_fld
      ----
      2 3 1 7
      2 3 2 STRING TO INTERGER WITHOUT DEFAULT
      2 3 3 cast($src_fld as integer) $tgt_fld
      ----
      3 3 1 10
      3 3 2 DEFAULT NO RULE
      3 3 3 '$default' $tgt_fld
      ----
      

      上面的 gensub() 使用 GNU awk,其他 awk 使用 match()+substr()。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-04-19
        • 1970-01-01
        • 1970-01-01
        • 2013-04-28
        • 1970-01-01
        • 1970-01-01
        • 2022-06-13
        相关资源
        最近更新 更多