【问题标题】:Filtering using awk returns empty files使用 awk 过滤返回空文件
【发布时间】:2020-09-29 17:39:39
【问题描述】:

我也有类似的问题:How to do filtering of multiple files in a directory using awk?

上述问题答案中的解决方案对我不起作用。

我有制表符分隔的 txt 文件(都在文件夹 Observation_by_pracid 中)。对于每个文件,我想创建一个新文件,该文件仅包含列 $9 (medcodeid) 中具有特定值的行。具体值可在 medicalcode_list.txt 中找到。 没有错误,但是它只返回空文件。

编码列表

medcodeid   
2576        
3199    

输入文件的格式

patid           consid      ... medcodeid   
500470520002    3062539302  ... 2576    
951924020002    3062538414  ... 310803013       
503478020002    3061587464  ... 257619018       
951924020002    3062537807  ... 55627011        
503576720002    3062537720  ... 3199    

期望的输出

patid           consid      ... medcodeid       
500470520002    3062539302  ... 2576    
503576720002    3062537720  ... 3199

我的代码

mkdir HBA1C_observation_bypracid
awk '
    NR==FNR {mlist[$1]; next }
    FNR==1 {close(out); out="HBA1C_observation_bypracid/HBA1C_" FILENAME }
    ($9 in mlist) { print > out } 
' PATH/medicalcode_list.txt *.txt

解决方案

mkdir HBA1C_observation_bypracid
awk '
    BEGIN{ FS=OFS="\t" }
    NR==FNR {mlist[$1]; next }
    FNR==1 {close(out); out="HBA1C_observation_bypracid/HBA1C_" FILENAME }
    ($9 in mlist) { print > out } 
' PATH/medicalcode_list.txt *.txt

添加“BEGIN...”解决了我的问题。

【问题讨论】:

  • 没有。我只需要最有效的方法,因为需要处理大量数据。
  • 如果将 $9 替换为 $NF,会得到什么?
  • 您很可能在codelist 或您的输入文件(但不是两者)中遇到CRLF 行尾。这意味着您尝试将2576\r2576 匹配,但这是行不通的。查看this question 了解更多信息。您可以通过cat -vET file 快速检查您的文件,看看那里是否有意外情况。
  • 或更简单:只需输入 file filename 这将为您提供信息
  • 两个文件都是 ASCI。行尾显示为 ^I

标签: awk


【解决方案1】:

您可以使用join 将列中的两个文件连接起来。

  • 文件必须按连接列排序。要对列执行数字 sort,请使用这种方式排序,其中 N 是列号:

      sort -kN -n FILE
    
  • 您还需要了解每个文件的第一行(列名)。您可以按以下方式使用 tail 命令,其中 N 是您要输出内容的行号(即第 2 行):

    tail -n +N
    
  • ...但是还是需要显示列值:

    head -n 1 FILE
    
  • 要连接两个文件 f1 和 f2,在 f1 的 c1 和 f2 的 c2 字段上,并输出文件 x 的字段 y:

    join -1 c1 -2 c2 f1 f2 -o "x.y, x.y"
    

工作样本:

head -n 1 input_file
for input_file in *.txt ; do
    join -1 1 -2 9 -o "2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9" \
         <(tail -n +2 PATH/medicalcode_list.txt | sort -k1 -n) \
         <(tail -n +2 "$input_file" | sort -k3 -n)
done

结果(对于您提供的输入文件):

patid           consid      ... medcodeid   
500470520002 3062539302 ... 2576
503576720002 3062537720 ... 3199

注意:列名与值不一致。不知道是不是先决条件。您可以使用printf 命令格式化显示。

【讨论】:

  • 感谢您的想法!这运行没有错误,但是控制台中既没有任何输出文件也没有输出。我错过了什么?我也不确定过滤在您的代码中的确切位置。我只是从 awk 开始
  • 我仍然无法运行它。但是,我找到了解决方案。我必须将 BEGIN{ FS=OFS="\t" } 添加到我的代码中。非常感谢您的努力!
【解决方案2】:

我个人认为在 shell 中循环会更简单(理解这将多次重读代码列表),使用更简单的 awk 函数,您应该能够测试和调试。比如:

for file in *.txt; do
    awk 'FNR == NR { mlist[$1] } FNR != NR && ($9 in mlist) { print }' \
       PATH/medicalcode_list.txt "$file" > HBA1C_observation_bypracid/HBA1C_"$file"
done

您应该能够在没有重定向的情况下启动,以确保对于单个文件,您可以将结果打印到预期的终端。如果您不这样做,则可能对文件有一些不正确的假设。

另一种选择是编写一个单独的 awk 脚本,该脚本编写代码以将列表硬编码到另一个 awk 脚本中。还可以检查变量mlist 的内容。

printf 'BEGIN {\n%s\n}\n $9 in mlist { print }' \
    "$(awk '{ print "mlist[" $1 "]" }' PATH/medicalcode_list.txt)" > filter.awk

for file in *.txt; do
   awk -f filter.awk "$file" > HBA1C_observation_bypracid/HBA1C_"$file"
done

【讨论】:

  • 非常感谢您的想法。您的第一种方法产生了与以前相同的输出:我得到了一个空的输出文件。 “硬编码列表”如下所示: cat filter.awk BEGIN { mlist[medcodeid] mlist[2576] mlist[3199] } 但是, awk -f filter.awk "$file" > HBA1C_observation_bypracid/HBA1C_ 的输出$file" 是一个没有标题的输出文件,并且带有来自源文件的随机行,而不是应该被过滤的行
  • 基于此,在我看来,您认为第 9 列与 awk 认为的不一样。我会从小处着手,只使用最简单的 awk 脚本来查看 awk 在不同列中的真正想法。从awk '{ print $1 }' 开始,然后是$2,等等。您还评论说行尾是^I。那会很奇怪,因为这通常代表一个制表符,这也是您所说的分隔符。正如另一位评论者所说,您需要确保您也有 unix 行尾。
  • 谢谢!这是一个重要的提示。你是对的 - 输入文件未被识别(制表位/空单元格被折叠)。如果我添加 BEGIN{ FS=OFS="\t" } ,我的代码和您的代码都可以完美运行。
猜你喜欢
  • 1970-01-01
  • 2018-02-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-22
  • 2014-12-10
  • 1970-01-01
  • 2021-05-08
  • 1970-01-01
相关资源
最近更新 更多