quarta-feira, 2 de julho de 2008

Dupla diabólica: find e xargs

Hoje veremos uma dupla considerada diabólica no shell, se utilizada sem a devida atenção: o find e o xargs.

O find, como o nome diz, é utilizado para se encontrar arquivos no sistema. Já o xargs facilita a repetição de um certo comando para cada entrada fornecida pra ele. Peraí, vamos entender melhor. Considere que eu queira apagar os arquivos terminados em ~ no diretório atual. Poderíamos utilizar a dupla da seguinte maneira:
$ find . -name "*~" | xargs rm

Aqui o find retornaria uma saída com os arquivos terminados em ~ e o xargs executaria o comando rm para cada uma das saídas.

Por exemplo, crie um diretório teste e entre nele:
$ mkdir teste; cd teste

Agora crie os arquivos "teste" e "teste~":
$ > teste; > teste~
$ ls
teste teste~

Agora vamos apagar os arquivos terminados em ~ do diretório atual:
$ find . -name "*~" | xargs rm
$ ls
teste

Olha aí o resultado acima, deu certo.

Até aí tudo bem, mas se o arquivos terminados em ~ possuíssem caracteres em branco? Experimente e crie um "teste 1~". Proteja-o com aspas para que o shell não interprete mal o que queremos:
$ > "teste 1~"
$ ls
teste teste 1~

Agora experimente apagá-lo com:
$ find . -name "*~" | xargs rm
rm: imposível remover `1~': Arquivo ou diretório inexistente
$ ls
teste 1~

E agora, José? Cadê meu arquivo "teste"? Ele APAGOU O ARQUIVO ERRADO!

Isso aconteceu simplesmente porque o rm tratou o arquivo "teste 1~" como dois arquivos diferentes: um "teste" e outro "1~". Ou seja, o espaço em branco acabou com nossa alegria (e talvez o emprego)!

Mas não se apavore, pois uma solução pra isso é utilizar a opção -print0 do find junto com a opção -0 do xargs.
$ > teste
$ ls
teste teste 1~
$ find . -name "*~" -print0 | xargs -0 rm
$ ls
teste

Pronto, agora sim, funcionou! A explicação é que o find separou os arquivos com um caractere nulo. O xargs interpretou os caracteres nulos como separadores dos arquivos e fez o que queríamos. :]

Para mais informações:
$ man find
$ man xargs

Um comentário:

Sergio disse...

Tenho um wiki com comandos linux, e nele muitas anotações sobre o find:

http://linuxdicas.wikispaces.com/find

No meu blog tenho muita coisa sobre bash:
http://vivaotux.blogspot.com/search/label/bash