Por que nem todos os arquivos são compactados e como melhorar a solução

8

Eu tenho uma pasta com cerca de 20 mil arquivos. Os arquivos são nomeados de acordo com o padrão xy_{\d1,5}_{\d4}\.abc, por exemplo xy_12345_1234.abc. Eu queria comprimir os primeiros 10K deles usando este comando:

ls | sort -n -k1.4,1.9 | head -n10000 | xargs tar -czf xy_0_10000.tar.gz

no entanto, o arquivo resultante tinha apenas cerca de 2K arquivos dentro.

ls | sort -n -k1.4,1.9 | head -n10000 | wc -l no entanto retorna 10000, conforme o esperado.

Parece-me que estou entendendo mal algo básico aqui ...

Estou usando o zsh 5.0.2 no Linux Mint 17.1, o GNU tar 1.27.1

EDITAR:

forjar como sugerido por @Archemar soa muito plausível, com o fork mais recente sobrescrevendo o arquivo resultante - o arquivo contém a 'cauda' dos arquivos - 7773 para 9999 .

Resultado de xargs --show-limit: Your environment variables take up 3973 bytes POSIX upper limit on argument length (this system): 2091131 POSIX smallest allowable upper limit on argument length (all systems): 4096 Maximum length of command we could actually use: 2087158 Size of command buffer we are actually using: 131072

substituindo -c com -r ou -u não funcionou no meu caso. A mensagem de erro foi tar: Cannot update compressed archives

usando ambos -r e -u é inválido e falha com tar: You may not specify more than one '-Acdtrux', '--delete' or '--test-label' option

substituindo -c com -a parece ser inválido também e falha com o mesmo tar: You must specify one of the '-Acdtrux', '--delete' or '--test-label' options embora eu não reconheça o problema azf e Acdtrux parecem disjuntos para mim.

EDIT 2:

-T parece um bom caminho, eu também encontrei um exemplo Aqui .

No entanto, quando tento

ls | sort -n -k1.4,1.9 | head -n10000 | tar -czf xy_0_10000.tar.gz -T - eu recebo tar: option requires an argument -- 'T'

bem, talvez os nomes dos arquivos não alcancem o alcatrão? Mas parece que eles, porque quando eu executo

ls | sort -n -k1.4,1.9 | head -n10000 | tar --null -czf xy_0_10000.tar.gz -T - eu recebo tar: xy_0_.ab\nxy_1_...<the rest of filenames separated by literal \n>...998.ab Cannot stat: File name too long

Então, por que o tar não está vendo os nomes dos arquivos?

kostja
fonte
e se você tentar um em vez de c, no comando tar?
Olivier Dulac
5
Relevante: Não analise a saída de ls
8bittree
1
O arquivo do OP não possui nomes complicados.
Archemar
@ 8bittree - bem como um conselho geral para scripts de shell robustos, sim. mas o que você sugere em vez de trabalhar com listas de arquivos com os oneliners únicos comuns?
kostja
1
@kostja eu usaria find, que tem um -print0 opção para usar um byte nulo como o delimitador em vez de uma nova linha. sort pode lidar com isso com o -z bandeira. head, infelizmente não manipula entender delimitadores de bytes nulos, mas esta resposta tem uma solução usando tr trocar \n e \0 antes e depois head. tar tem --null -T - ler nomes de arquivos delimitados por nulo de stdin.
8bittree

Respostas:

12

você atingiu o limite de xargs?

xargs --show-limit

experimentar :

  • criar um manequim .tgz Arquivo tar czf xy_0_10000.tar.gz /hello/world
  • substituir -czf por -Azf

quando o xarg atingir seu limite, ele irá bifurcar o comando, então o comando que você executou foi

  tar czf xy_0_10000.tar.gz file1 file2 .... file666
  tar czf xy_0_10000.tar.gz file667 file668 ... file1203
  tar czf xy_0_10000.tar.gz file1024 ... file2000

como cada tar substituir o anterior, você deve estar ficando apenas por último tar c corre.

Editar:

1) de acordo com man tar no Ubuntu -a e -r parece equivalente o acréscimo é feito por (qualquer um) -A, --catenate, --concatenate

