Concatene vários arquivos, mas inclua o nome do arquivo como cabeçalhos de seção

352

Eu gostaria de concatenar uma série de arquivos de texto em um arquivo grande no terminal. Eu sei que posso fazer isso usando o comando cat. No entanto, gostaria que o nome do arquivo de cada arquivo preceda o "despejo de dados" desse arquivo. Alguém sabe como fazer isto?

o que eu tenho atualmente:

file1.txt = bluemoongoodbeer

file2.txt = awesomepossum

file3.txt = hownowbrowncow

cat file1.txt file2.txt file3.txt

saída desejada:

file1

bluemoongoodbeer

file2

awesomepossum

file3

hownowbrowncow
usuario
fonte
Na verdade, estou usando uuencode & uudecode fazer algo semelhante, eu não preciso do inbetween legível arquivo, só precisa do resultado e tubulação-los para outro comando novamente

Respostas:

531

Estava procurando a mesma coisa e encontrou isso para sugerir:

tail -n +1 file1.txt file2.txt file3.txt

Resultado:

==> file1.txt <==
<contents of file1.txt>

==> file2.txt <==
<contents of file2.txt>

==> file3.txt <==
<contents of file3.txt>

Se houver apenas um arquivo, o cabeçalho não será impresso. Se estiver usando utilitários GNU, você pode -vsempre imprimir um cabeçalho.

DS.
fonte
2
Isso funciona com o GNU tail (parte do GNU Coreutils) também.
ArjunShankar
7
-n +1Opção impressionante ! Uma alternativa: head -n-0 file1 file2 file3.
Frozen Flame
2
Funciona muito bem com a cauda BSD e GNU no MacOS X. Você pode deixar de fora o espaço entre -ne +1, como em -n+1.
VDM
4
tail -n +1 *foi exatamente o que eu estava procurando, obrigado!
kR105
11
funciona no MacOsX 10.14.4sudo ulimit -n 1024; find -f . -name "*.rb" | xargs tail -n+1 > ./source_ruby.txt
kolas
186

Eu usei grep para algo semelhante:

grep "" *.txt

Ele não fornece um 'cabeçalho', mas prefixa cada linha com o nome do arquivo.

Theo
fonte
10
A saída é interrompida se for *.txtexpandida para apenas um arquivo. A este respeito, eu aconselhogrep '' /dev/null *.txt
antak
11
+1 por me mostrar um novo uso do grep. isso atendeu perfeitamente minhas necessidades. no meu caso, cada arquivo continha apenas uma linha, por isso me deu uma saída perfeitamente formatado que foi facilmente parsable
verboze
9
grepimprimirá apenas os cabeçalhos dos arquivos se houver mais de um arquivo. Se quiser imprimir sempre o caminho do arquivo, use -H. Se você não quiser usar os cabeçalhos -h. Observe também que ele será impresso (standard input)para STDIN.
Jorge Bucaran
11
Você também pode usar ag(o pesquisador prateado): por padrão, ag . *.txtprefixa cada arquivo com seu nome e cada linha com seu número.
Anol
11
É importante notar que -na passagem para grep também gera números de linha que permitem escrever linhas simples com identificações que podem ser captadas por, por exemplo, emacs.
DepressedDaniel
104

Isso deve fazer o truque também:

find . -type f -print -exec cat {} \;

Significa:

find    = linux `find` command finds filenames, see `man find` for more info
.       = in current directory
-type f = only files, not directories
-print  = show found file
-exec   = additionally execute another linux command
cat     = linux `cat` command, see `man cat`, displays file contents
{}      = placeholder for the currently found filename
\;      = tell `find` command that it ends now here

Você também pode combinar pesquisas através de operadores booleanos como -andou -or. find -lsé legal também.

Maxim_united
fonte
11
Você poderia explicar mais o que esse comando faz? É exatamente o que eu precisava
AAlvz
4
Este é o comando find padrão do linux. Ele pesquisa todos os arquivos no diretório atual, imprime seu nome e, em seguida, para cada um, cria o arquivo. Se você omitir -print, o nome do arquivo não será impresso antes do cat.
precisa saber é o seguinte
17
Você também pode usar -printfpara personalizar a saída. Por exemplo: find *.conf -type f -printf '\n==> %p <==\n' -exec cat {} \;para corresponder à saída detail -n +1 *
Maxim_united
11
-printf não funciona no mac, a menos que você queira brew install findutilse use em gfindvez de find.
Matt Fletcher
11
Se você quiser cores que você pode usar o fato de que findpermite que vários -execs:find -name '*.conf' -exec printf '\n\e[33;1m%s\e[0m\n' {} \; -exec cat {} \;
banbh
24

