ファイル名と行番号のリストから特定の行を見つけて印刷します。

ファイル名と行番号のリストから特定の行を見つけて印刷します。

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

すべてのファイルはで始まります。解析して各ファイル名を繰り返し、FILENAMEと指定された行番号(FILENAME)を印刷したいとinput.txt思います。input.txtoutput.txt

私はsedまたはawkを使用し、次のコマンドがアクションを実行することを知っています。

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

しかし、どのようにファイルを繰り返しinput.txtてFILENAMEを見つけ、FILENAMEからLINENUMBERを抽出するかoutput.txt

指定されたFILENAMEはinput.txtサブディレクトリの1つにありますinput.txt。この場所のサブディレクトリの1つ(1レベル)内のinput.txtにFILENAMEを持つファイルは1つしかありません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で一度に1行ずつ読み、アンダースコアに基づいて行を3つの部分に分割します。ファイル名を印刷してsedコマンドをトリガーし(デフォルトでは何も印刷しない)、指定された行番号に行を印刷してsedへの呼び出しを終了します。ファイル名の場所は、現在のディレクトリのすぐ下のディレクトリの1つになければなりません。

これにより、すべての出力が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

したがって、上記のスキームを使用すると、ファイルの2行目にFILE2.DAT_20180123_5なりFILE2 b、ファイルの7行目はFILE4.DAT_20180123_7-になりますFILE4 ginput.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

関連情報