No exemplo que você fornece, a única coisa que eval
está obtendo é cat
, o redirecionamento está acontecendo fora de eval
e o arquivo está sendo fornecido como stdin para o comando eval "cat"
.
Uma variação é citar todo o comando, incluindo o redirecionamento, usando aspas simples:
$ eval 'cat < "$filename"'
hi
Agora, eval
está obtendo a string inteira, incluindo o redirecionamento e o nome da variável, portanto, está fazendo uma expansão de variável e a cotação necessária para o nome do arquivo com espaços. Isso ainda funcionaria.
Outra opção é usar aspas duplas para a string:
$ eval "cat < '$filename'"
hi
Agora, a variável é expandida pelo shell, mas isso ainda funciona, pois as aspas dentro dela mantêm o nome do arquivo juntos. (Observe que isso quebraria se o nome do arquivo contivesse um apóstrofo).
O que não * funciona é isto:
$ eval "cat" "<" "$filename"
Isso é semelhante ao seu exemplo, mas com o <
cotado, o redirecionamento não será executado pelo shell externo. eval
reunirá os argumentos e o comando resultante será:
cat < my file
O que não funcionará como esperado, já que as aspas em torno de my file
já desapareceram ...