localize e imprima uma linha em particular da lista de nome de arquivo e número de linha

2

Eu tenho um arquivo input.txt que contém vários nomes de arquivos no formato abaixo. FILENAME_DATE_LINENUMBER , o input.txt contém muitos desses nomes de arquivos. O nome do arquivo em si tem precisamente 5 sublinhado .

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

Todos os arquivos estão presentes no subdiretório como input.txt . Eu quero analisar input.txt , percorrer cada nome de arquivo e imprimir FILENAME e o número de linha especificado (do FILENAME) para output.txt

Eu sou iniciante no shell script e entendo que sed ou awk serão usados, e o comando abaixo pode fazer o trabalho.

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

Mas como posso iterar pelo arquivo input.txt e encontrar o FILENAME e extrair o LINENUMBER de FILENAME para output.txt

O FILENAME especificado em input.txt pode em um dos subdiretórios em que input.txt está localizado. Pode haver apenas um arquivo com FILENAME em input.txt dentro de um dos subdiretórios (um nível) da input.txt location.

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

Em output.txt , deve ser impresso como

FILENAME
LINE( Extracted from FILENAME present in input.txt )
    
por shubham deodia 23.01.2018 / 18:36

4 respostas

0
#!/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/.*_// =}'

Saída:

FILE_NAME_1.DAT
LINE(103)
FILE_NAME_2.DAT
LINE(104)
FILE_NAME_3.DAT
LINE(105)
FILE_NAME_4.DAT
LINE(106)
    
por 31.01.2018 / 16:55
0
:> awk -F_ '{ print $1; print $3; }' inputfile
FILE1.DAT
4
FILE2.DAT
5
FILE3.DAT
6
FILE4.DAT
7
    
por 23.01.2018 / 18:51
0

Se eu estou entendendo você corretamente,

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

Isto lê uma linha de cada vez a partir de input.txt, dividindo a linha em 3 partes com base no sublinhado. Ele imprime o nome do arquivo, em seguida, dispara um comando sed que (por padrão, imprime nada) e, em seguida, no número de linha especificado, imprime a linha e encerra a invocação de sed. A localização do nome do arquivo deve estar em um dos subdiretórios imediatos do diretório atual.

Toda a saída é redirecionada para o output.txt.

    
por 23.01.2018 / 18:54
0

Solução complexa com o GNU parallel + find + awk :

Digamos que cada arquivo de entrada tenha um conteúdo semelhante ao seguinte:

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

Assim, pelo esquema acima, a segunda linha no arquivo FILE2.DAT_20180123_5 seria FILE2 b e a sétima linha no arquivo FILE4.DAT_20180123_7 - FILE4 g . Suponha que o arquivo input.txt seja o mesmo da pergunta.

O trabalho:

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

O conteúdo final de output.txt :

cat DIR1/FILE1.DAT_20180123_4
FILE1 a 
FILE1 b 
FILE1 c 
FILE1 d 
FILE1 e 
FILE1 f 
FILE1 g
    
por 23.01.2018 / 20:32