Como fazer `head` e` tail` na entrada delimitada por nulo no bash?

18

findO comando pode gerar nomes de arquivos como cadeias delimitadas por nulo (se -print0houver) e xargsconsumi-los com a -0opção ativada. Mas no meio, é difícil manipular essa coleção de arquivos - sortcomando tem -zswitch, que torna possível para resolver esses arquivos, mas heade tailnão tê-los.

Como posso fazer heade tailnessas entradas delimitadas por nulos de uma maneira conveniente? (Eu sempre posso criar um script ruby ​​curto e lento, mas espero que possa haver uma maneira melhor)

Rogach
fonte

Respostas:

21

O GNU heade a tailversão 8.25 do coreutils têm uma -zopção para isso.

Com versões mais antigas ou para sistemas não-GNU, você pode tentar trocar \0e \n:

find ... -print0 |
  tr '\0\n' '\n\0' |
  head |
  tr '\0\n' '\n\0'

Observe que algumas headimplementações não conseguem lidar com caracteres NUL (e não são exigidos pelo POSIX), mas onde encontrar suporte -print0, heade utilitários de texto geralmente suportam caracteres NUL.

Você também pode usar uma função para quebrar qualquer comando entre os dois trs:

nul_terminated() {
  tr '\0\n' '\n\0' | "$@" | tr '\0\n' '\n\0'
}

find ... -print0 | nul_terminated tail -n 12 | xargs -r0 ...

Lembre-se de que nul_terminated, abaixo , a \0significa um caractere de nova linha. Por exemplo, para substituir \npor _:

find . -depth -name $'*\n*' -print0 | nul_terminated sed '
  p;h;s,.*/,,;s/\x0/_/g;H;g;s,[^/]*\n,,' | xargs -r0n2 mv

( \x0sendo também uma extensão GNU).

Se você precisar executar mais de um comando de filtragem , poderá:

find ... -print0 |
  nul_terminated cmd1 |
  nul_terminated cmd2 | xargs -r0 ...

Mas isso significa executar alguns trcomandos redundantes . Como alternativa, você pode executar:

find ... -print0 | nul_terminated eval 'cmd1 | cmd2' | xargs -r0 ...
Stéphane Chazelas
fonte
2
Isso não derruba o principal motivo¹ do uso, em \x0vez de \ndelimitar os valores? (¹ para que você pode lidar com valores que pode conter \n)
Thedward
@Thedward, não, pelo contrário -print0 | tr '\n\0' '\0\n'tem linhas que representam os caminhos de arquivo em que caracteres de nova linha neles foram convertidos para \0. Portanto, se você pegar a primeira linha head -n 1e converter \0s novamente em novas linhas tr '\0\n' '\n\0', terá o primeiro caminho de arquivo delimitado por NUL com seus caracteres de nova linha incorporados.
Stéphane Chazelas