Estou assumindo que o seguinte bloco representa uma função, mas pode não ser:
mounted()true
if
...
fi ... && mounted
Eu quero entender o que é essa construção. Não encontrei sintaxe semelhante no cabeçalho de funções em zsh
Um trecho mais completo do código real é:
#! /bin/zsh -p
# leaving out a section...
tmpdir=$(mktemp -d) || exit
mounted()true
if
mount "$type[@]" -o "${(j[,])opts}" -- "$dev" "$tmpdir"
then
mount --bind -- "$tmpdir/$subdir" "$dest" || mounted()false
umount -- "$tmpdir"
fi && rmdir -- "$tmpdir" && mounted
Assim que eu entender, vou converter todo o script zsh para uma linguagem com a qual estou mais familiarizado. Eu poderia convertê-lo em bash como uma etapa intermediária.
Essa é a sintaxe do shell Bourne (dos anos 80) para definir uma função, não é específica do zsh.
No shell Bourne, uma função é definida colocando -se
functionName()
na frente de um comando¹.Assim
name() true
define uma função chamadaname
com otrue
comando simples como seu corpo.Esse é o caso em quase todos os shells do tipo Bourne (ksh, ash, dash, bosh, pdksh, mksh, zsh...). Uma exceção notável é
bash
² (o shell GNU) que inicialmente só permitia que um grupo de comandos ({ ...; }
) fosse o corpo de uma função e posteriormente o alterasse para qualquer comando composto como o POSIX requer para ash
linguagem .Então, em
bash
, você precisaria dename()((1))
orname()[[ . ]]
((( ... ))
e[[ ... ]]
—construtos emprestados de ksh— sendo considerados comandos compostos lá), ouname() { true; }
onde o corpo da função é um grupo de comandos com apenas um comando simples dentro.Observe que
ksh
foi o shell que introduziu as funções pela primeira vez, embora com uma sintaxe diferente:function name { body; }
.zsh
suporta a sintaxe Korn e Bourne e possui extensões próprias.Em
zsh
verinfo zsh function
, o que deve levar você à parte sobre a palavra-function
chave Korn para a sintaxe das definições de funções. Uma extensão dezsh
é que você pode definir mais de uma função de uma vez com o mesmo corpo e usar qualquer string como o nome da função³:Para definir algumas funções diferentes que chamam
true
oufalse
sem argumentos.Se você omitir o nome da função, isso se torna uma função anônima, que pode receber argumentos e é invocada no local:
(embora por razões óbvias, para que essa função anônima possa receber argumentos, seu corpo não pode ser um comando simples).
Em
zsh
, as funções também são disponibilizadas através do$functions
array associativo especial onde as teclas são os nomes das funções e valorizam o código no corpo. Entãofunctions[name]=true
é outra maneira de definir aname
função.Agora, usar funções para armazenar um valor booleano não é algo que você vê com muita frequência, mas se você parar para pensar sobre isso por um minuto, faz bastante sentido.
Na linguagem C, por exemplo, as construções
if
/while
ou os operadores lógicos&&
/||
atuam em números.if (condition) something
fazsomething
secondition
for um número diferente de zero.awk
ouperl
que são linguagens semelhantes a C estendem isso paracondition
serem definidas ou serem strings não vazias.Mas os shells estão antes de todos os intérpretes de linha de comando. No shell, tudo é um comando.
if
/while
e&&
/||
na maioria dos shells atuam em comandos.if condition; then something; fi
fazsomething
se ocondition
comando for bem-sucedido.false
etrue
são comandos de constantes booleanas (incorporados na maioria dos shells) que sempre falham / são bem-sucedidos, respectivamente, então são os óbvios para representar valores booleanos. E as funções são a estrutura de dados mais apropriada para armazenar comandos (ou código shell em geral).alias
es (que muitos consideram uma herança quebrada do csh, um shell que (como o Bourne shell inicialmente) não tinha funções) não funcionaria aqui, pois os aliases são expandidos no momento em que o código que os contém é lido, não em tempo de execução. Por exemplo em:O corpo da função realmente conterá
if false; then...
porque oname
alias foi expandido no momento em que o código para definir a função foi lido, não no momento em que a função é executada.Pode-se armazenar o código em uma variável em vez de uma função:
Onde testamos o sucesso do
eval
comando que é instruído a interpretar o código em$name
4 . Observe que, nesse caso, um vazio / indefinido (exceto com anounset
opção)$name
produz true .Ou poderíamos fazer (e é o que geralmente faço em
sh
/bash
scripts):Onde executamos o comando cujo nome está armazenado
$name
sem argumentos.Ou:
Onde armazenamos os argumentos de um comando simples em um
$name
array de variáveis.Pessoas acostumadas a linguagens do tipo C podem querer armazenar os booleanos como variáveis inteiras e executar um comando como
[
/test
ouexpr
para testar o valor de inteiros quando convertidos de suas representações textuais:Em shells semelhantes a Korn (inclui
bash
ezsh
), você pode usar a((...))
construção que avalia expressões aritméticas semelhantes a C e retorna sucesso se isso resultar em um número diferente de zero (mesmo NaN).Você também pode executar um comando que compara strings (como
[
/test
/expr
novamente) ou faz correspondência de padrões como essa outra[[ string = pattern ]]
construção semelhante a Korn:(isso soa tão estranho para mim quanto fazer um
if (strcmp(name, "true") == 0)...
em C).Ou mesmo se você gostasse, com um teste
awk
/perl
-like para uma variável definida.¹ Houve um bug no shell Bourne (não em seus clones/derivativos 5 ) embora isso não funcionasse corretamente se você usasse um comando simples com redirecionamentos como o corpo da função, e é provavelmente por isso que o POSIX requer apenas comandos compostos ser suportado como corpo de funções.
²
yash
(escrito para a especificação POSIX),posh
(com base,pdksh
mas escrito para ajudar a verificar a conformidade com o padrão, portanto, com a maioria das extensões do padrão removidas, incluindo aquela) são duas outras exceções³ que é consistente com o fato de que comandos externos e argumentos de comando podem ser qualquer string, pois os arquivos podem conter qualquer string (embora um nome de arquivo não possa estar vazio e nomes de arquivo/argumentos não possam conter bytes NUL). No shell Bourne, os nomes de funções e variáveis compartilhavam o mesmo namespace (não era possível definir uma função e uma variável com o mesmo nome) e os nomes das funções tinham as mesmas limitações que os nomes das variáveis.
4 com um espaço prefixado para correção, pois algumas
eval
implementações não suportam--
marcar o fim das opções5 zsh, costumava ter seus próprios problemas quando os redirecionamentos eram usados no corpo de uma função, a maioria dos quais foi corrigida. Mas mesmo agora, e conforme declarado explicitamente na documentação, em
f() { cmd; } < $1
, (no caso especial em que o corpo é um grupo de comandos com redirecionamentos) isso$1
não se refere ao$1
no escopo da função, mas ao$1
do chamador que em essa instância a torna não compatível com POSIX. Isso não se aplica a comandos simples ou outros tipos de comandos compostos (que sãozsh
encapsulados internamente em{...}
).