Um script pequeno e simples falha e esta é a saída “Nenhum arquivo ou diretório”

2

O script é:

#!/bin/bash
IFS="
"

command="ls /tmp"

for file in '$command';
do
    echo $file
done

E a saída é:

$ ./test.sh 
./test.sh: line 17: ls /tmp: No such file or directory
    
por tres.14159 03.10.2014 / 14:36

2 respostas

4

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
    
por 03.10.2014 / 14:52
0

Remova a parte IFS .

$IFS é a variável do separador de campo interno de bash . Sua linha no script faz com que bash separe os campos apenas em novas linhas e não no padrão (espaço, tabulação e nova linha). ls fornece a saída separada nas guias.

Isso funciona:

#!/bin/bash

command="ls /tmp"

for file in '$command';
do
    echo $file
done
    
por 03.10.2014 / 14:48