Estou lendo alguns livros sobre scripts bash e lutando para entender a citação adequada e o uso de IFS
. Talvez alguém possa me ajudar com um pequeno exemplo envolvendo nomes de arquivos entre aspas. Fazendo isso a partir de uma linha de comando, isso funciona para imprimir os nomes dos arquivos corretamente, mesmo que incluam espaços:
set - *
for i in "$@"; do echo $i; done
Isso não funciona, pois quebra nos espaços:
set - `find . -name "*"`
for i in "$@"; do echo $i; done
Nem faz:
IFS=$'\0' set - `find . -name "*" -print0`
for i in "$@"; do echo $i; done
Nem a combinação que usa IFS=$'\n'
e -print
. Por que tudo isso falha?
O seguinte também falha, mas neste caso ocorre um erro ("bash: erro de sintaxe próximo ao token inesperado `do'"). Por que?
IFS=$'\n' for i in `find . -name "*" -type f`; do echo $i; done
mas isso funciona (observe o ";"):
IFS=$'\n'; for i in `find . -name "*" -type f`; do echo $i; done
e isso falha porque os nomes dos arquivos não são divididos (os for
loops apenas uma vez):
IFS=''; for i in `find . -name "*" -type f -print0`; do echo -e "$i\n"; done
Então, novamente, por que o primeiro e o terceiro falham?
Finalmente, estou correto em minha crença de que, ao definir IFS
, ''
é o mesmo que $'\0'
? (Tentei ambos no exemplo imediatamente anterior.) Se sim, por que aparentemente preciso $'\n'
em vez de apenas \n
?
*Bash é a versão 4.3.42(1) no Ubuntu Gnome 16.04.
Responder1
Duas coisas que aprendi ao depurar comandos bash: Uma) Se não estiver funcionando como você acha que deveria, faça eco/printf dos comandos para garantir que eles sejam exibidos da maneira que você acha que deveriam. Dois) Execute manualmente os comandos que você possui em seus comandos para ver se eles funcionam corretamente (a menos que sejam comandos potencialmente destrutivos, como: rm, dd, chmod 777, etc.).
Como ou o que exatamente você está tentando realizar? O meu parece funcionar bem com esse comando literalmente, então você precisará explicar o que deseja que ele faça. Você tem espaços em seus nomes de arquivos que estão causando problemas com seu comando? Contanto que você se livre do espaço da variável IFS, provavelmente funcionará da maneira que você deseja: IFS=$'\n\t'
. A -print0
opção basicamente remove todos os separadores de linha, de modo que tudo o que você deseja desses comandos não tenha nenhum separador, a menos que os arquivos tenham espaços em seus nomes. Para o seu segundo conjunto de perguntas:
IFS=$'\n' for i in `find . -name "*" -type f`; do echo $i; done
Este comando não é executado corretamente porque o bash o lê assim:
IFS=$'\n' for i in `find . -name "*" -type f`
do echo $i
done
A primeira linha define IFS como: $'\n' for i in
. Depois disso, a próxima linha é lida como: do echo $i
, o que não funciona, pois o do
comando requer algum tipo de loop para ser o comando anterior. O próximo funciona porque o ponto e vírgula informa ao bash que o final do comando foi alcançado:
IFS=$'\n'
for i in `find . -name "*" -type f`
do echo $i
done
Finalmente, estou correto em acreditar que, ao definir IFS, '' é o mesmo que $'\0'? (Eu tentei ambos no exemplo imediatamente anterior.) Se sim, por que aparentemente preciso de $'\n' em vez de apenas \n?
Sim para a primeira parte, ''
e '\0'
ambos são considerados NULOS, então são basicamente iguais. Isso se deve à interpretação do bash $''
como uma string ANSI C. E você precisa colocar aspas em torno do \n para que o bash o interprete como uma string C para que um caractere de nova linha seja colocado no IFS.
Você pode ler mais sobre estesaqui.