Concatene vários arquivos compactados, ignorando as linhas de cabeçalho em todos, exceto no primeiro arquivo

3

Eu tenho uma coleção de arquivos gzipados que quero combinar em um único arquivo. Cada um deles tem formato idêntico. Eu quero manter as informações de cabeçalho apenas do primeiro arquivo e ignorá-lo nos arquivos subseqüentes.

Como um exemplo simples, tenho quatro arquivos idênticos com o seguinte conteúdo:

$ gzcat file1.gz
# header
1
2

Eu quero acabar com

# header
1
2
1
2
1
2
1
2

Na realidade, posso ter um número variável de arquivos, então gostaria de poder fazer isso programaticamente. Aqui está a solução não-programática que tenho até agora ...

cat <(gzcat file1.gz) <(tail -q -n +2 <(gzcat file2.gz) <(gzcat file3.gz) <(gzcat file4.gz))

Este comando funciona, mas é “hard coded” para lidar com quatro arquivos, e preciso generalizá-lo para qualquer número de arquivos. Eu estou usando bash como o shell se isso ajuda. Minha preferência é pelo desempenho (na realidade, os arquivos podem ter milhões de linhas), então estou bem com uma solução menos do que elegante se for rápida.

    
por SethMMorton 17.09.2018 / 03:26

2 respostas

1

Se o comando que você mostra na sua pergunta funcionar basicamente (para um número codificado de arquivos), então

first=1
for f in file*.gz
do
    if [ "$first" ]
    then
        gzcat "$f"
        first=
    else
        gzcat "$f"| tail -n +2
    fi
done > collection_single_file

deve funcionar para você. Espero que a lógica seja bem clara. Observe todos os arquivos (altere o caractere curinga conforme apropriado para seus nomes de arquivos). Se for o primeiro da lista, gzcat , para obter o arquivo inteiro (incluindo o cabeçalho). Caso contrário, use tail para remover o cabeçalho. Depois de você ter lidado com um arquivo, nenhum outro arquivo será o primeiro.

Isso invoca tail N −1 vezes, em vez de apenas uma vez (como sua resposta). Além disso, minha resposta deve ser a mesma que a sua resposta.

    
por 17.09.2018 / 05:57
1

Uma variação na solução do G-Man que não usa uma variável separada para acompanhar o primeiro arquivo :

set -- file*.gz

{
    gzcat "$1"; shift

    for file do
        gzcat "$file" | sed '1d'
    done
} >combined.txt

Isso descompacta o primeiro arquivo e, em seguida, percorre os demais, passando cada um por um script sed curto que exclui a primeira linha. A saída é redirecionada para combined.txt .

O comando set -- file*.gz define os parâmetros posicionais ( $1 , $2 , etc., que coletivamente são a matriz $@ ) para os nomes de arquivos que correspondem ao padrão fornecido. O shift remove o $1 da matriz depois de descompactá-lo. O loop faz um loop sobre os nomes de arquivos restantes na matriz e também pode ter sido escrito

for file in "$@"; do
    gzcat "$file" | sed '1d'
done

O { ... } nos permite redirecionar a saída dos comandos para um arquivo de uma só vez.

Ainda mais curto, com a suposição adicional de que uma "linha de cabeçalho" está sempre começando com um caractere # (como no exemplo da pergunta) e que não há outras linhas desse tipo nos dados:

gzcat file*.gz | awk 'NR > 1 && /^#/ { next } 1' >combined.txt

ou

gzcat file*.gz | sed '2,${ /^#/d; }' >combined.txt

Ambos saltam qualquer linha que comece com # se ocorrer na segunda linha ou mais tarde no conteúdo combinado dos dados não compactados.

    
por 19.09.2018 / 09:57