2) zip (não gzip ) pode ser usado para adicionar arquivo, talvez uma opção gzip irá fazer o truque. (usar | xargs zip -qr xy_0_0000.zip , isso resultará em um arquivo zip, não no .tar.gz, no entanto)

3) usar a solução do @ rsanchez
É importante adicionar a opção ao tar de forma adequada, tente

ls | sort -n -k1.4,1.9 | head -n10000 |tar -czf xy_0_10000.tar.gz -T -

Onde  - -T - significa a opção de uso -T E use - como argumento para -T (você poderia gerar uma lista de arquivos em /tmp/foo.lst , então use -T /tmp/foo.lst )

Archemar
fonte
poderia um (= adicionar) em vez de c (= criar / sobrescrever) contornar essa limitação?
Olivier Dulac
@ OlivierDulac ( Aviso: este é um palpite puro ) Provavelmente não resolverá, já que o tar não pode criar arquivos vazios. Você maio comprimir uma pasta vazia primeiro e usar a (add) para adicionar os arquivos ao arquivo tar. Então, você pode abrir o tar e remover a pasta (usando 7zip ou algo assim)
Ismael Miguel
@ismaelmiguel: Tenho certeza que será feliz criar o arquivo. se não, apenas: touch xy_0_10000.tar.gz && { _the full command here_ ; }
Olivier Dulac
1
@ OlivierDulac Isso será inválido .gz Arquivo.
Ismael Miguel
Todas as páginas de trabalho que vejo manpages.ubuntu.com/manpages/vivid/en/man1/tar.1.html (15.04) de volta ao preciso (12.04) -r acrescentar, mas -a auto-compressa que não é equivalente. E -rz não funciona: zip pode adicionar a um arquivo existente porque o diretório não está compactado, mas tar com compactação comprime os metadados junto com os dados. Você pode tar -r por partes em um descompactado arquivar e depois gzip o resultado. Ou ...
dave_thompson_085
12

Não há necessidade de xargs. Se você der diretamente tar a -T - opção vai leia os nomes dos arquivos da entrada padrão.

Por exemplo:

... | tar -T - -czf xy_0_10000.tar.gz
rsanchez
fonte
Eu pareço estar usando a opção incorretamente, não posso fazê-lo funcionar com o tubo. Tentei ...| tar Tczf xy_..., ...| tar Tcz -f xy_... ...| tar -czf xy_... -T e várias outras permutações, mas estou ficando apenas tar: You must specify one of the '-Acdtrux', '--delete' or '--test-label' options, tar: -f: Cannot stat: No such file or directory se usando -f separadamente de outras opções e tar: option requires an argument -- 'T'. Você poderia por favor adicionar um exemplo de uso?
kostja
@kostja exemplo adicionado.
rsanchez
Muito obrigado, rsanchez. Não tenho certeza porque a variante com -T - no final de tar lista de opções não funcionou, mas o seu exemplo fez. Infelizmente, minha pergunta realmente tinha duas partes - a fonte do erro e uma possível melhora. Enquanto você agia no último, Archemar era excelente no primeiro e quase tinha o último direito. Não tenho certeza de qual das suas respostas aceitar, já que ambas foram obviamente úteis.
kostja
1

Eu quero complementar as outras duas respostas com um zsh solução, que nem analisa ls , nem precisa xargs . No entanto, não tenho certeza agora, se ele também sofre com a limitação do comprimento da linha de comando.

  1. Defina uma função que gere sua chave de classificação desejada modificando $REPLY.

    sortkey() { REPLY=${REPLY[4,9]} }
    

    Isso é equivalente ao seu sort -n -k1.4,1.9

  2. Gere uma matriz $files com os nomes de arquivos classificados com a função acima:

    files=(*(o+sortkey))
    

    Isso é equivalente a ls | sort -n -k1.4,1.9

  3. Retornar os primeiros 10 000 arquivos com

    ${files[0,9999]}
    

    Isso é equivalente a ls | sort -n -k1.4,1.9 | head -n10000

Então, tudo isso deve fazer o truque:

sortkey() { REPLY=${REPLY[4,9]} }
files=(*(o+sortkey))
tar -czf xy_0_10000.tar.gz ${files[0,9999]}
mpy
fonte