Isso deve fazer o truque:

for filename in file1.txt file2.txt file3.txt; do
    echo "$filename"
    cat "$filename"
done > output.txt

ou faça isso para todos os arquivos de texto recursivamente:

find . -type f -name '*.txt' -print | while read filename; do
    echo "$filename"
    cat "$filename"
done > output.txt
Chris Eberle
fonte
11
não funcionou. Acabei de escrever um código awk realmente feio: para i em $ listoffiles do awk '{print FILENAME, $ 0, $ 1, $ 2, $ 2, $ 3, $ 4, $ 5, $ 6, $ 7, $ 7, $ 8, $ 9, $ 10, $ 11}' $ i >> concat.txt concluído
Nick
2
...cuidado ao elaborar? Isso é tão simples quanto o código do bash.
Chris Eberle
@ Nick: sua linha awk não deve mesmo trabalho, considerando que $0é toda a linha, então você realmente tem repetindo colunas lá ...
Chris Eberle
Solução Nifty outra forma :): @ Nick
Chris Eberle
@ Chris: sim, mas é muito mais feio do que eu gostaria que fosse. Talvez seu código não estivesse funcionando para mim porque eu estou usando >> para capturar o stdout?
Nick #
17

Eu tinha uma série de arquivos que terminavam em stats.txt que eu queria concatenar com os nomes dos arquivos.

Fiz o seguinte e, quando houver mais de um arquivo, o comando "more" inclui o nome do arquivo como cabeçalho.

more *stats.txt > stats.txt

ou para um caso geral

more FILES_TO_CONCAT > OUTPUT_FILE
Steinfadt
fonte
6
more <FILES> | cat
Acumenus
15
find . -type f -print0 | xargs -0 -I % sh -c 'echo %; cat %'

Isso imprimirá o nome do arquivo completo (incluindo o caminho) e o conteúdo do arquivo. Também é muito flexível, pois você pode usar -name "expr" para o comando find e executar quantos comandos quiser nos arquivos.

kevinf
fonte
11
Também é bastante simples de combinar grep. Para usar com bash:find . -type f -print | grep PATTERN | xargs -n 1 -I {} -i bash -c 'echo ==== {} ====; cat {}; echo'
dojuba
5

Eu gosto dessa opção

