根据另一个文件中的数字从文件夹中的文本文件中提取行

时间:2013-05-25 09:44:26

标签: bash sed awk

我有一个文件ff.txt,如下所示

*ABNA.txt
 356
 24
 36
 112
*AC24.txt
 457
 458
 321
 2

ABNA.txt和AC24.txt是名为foo1的文件夹中的文件。根据ff.txt文件中的数字,我想从foo1文件夹中的相应文件中提取行,并使用另一个文件夹foo2中的现有文件名创建新文件。 如果ABNA.txt文件的第三列或第四列包含356,24,36,112个数字,请提取该行并将其另存为另一个文件夹foo2,如ABNA.txt。

文件夹foo1中的ABNA.txt文件如下所示

dfg qza 356 245
hjb hkg 455 24
ghf qza 12  123
dfg qza 36  55

文件夹foo1中的AC24.txt文件如下所示

hjb hkg 457 167
ghf qza  2  165
sar sar 234 321
dfg qza 345 345

输出:

文件夹foo2中的ABNA.txt文件

dfg qza 356 245
hjb hkg 455 24
dfg qza 36  55

文件夹foo2中的AC24.txt文件

hjb hkg 457 167
ghf qza  2  165
sar sar 234 321

你的帮助将不胜感激!

4 个答案:

答案 0 :(得分:1)

<强>已更新

这是一个纯粹的bash解决方案(grep已删除):

#!/bin/bash

file=
s=()

grp() { r="${s[@]}";r="\b("${r// /|}")\b";
  while read w; do [[ $w =~ $r ]] && echo $w;done <foo1/$file >foo2/$file
}

while read a; do
  if [[ $a =~ ^\* ]]; then
     [ -n "$file" ] && grp
     file=${a#\*}
     s=()
  else s=(${s[@]} $a)
  fi
done < ff.txt
[ -n "$file" ] && grp

#See input and output files
for i in foo1/*;{ echo %% in $i; cat $i;}
for i in foo2/*;{ echo %% out $i; cat $i;}

输出

%% in foo1/ABNA.txt
dfg qza 356 245
hjb hkg 455 24
ghf qza 12  123
dfg qza 36  55
%% in foo1/AC24.txt
hjb hkg 457 167
ghf qza  2  165
sar sar 234 321
dfg qza 345 345
%% out foo2/ABNA.txt
dfg qza 356 245
hjb hkg 455 24
dfg qza 36  55
%% out foo2/AC24.txt
hjb hkg 457 167
ghf qza  2  165
sar sar 234 321

在while循环中,它解析ff.txt文件。如果一行以*开头,则设置file环境变量。如果不是以*开头,则它是一个数字并添加到s数组中。如果找到一个新的文件名并且有一个旧的文件名集,那么它会调用grp函数来完成实际的工作。

函数grp\b(num1|num2...)\b格式创建正则表达式。 \b仅匹配完整的数字。因此\b24\b245不匹配。 while循环从foo1读取文件,将每一行与正则表达式匹配,并将具有相同名称的文件写入目录foo2。它不检查是否存在foo2目录。

答案 1 :(得分:1)

这可能适合你(GNU sed和Bash):

folder1=foo1
folder2=foo2
sed -r '/^\*/!{s/\s*//g;H;$!d};1{h;d};x;s/\n/ /;s/\n/|/g;s#\*(.*) (.*)#<'"$folder1"'/\1 sed -nr '\''/^(\\S+\\s+){2,3}\\b(\2)\\b/w '"$folder2"'/\1'\''#' ff.txt | sh

这会将ff.txt文件转换为通过管道传递到sh命令的脚本。用户必须首先将bash变量$folder1$folder2分别设置到包含源文件和输出文件的目录中。

答案 2 :(得分:1)

你可以尝试这样的事情 -

awk '
BEGIN {
    readpath=sprintf("%s", "/path/to/foo1")
    writepath=sprintf("%s", "/path/to/foo2")
    }
$0~/\*/ {
    file = substr($1,2)
    while ((getline var < (readpath"/"file)) > 0) {
        split (var, a, " ")
        ary[a[3]]=var
        ary[a[4]]=var
        }
    }
($1 in ary) {
    print ary[$1] > (writepath"/"file)
    }' foo.txt

阐释:

  • BEGIN 语句中设置读取路径和写入路径。
  • 对于 foo.txt 文件
  • 中包含文件名的行
  • 使用 substr 捕获名为 file
  • 的变量中的文件名
  • 在名为 var 的变量中读取文件。
  • 拆分变量 var 以使用第3列和第4列作为数组 ary 的索引。
  • 来自 foo.txt 文件,如果数组中存在第一列,则将其写入文件。

测试:

[jaypal:~/temp/test] ls
foo.txt foo1    foo2

[jaypal:~/temp/test] cat foo.txt
*ABNA.txt
356
24
36
112
*AC24.txt
457
458
321
2

[jaypal:~/temp/test] ls foo1/
ABNA.txt AC24.txt

[jaypal:~/temp/test] head foo1/*
==> foo1/ABNA.txt <==
dfg qza 356 245
hjb hkg 455 24
ghf qza 12  123
dfg qza 36  55

==> foo1/AC24.txt <==
hjb hkg 457 167
ghf qza  2  165
sar sar 234 321
dfg qza 345 345

[jaypal:~/temp/test] ls foo2/
[jaypal:~/temp/test] 

[jaypal:~/temp/test] awk '
BEGIN {
    readpath=sprintf("%s", "./foo1")
    writepath=sprintf("%s", "./foo2")
    }
$0~/\*/ {
    file = substr($1,2)
    while ((getline var < (readpath"/"file)) > 0) {
        split (var, a, " ")
        ary[a[3]]=var
        ary[a[4]]=var
        }
    }
($1 in ary) {
    print ary[$1] > (writepath"/"file)
    }' foo.txt

[jaypal:~/temp/test] ls foo2/
ABNA.txt AC24.txt

[jaypal:~/temp/test] head foo2/*
==> foo2/ABNA.txt <==
dfg qza 356 245
hjb hkg 455 24
dfg qza 36  55

==> foo2/AC24.txt <==
hjb hkg 457 167
sar sar 234 321
ghf qza  2  165

答案 3 :(得分:1)

#!/bin/bash
mkdir -p foo2
awk '
    function process_file(filename, values,     filein, fileout, line, f) {
        if (filename == "") return
        filein = "./foo1/" filename
        fileout = "./foo2/" filename
        while ((getline line < filein) > 0) {
            split(line, f)
            if (f[3] in values || f[4] in values) {
                print line > fileout
            } 
        }
    }

    /^\*/ {
        process_file(filename, values)
        filename = substr($0, 2)
        delete values
        next
    }
    { values[$1] }
    END { process_file(filename, values) }
' ff.txt