首页 > 解决方案 > 使用 bash 在特定列中提取具有特定值的行

问题描述

我有 1000 个文本文件,每个文件都以制表符分隔,格式如下

John    32     NY     12     USA
Peter   78.    CA.    8.     USA
Stef.   67.    CA.    12.    USA

我想提取所有第四列正好是 12 的行。这就是我所做的:


file='random'

FILES=/home/user/data/*.txt
for f in $FILES; 
do 
echo $f
filename=$(basename $f)
awk -F"\t" '$4 == 12' $f >  /home/user/extra/$file/$filename; 
done

但这会产生空文件,我不确定我在这里做错了什么。见解将不胜感激。

标签: bashawk

解决方案


请阅读正确的 Bash 和 shell 脚本变量大写https://mywiki.wooledge.org/Quotes以了解脚本中的一些问题,并将您编写的任何 shell 脚本复制/粘贴到https://www.shellcheck.net/直到你把基本面弄下来。

关于But this produces empty files- 当然,对于任何给出的cmd命令

for f in *; do
    cmd "$f" > "out$f"
done

$4==12您正在为 shell 循环中的每个输入文件创建一个输出文件,因此如果任何输入文件在您的 awk 脚本中不匹配(cmd在这种情况下),您仍然会得到一个输出文件,它只是空的。如果你不希望你可以这样做:

tmp=$(mktemp)
for f in *; do
    cmd "$f" > "$tmp" &&
    mv -- "$tmp" "out$f"
done

并写入cmd以成功/失败状态退出,就像grep找到匹配项时一样(在 awk 中微不足道),或者您可以检查"$tmp"之前的大小mv

tmp=$(mktemp)
for f in *; do
    cmd "$f" > "$tmp" &&
    [[ -s "$tmp" ]] &&
    mv -- "$tmp" "out$f"
done

但是,您不需要 shell 循环或其他命令,只需 1 次调用 awk 即可一次处理所有文件。在每个 Unix 机器上的任何 shell 中使用任何 awk只做这个

awk -v file='random' -F'\t' '
    FNR == 1 {
        close(out)
        f = FILENAME
        sub(".*/","",f)
        out = "/home/user/extra/" file "/" f
    }
    $4 == 12 {
        print > out
    }
' /home/user/data/*.txt

如果你想要一个字符串而不是数字比较,那么它12.不匹配,12然后做$4 == "12"而不是$4 == 12.

在上面file是一个糟糕的变量名选择来保存一个目录的名字,但我把它放在一边以避免改变我不需要做的任何事情。


推荐阅读