使用 bash 在特定列中提取具有特定值的行

extract rows with specific value in a specific column using 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" ' == 12' $f >  /home/user/extra/$file/$filename; 
done

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

你可以试试这个awk

awk -v file="/home/user/extra/$file/$filename" '==12 {print > file}' "$f"

请阅读 Correct Bash and shell script variable capitalization and https://mywiki.wooledge.org/Quotes to understand some of the issues in your script and copy/paste any shell script you write into https://www.shellcheck.net/ 直到掌握基本原理。

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

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

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

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

并写入 cmd 以 succ/fail 状态退出,就像 grep 在找到匹配项时所做的那样(在 awk 中微不足道),或者您可以检查 [=20 的大小=] 在 mv:

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

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

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

如果您想要字符串而不是数字比较,以便 12.12 不匹配,则执行 == "12" 而不是 == 12

在上面 file 是一个糟糕的变量名选择来保存 目录 的名称,但我把它放在一边以避免更改任何我没有更改的内容不得不。