从文件名和行号列表中查找并打印特定行

从文件名和行号列表中查找并打印特定行

我有一个文件input.txt,其中包含以下格式的多个文件名。 FILENAME_DATE_LINENUMBERinput.txt包含许多这样的文件名。 文件名本身恰好有 5 个下划线

FILE_NAME_1.DAT_20180123_4
FILE_NAME_2.DAT_20180123_5
FILE_NAME_3.DAT_20180123_6
FILE_NAME_4.DAT_20180123_7

所有文件都以input.txt.我想解析input.txt、迭代每个文件名并打印 FILENAME 和指定的行号(从 FILENAME )到output.txt

我知道将使用 sed 或 awk ,下面的命令可以完成这项工作。

awk 'FNR==LINENUMBER {print FILENAME, $0}' *.txt >output.txt

但是我如何遍历文件input.txt并找到 FILENAME 并从 FILENAME 中提取 LINENUMBER 到output.txt

指定的 FILENAMEinput.txt可以位于所在的子目录之一input.txt。该位置的子目录之一(一级)内的 input.txt 中只能有一个具有 FILENAME 的文件input.txt

DIR
├── input.txt
│   ├── DIR1
│   │   ├── FILE_NAME_1.DAT
│   ├── DIR2
│   │   ├── FILE_NAME_2.DAT
│   ├── DIR3
│   │   ├── FILE_NAME_3.DAT

其中output.txt应打印为

FILENAME
LINE( Extracted from FILENAME present in input.txt )

答案1

#!/bin/bash                                                                                   

do_one() {
    # two args: $1=filename_no_dir $2=line_number                                             
    # Find the single filename                                                                
    eval file=*"/$1"
    echo $1
    # $. == line number                                                                       
    perl -ne 'chomp; $.=='"$2"' and print "LINE($_)\n"' $file
}
export -f do_one

# Generate som test data                                                                      
parallel 'mkdir DIR{}; seq 100 110 >DIR{}/FILE_NAME_{}.DAT' ::: {1..4}

# Test input.txt                                                                              
cat <<EOF |                                                                                   
FILE_NAME_1.DAT_20180123_4                                                                    
FILE_NAME_2.DAT_20180123_5                                                                    
FILE_NAME_3.DAT_20180123_6                                                                    
FILE_NAME_4.DAT_20180123_7                                                                    
EOF                                                                                           
  # Remove _YYYYMMDD.* to get filename, and .*_ to get line number                            
  parallel do_one '{= s/_201\d\d\d\d\d.*// =}' '{= s/.*_// =}'

输出:

FILE_NAME_1.DAT
LINE(103)
FILE_NAME_2.DAT
LINE(104)
FILE_NAME_3.DAT
LINE(105)
FILE_NAME_4.DAT
LINE(106)

答案2

:> awk -F_ '{ print $1; print $3; }' inputfile
FILE1.DAT
4
FILE2.DAT
5
FILE3.DAT
6
FILE4.DAT
7

答案3

如果我理解正确的话

while IFS=_ read -r filename unuseddate linenum
do
  printf "%s\n" "$filename"
  sed -n "${linenum}{p;q}" */"$filename"
done < input.txt > output.txt

这会从 input.txt 中一次读取一行,并根据下划线将该行分成 3 部分。它打印文件名,然后触发 sed 命令(默认情况下不打印任何内容),然后在指定的行号上打印该行并退出 sed 的调用。文件名的位置应位于当前目录的直接子目录之一中。

然后所有输出都会重定向到output.txt。

答案4

GNU 的复杂解决方案parallel+find+awk:

假设每个输入文件的内容类似于以下内容:

cat DIR1/FILE1.DAT_20180123_4
FILE1 a 
FILE1 b 
FILE1 c 
FILE1 d 
FILE1 e 
FILE1 f 
FILE1 g

因此,通过上述方案,文件中的第二行将FILE2.DAT_20180123_5FILE2 b,文件中的第七行将是FILE4.DAT_20180123_7- FILE4 g。假设input.txt文件与问题中的文件相同。

工作:

find . -type f -regextype posix-egrep -regex ".*/($(paste -s -d'|' input.txt))" \
| parallel -j0 "awk -v n="{=s/.*_//=}" -v fn="{/}" \
               'NR==n{ print fn,\$0; exit }' {}" > output.txt

最终output.txt内容:

$ cat output.txt 
FILE4.DAT_20180123_7 FILE4 g
FILE3.DAT_20180123_6 FILE3 f 
FILE1.DAT_20180123_4 FILE1 d 
FILE2.DAT_20180123_5 FILE2 e

相关内容