Bash tem uma variável especial chamada IFS.
é usado pelo bash para saber como dividir uma string em uma lista de arquivos. (Separador de campo interno)
Por padrão, ele contém espaço, tabulação e nova linha.
for f in 'find ...
será dividido nos caracteres contidos na variável $ IFS. Se houver uma nova linha ou espaço nos nomes de arquivo retornados pelo comando find, ele será tratado como um nome de arquivo diferente e, portanto, o loop será executado para cada parte do nome.
$ ls -l -rw-r--r-- 1 marko marko 0 2010-11-01 12:06 my?bad.exe -rw-r--r-- 1 marko marko 0 2010-11-01 12:06 space bad.exe $ for f in $(find -name "*.exe"); do echo "ITERATING ($f)"; done ITERATING (./space) ITERATING (bad.exe) ITERATING (./my) ITERATING (bad.exe)
Podemos ajustar a variável $ IFS, mas é complicado. Vamos ver porque:
(Teste este código em scripts para que a mudança da variável IFS não permaneça no seu shell, você pode ficar confuso)
$ IFS=""; for f in $(find -name "*.exe"); do echo "ITERATING ($f)"; done ITERATING (./space bad.exe ./my bad.exe)
Como você pode ver, agora o bash não tem nenhuma regra para dividir a string retornada pelo comando find e trata toda a saída como um único valor.
Claramente, o problema aqui é que, se usarmos for f in 'find....
, não temos como distinguir uma nova linha contida no arquivo de uma nova linha gerada pelo comando find.
Agora, por questões de integridade, mostrarei a você como fazer sua abordagem funcionar, mas, por favor, veja uma solução mais fácil no final da resposta.
Poderíamos usar outros caracteres delimitadores e corrigir seu problema atual:
$ for f in $(find -name "*.exe" -exec echo -n {}: \;); do echo "ITERATING ($f)"; done; ITERATING (./space bad.exe) ITERATING (./my bad.exe) ITERATING ()
Aqui, o comando find -name "*.exe" -exec echo -n {}: \;
retorna a lista de arquivos separados por ":" (que não são válidos nos nomes de arquivos do Windows).
Agora, o próximo passo seria realmente excluir esses arquivos. Leve em consideração que o último arquivo é terminado por ":" e, portanto, o bash itera uma última vez com uma string vazia.
IFS=":"; for f in $(find -name "*.exe" -exec echo -n {}: \;); do if [ ! -z "$f" ]; then rm "$f"; fi; done;
Você deve ser capaz de realizar também o teste basename / dirname nesse loop, como fez antes.
No entanto, esta solução é feia e funciona apenas porque temos um personagem em que podemos confiar.
Acontece que o próprio comando find pode executar comandos para cada arquivo que encontrar.
find -name "*.exe" -exec ./yourscript.sh {} \;
Em seguida, o yourscript.sh receberá o nome completo em $ 1 e você poderá excluir com rm "$1"
, mesmo que contenha novas linhas e espaços.
O comando find também pode ser usado para executar inline o teste basename / dirname que você faz dentro do seu script, mas é um tópico mais avançado.