Se selected-images-to-copy.txt
for uma lista de arquivos apenas (o último elemento do caminho é sempre um arquivo, não um diretório), aqui está uma solução para criar o arquivo com direitos de diretório apropriados:
EDIT: Eu adicionei uma solução melhor no final, mantendo a (s) solução (ões) intermediária (s), capitalizando os comentários da dave_thompson_085 e pensando no que poderia ser melhorado com as informações disponíveis
Como ele escreveu, (e como não expliquei completamente), a parte importante da solução é usar --no-recursion
. Isso permite salvar todas as meta-informações para cada diretório adicionado manualmente no caminho, até os próprios arquivos, sem incluir todos os outros diretórios e arquivos indesejados que seriam recursivamente adicionados de outra forma.
awk -F/ '{ d=$1; for (i=2; i <= NF; i++) { print d; d=d "/" $i }; print d }' selected-images-to-copy.txt > selected-images-to-copy-with-explicit-arborescences.txt
tar cf - --no-recursion -T selected-images-to-copy-with-explicit-arborescences.txt | pigz | pv | nc 1.1.1.1 2222
Se você realmente quiser fazê-lo on-the-fly, usando <()
construct do bash:
tar cf - --no-recursion -T <(awk -F/ '{ d=$1; for (i=2; i <= NF; i++) { print d; d=d "/" $i }; print d }' selected-images-to-copy.txt) | pigz | pv | nc 1.1.1.1 2222
O comando awk apenas reconstrói e adiciona o caminho, um nível de diretório por vez até o próprio arquivo.
Dessa forma, qualquer diretório no caminho de um arquivo para salvar também é colocado no arquivo, mas com o --no-recursion
mais nada acontecerá. Assim, toda propriedade de diretório antes do arquivo será salva e restaurada corretamente.
Ainda há um problema de desempenho que você tem que trocar em algum lugar: haverá muitos arborescences repetidos, então o segundo tar frequentemente refaz um chown no mesmo diretório base. Você poderia classificar -u o resultado do awk para remover todas as duplicatas, mas, em seguida, o tipo pode levar muito tempo antes de dar os resultados e a transferência para iniciar. Com um pequeno script em perl que armazena elementos únicos na memória (trade-off é o uso da memória, mas duvido que seja um problema), não há necessidade de ordenar a saída de entradas exclusivas sem atraso. Então a solução se torna:
tar cf - --no-recursion -T <(awk -F/ '{ d=$1; for (i=2; i <= NF; i++) { print d; d=d "/" $i }; print d }' selected-images-to-copy.txt | perl -w -e 'use strict; my %unique; while (<>) { if (not $unique{$_}++) { print } }' ) | pigz | pv | nc 1.1.1.1 2222
EDITAR: Se o conteúdo de selected-images-to-copy.txt
for mais ou menos uma lista ordenada de arquivos (a saída não ordenada de um comando find
[...] -type f
é boa o suficiente), aqui está uma solução que não precisa de nenhum uso de memória (o que pode de fato ter se tornado um problema com centenas de milhões de entradas)
É bom o suficiente apenas lembrar o último caminho mais longo e compará-lo ao próximo caminho:
- ou o próximo não é um prefixo do anterior, o que significa que é um novo arborescente (ou novo arquivo na mesma arborescência) e tem que ser arquivado e neste caso é projetado o novo "último caminho mais longo". Se a lista inicial não foi pelo menos apresentada como uma árvore (como em pelo menos uma saída do comando find
, ou, claro, uma lista ordenada), algumas repetições começam a aparecer.
- ou é um prefixo (uma correspondência de substring do primeiro caractere), o que significa que é um diretório que já foi visto, já que faz parte do caminho do anterior e pode ser ignorado com segurança.
Estou adicionando um /
à direita na comparação para descobrir facilmente que mnt/a/b/foo/
não é um prefixo de mnt/a/b/foobar
. Com mnt/a/b/foobar/file4.png
e mnt/a/b/foo/file5.png
como entrada, a propriedade do diretório mnt/a/b/foo
não teria sido restaurada sem esse truque. Então o comando perl é substituído por:
awk '{ if (index(old,$0 "/") != 1) { old=$0; print } }'
Este exemplo:
file1.png
mnt/a/b/file2.png
mnt/a/b/file3.png
mnt/a/b/c/foobar/file4.png
mnt/a/b/c/foo/file5.png
mnt/a/b/file6.png
mnt/a/b/d/file7.png
Através deste filtro:
awk -F/ '{ d=$1; for (i=2; i <= NF; i++) { print d; d=d "/" $i }; print d }' | awk '{ if (index(old,$0 "/") != 1) { old=$0; print } }'
Dá esses diretórios e arquivos prontos para tar --no-recursion
:
file1.png
mnt
mnt/a
mnt/a/b
mnt/a/b/file2.png
mnt/a/b/file3.png
mnt/a/b/c
mnt/a/b/c/foobar
mnt/a/b/c/foobar/file4.png
mnt/a/b/c/foo
mnt/a/b/c/foo/file5.png
mnt/a/b/file6.png
mnt/a/b/d
mnt/a/b/d/file7.png
Assim, a solução com todo o par de comandos se torna (o root já usa -p
e --same-owner
, e melhor a extravagante bash <()
quando um |
pode funcionar e facilmente permite quebrar a linha longa com \
para legibilidade):
# TARGET (extract):
$ nc -l -p 2222 | pigz -d | sudo tar xf - -C /
# SOURCE:
$ awk -F/ '{ d=$1; for (i=2; i <= NF; i++) { print d; d=d "/" $i }; print d }' selected-images-to-copy.txt | \
awk '{ if (index(old,$0 "/") != 1) { old=$0; print } }' | \
tar cf - --no-recursion -T - | pigz | pv | nc -w 60 1.1.1.1 2222