O problema é que o bash não executará variáveis que contenham espaços corretamente porque você definiu $IFS
para uma nova linha. Isso faz com que ele tente executar a coisa toda como um comando, em vez de usar a primeira palavra como um comando e passar o resto como argumentos.
Se você tivesse que executar isso com strace
, em vez de ver isso:
execve("/bin/ls", ["ls", "/tmp"], [/* 42 vars */]) = 0
--- -----
| |-------------> 2nd argument
|---------------------> 1st argument, the command to run.
Você veria que o primeiro argumento é ls /tmp
em vez de ls
sozinho, e isso é algo que o sistema não pode executar.
Seu script realmente funcionará como esperado se você não definir a variável IFS
. No entanto, você nunca deve usar ls
para iterar a saída de um comando, a menos que você saiba que a saída nunca contará com espaços ou outros caracteres estranhos. Para ilustrar:
$ touch 'file name with spaces'
$ for i in $(ls ./); do echo "$i"; done
file
name
with
spaces
A maneira correta é usar um destes:
$ for i in *; do echo "i is: $i"; done
i is: file name with spaces
$ find . -type f | while IFS= read -r i; do echo "i is: $i"; done
i is: ./file name with spaces
Se você realmente quiser usar ls
, pelo menos use-o com um loop while. Isso ainda vai quebrar em várias coisas, mas pelo menos pode lidar com espaços:
$ ls | while read i; do echo "i is: $i"; done
i is: file name with spaces
Finalmente, para poder lidar com qualquer nome do arquivo, incluindo aqueles com novas linhas, barras invertidas, espaços ou qualquer outra coisa. Use:
$ find . -type f -print0 | while IFS= read -r -d '' i; do echo "i is: $i"; done
i is: ./file name with spaces