Localização e conteúdo do arquivo de saída com base na lista de arquivos

5

Eu tenho um arquivo que contém locais de diferentes arquivos chamados locationfile.txt que ele contém:

/home/user/documents/file1
/home/user/file2
/home/user/file3

Cada arquivo de texto tem cerca de 10 linhas e eu quero enviar todos os arquivos para outro arquivo chamado finalfile .

Eu descobri que se eu fizer

cat locationfile.txt | while read line; do cat "$line"; done

Isso imprime todos os arquivos juntos para que se torne um arquivo de texto de ~ 30 linhas.

Qual é a minha pergunta, como posso cuspir esse arquivo incluir os locais dos arquivos entre o texto assim:

/home/user/documents/file1
text
text
text
""
/home/user/file2
text
text
text
""
/home/user/file3
text
text
text
""
    
por user211145 18.01.2017 / 16:39

6 respostas

8

Antes do cat fazer um echo ou printf do nome do arquivo:

while read line; do printf '%s\n' "$line"; cat "$line"; done <locationfile.txt >finalfile

Ou mais legível:

while read line; do
  printf '%s\n' "$line"
  cat "$line"
done <locationfile.txt >finalfile

Observe que isso exige que todos os caminhos em locationfile.txt não tenham \ neles.

Embora ter \ em nomes de caminho seja altamente incomum, seria mais seguro fazer

while read -r line; do
  printf '%s\n' "$line"
  cat "$line"
done <locationfile.txt >finalfile

... que não tem as mesmas restrições.

Adicionando uma verificação para garantir que o arquivo também exista e emitindo um aviso, caso contrário:

while read -r line; do
  if [[ -f "$line" ]]; then
    printf '%s\n' "$line"
    cat "$line"
  else
    printf 'Warning: "%s" does not exist\n' "$line" >&2
  fi
done <locationfile.txt >finalfile

"Não existe" aqui significa "pelo menos não é um arquivo regular".

Se você quiser também o "" , como uma espécie de marcador de conteúdo de final de arquivo, basta colocar isso em branco depois de cat :

while read -r line; do
  if [[ -f "$line" ]]; then
    printf '%s\n' "$line"
    cat "$line"
    echo '""'
  else
    printf 'Warning: "%s" does not exist\n' "$line" >&2
  fi
done <locationfile.txt >finalfile

E, finalmente, dê nomes de autodocumentação:

while read -r file_path; do
  if [[ -f "$file_path" ]]; then
    printf '%s\n' "$file_path"
    cat "$file_path"
    echo '""'
  else
    printf 'Warning: "%s" does not exist\n' "$file_path" >&2
  fi
done <file_paths.txt >files_concat_with_paths.txt
    
por 18.01.2017 / 17:00
3
while read file; do
    ( echo "$file"; cat "$file"; echo '"""' ) >> /path/to/outputfile
done < /path/to/filelist
    
por 18.01.2017 / 17:01
3

Aqui está uma opção se você tiver o GNU awk, que evita completamente os loops de shell:

xargs -d '\n' -a locationfile.txt gawk 'BEGINFILE{print FILENAME} 1' > newfile

ou com o GNU sed:

xargs -d '\n' -a locationfile.txt sed -s '1F' > newfile

Se você não se importa com o formato exato dos nomes de arquivos, então você pode usar este truque com head :

xargs -d '\n' -a locationfile.txt head -vn -0 > newfile

(o -n -0 informa head para a saída de todas as linhas, exceto o primeiro 0, ou seja, todas as linhas).

    
por 18.01.2017 / 17:34
3

Outra abordagem Perl:

$ perl -le 'do{print $_,'cat $_',"\"\"";} for <>' locationfile.txt
text
text
text
""
text
text
text
""
text
text
text
""

Isso está usando um pouco da magia Perl e pode precisar de uma explicação. O -l remove as novas linhas à direita de cada linha de entrada e adiciona uma nova linha a cada chamada print . O -e é como você fornece um script para perl .

Agora, <> é "o conteúdo do arquivo de entrada dividido em uma matriz". Portanto, do {foo} for <> executará foo em cada linha do arquivo de entrada. Dentro desse for loop, a variável especial $_ é atribuída a cada linha de entrada. A sintaxe 'command' executará command no shell padrão (uma chamada do sistema) e retornará sua saída. Portanto, cat $_ irá cat de cada nome de arquivo em locationfile.txt .

Completamente, print $_,'cat $_',"\"\"" imprimirá o nome do arquivo (atualmente em $_ ), o resultado de cat (o conteúdo do arquivo) e, em seguida, "" (mas precisamos escapar deles, portanto \"\" ).

Em outras palavras, esse script significa "ler cada linha de entrada, imprimi-la, cat do arquivo que ela contém e imprimir um "" após cada arquivo.

    
por 18.01.2017 / 18:55
2

Você pode adicionar apenas echo "$line" ao seu loop:

cat locationfile.txt | while read line; do echo "$line" && cat "$line"; done
    
por 18.01.2017 / 17:00
2

Aproximação do AWK via função system() . Nós criamos a string de comando via sprintf e passamos para system()

$ awk '{print $0; cmd=sprintf("cat \"%s\"",$0);system(cmd); print "\"\""}' filenames.txt                                 
/home/xieerqi/input1.txt
Hello, I am input file #1
And this is second line of the text
""
/home/xieerqi/input2.txt
And I am input file #2
Roses are red, violets are blue
AWK is awesome, Python is too
""
/etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 127.0.1.1
""

E a abordagem Perl, mas com o arquivo de abertura dentro do código:

$ perl -ne 'chomp; open($fh,$_) or die;print "$_\n";while($line = <$fh>){ print $line; print "\"\"\n" if eof}' filenames>
/home/xieerqi/input1.txt
Hello, I am input file #1
And this is second line of the text
""
/home/xieerqi/input2.txt
And I am input file #2
Roses are red, violets are blue
AWK is awesome, Python is too
""
/etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 127.0.1.1
""
    
por 18.01.2017 / 18:11

Tags