for x in $(ls ./*.php); do echo $x; cat $x | grep -i 'menuItem'; done

A saída é assim:

./debug-things.php
./Facebook.Pixel.Code.php
./footer.trusted.seller.items.php
./GoogleAnalytics.php
./JivositeCode.php
./Live-Messenger.php
./mPopex.php
./NOTIFICATIONS-box.php
./reviewPopUp_Frame.php
            $('#top-nav-scroller-pos-<?=$active**MenuItem**;?>').addClass('active');
            gotTo**MenuItem**();
./Reviews-Frames-PopUps.php
./social.media.login.btns.php
./social-side-bar.php
./staticWalletsAlerst.php
./tmp-fix.php
./top-nav-scroller.php
$active**MenuItem** = '0';
        $active**MenuItem** = '1';
        $active**MenuItem** = '2';
        $active**MenuItem** = '3';
./Waiting-Overlay.php
./Yandex.Metrika.php
ch3ll0v3k
fonte
4

É assim que eu costumo lidar com formatação assim:

for i in *; do echo "$i"; echo ; cat "$i"; echo ; done ;

Eu geralmente canalizo o gato para um grep para obter informações específicas.

MorpheousMarty
fonte
3
Tenha cuidado aqui. for i in *não incluirá subdiretórios.
Acumenus
4

E a solução awk ausente é:

$ awk '(FNR==1){print ">> " FILENAME " <<"}1' *
kvantour
fonte
Obrigado pela sua ótima resposta. Você pode explicar o significado de 1no final da expressão?
bwangel
11
Obrigado. Você pode ter um olhar para o que é o significado de 1 no final do awk roteiro
kvantour
3

você pode usar esse comando simples em vez de usar um loop for,

ls -ltr | awk '{print $9}' | xargs head
Gagan
fonte
3

Se você gosta de cores, tente o seguinte:

for i in *; do echo; echo $'\e[33;1m'$i$'\e[0m'; cat $i; done | less -R

ou:

tail -n +1 * | grep -e $ -e '==.*'

ou: (com o pacote 'multitail' instalado)

multitail *
sjas
fonte
11
Por uma questão de cores, destacando o nome do arquivo:find . -type f -name "*.txt" | xargs -I {} bash -c "echo $'\e[33;1m'{}$'\e[0m';cat {}"
MediaVince 5/17/17
3

Se todos os arquivos tiverem o mesmo nome ou puderem corresponder find, você pode fazer (por exemplo):

find . -name create.sh | xargs tail -n +1

para encontrar, mostre o caminho e crie um arquivo para cada arquivo.

drkvogel
fonte
2

Aqui está uma maneira realmente simples. Você disse que quer gato, o que implica que você deseja visualizar o arquivo inteiro. Mas você também precisa do nome do arquivo impresso.

Tente isto

head -n99999999 * ou head -n99999999 file1.txt file2.txt file3.txt

espero que ajude

Trey Brister
fonte
4
uma sintaxe mais limpa seria head -n -0 file.txt file2.txt file3.txt. Funciona headno GNU coreutils
doubleDown 20/12/12
1

Este método imprimirá o nome do arquivo e, em seguida, o conteúdo do arquivo:

tail -f file1.txt file2.txt

Resultado:

==> file1.txt <==
contents of file1.txt ...
contents of file1.txt ...

==> file2.txt <==
contents of file2.txt ...
contents of file2.txt ...
serenesat
fonte
3
O -fé útil se você deseja acompanhar um arquivo que está sendo gravado, mas não é realmente dentro do escopo do que o OP perguntou.
Tripleee 4/09/15
1

Se você quiser substituir aqueles feios ==> <== por outra coisa

tail -n +1 *.txt | sed -e 's/==>/\n###/g' -e 's/<==/###/g' >> "files.txt"

explicação:

tail -n +1 *.txt - gera todos os arquivos na pasta com cabeçalho

sed -e 's/==>/\n###/g' -e 's/<==/###/g'- substitua ==>por nova linha + ### e <==por apenas ###

>> "files.txt" - envia tudo para um arquivo

boroboris
fonte
0
find . -type f -exec cat {} \; -print
user5689319
fonte
0

Se você deseja o resultado no mesmo formato da saída desejada, pode tentar:

for file in `ls file{1..3}.txt`; \
do echo $file | cut -d '.' -f 1; \ 
cat $file  ; done;

Resultado:

file1
bluemoongoodbeer
file2
awesomepossum
file3
hownowbrowncow

Você pode colocar echo -eantes e depois do corte para ter o espaçamento entre as linhas também:

$ for file in `ls file{1..3}.txt`; do echo $file | cut -d '.' -f 1; echo -e; cat $file; echo -e  ; done;

Resultado:

file1

bluemoongoodbeer

file2

awesomepossum

file3

hownowbrowncow
Fekete Sumér
fonte
Explicação: o forloop percorre a lista em que o lscomando resultou. $ ls file{1..3}.txtResultado: file1.txt file2.txt file3.txtcada iteração será echoa $filestring e depois será canalizado para um cutcomando onde eu usei .como separador de campo que divide fileX.txt em duas partes e imprime o campo1 (campo2 é o txt) O resto deve ficar claro
Fekete Sumér
0
  • AIX 7.1 ksh

... olhando para aqueles que já mencionaram a cabeça funciona para alguns de nós:

$ r head
head file*.txt
==> file1.txt <==
xxx
111

==> file2.txt <==
yyy
222
nyuk nyuk nyuk

==> file3.txt <==
zzz
$

Minha necessidade é ler a primeira linha; como observado, se você quiser mais de 10 linhas, precisará adicionar opções (cabeçalho -9999, etc.).

Desculpe por postar um comentário derivado; Não tenho credito suficiente para comentar / adicionar ao comentário de alguém.

JustinKaisse
fonte
-1

Para resolver essas tarefas, geralmente uso o seguinte comando:

$ cat file{1..3}.txt >> result.txt

É uma maneira muito conveniente de concatenar arquivos se o número de arquivos for bastante grande.

Igor
fonte
11
Mas isso não responde à pergunta do OP.
kvantour
-3

Primeiro, criei cada arquivo: echo 'information'> file1.txt para cada arquivo [123] .txt.

Depois imprimi cada arquivo para garantir que as informações estavam corretas: arquivo final? .Txt

Então eu fiz isso: tail file? .Txt >> Mainfile.txt. Isso criou o Mainfile.txt para armazenar as informações em cada arquivo em um arquivo principal.

O gato Mainfile.txt confirmou que estava tudo bem.

==> file1.txt <== bluemoongoodbeer

==> file2.txt <== awesomepossum

==> file3.txt <== hownowbrowncow

John Ray
fonte