Análise de opção ZSH com zparseopts

Aqui está um pequeno truque que eu tive que usar para obter o resultado desejado da implementação do meu analisador de opções. Eu estava usando zparseopts, um equivalente zsh de conchas bourne getopt(s).

Deixe-me ilustrar o problema que enfrentei, se alguém tiver uma maneira (mais) idiomática de fazer isso (também conhecido como esqueci algum recurso / opção), eu estaria interessado em descobrir.

O que eu queria era uma análise de opção repetida. Não tenho nenhum valor padrão, também não gosto de usar um optsarray, pois acho que também poderia demonstrar essa rota depois, pois ela destaca perfeitamente meu dilema.

Digamos que eu tenha uma função que aceita vários argumentos de arquivo. Claro que eu poderia usar uma lista de nomes de arquivo separados por dois pontos ou por espaço, mas eu queria / precisava deles como opções separadas.

#!/usr/bin/env zsh

function zomg/check()
{
zparseopts
-D -E -- f+:=files -files+:=files
}

O que acabei de fazer foi declarar uma função chamada zomg/checke nela coloquei nosso analisador de opções que tem a -Dchave (para remover quaisquer opções encontradas na lista de argumentos posicionais) e a -Echave (para não parar quando opções desconhecidas – não em as especificações – são encontradas) definidas. Eu geralmente gosto de manter esses dois por perto, embora tecnicamente eles não sejam realmente necessários aqui ainda.

As partes seguintes --são chamadas de especificações e devem estar no formato . No nosso caso, temos duas especificações que se referem ao mesmo array, a saber . A primeira é a opção e a segunda são as opções longas (denotadas por um único hífen de prefixo ). O sinal de mais indica que desejamos que essa opção seja usada repetidamente, se não exigirmos isso explicitamente, apenas a última fornecida ou será armazenada. Os únicos dois pontos indicam que esta opção é opcional.opt[=array]files-f--files-+-f--file:

Para tornar um pouco mais fácil, aqui está uma reescrita da mesma função sem o extra apenas esqueleto:

zparseopts -- f+:=storage -files+:=storage

Agora, nosso armazenamento é do tipo array, portanto, devemos ser capazes de referenciá-lo usando o índice para recuperar elementos.

Vamos dar uma olhada rápida em como o array de variáveis ​​(lista) fica depois de passá-lo zomg/check -f foo -f bar -f baz

print $storage[@]
-f foo -f bar -f baz

Como você pode ver, este é o padrão repetido e verificar quantos elementos este array possui

print ${#storage}

Vai nos dizer 6como resultado. Isso nos deixa com a tarefa de filtrar os valores. Como esse array não é do tipo associativo (dicionário, mapa hash, armazenamento de chave / valor), não podemos simplesmente fazer e obter nossa lista de 3 elementos retornada. Conforme eu vim, encontrei duas maneiras razoavelmente fáceis de expressar os valores.${(v)storage}foo bar baz

A primeira é a mais fácil: eliminamos todas as ocorrências de em nossa matriz:-f

storage=("${(@)storage:#-f}")
print $storage[@]

Irá retornar foo bar baze testar o comprimento do array usando retornará como resposta. No entanto, ele não conseguiria remover nenhum do array.${#storage}3--files

O segundo método é um pouco mais elaborado, embora também mais divertido, e não falha em remover qualquer opção longa de opções de sinônimo / alias. Raciocinamos como se tivéssemos um dicionário de pares de valores-chave (use um auxiliar) para obter uma lista de inteiros pares e ímpares. Os números ímpares começando em 1 serão as chaves e, subsequentemente, os números pares formam os nossos valores:

typeset -A helper
helper
=($(seq 1 ${#storage}))

O que acabamos de fazer é criar um array associativo que é preenchido pela sequência de números neste caso 6. O array gerado por zparseopts nos garante um número par de elementos, contanto que testemos o valor zero no segundo elemento. Eu sempre uso para isso.1..to..number of elements in arraytest -z ${storage[2]}

Com esses pares de chave / valor de , e agora podemos obter facilmente os elementos de nossa matriz unidimensional regular por meio de nossos valores como número de índice:1 and 23 and 45 and 6

for item in ${(@v)helper}
do
print ${storage[$item]}
done

Isso fará um loop de números 2, 4e 6que se correlacionam com as posições na lista (-f foo -f bar -f baz)e, como tal, extraem os dados para nós.