Como uma solução shell, getopts
é provavelmente mais fácil. A coisa sobre getopts
é que é especificado em POSIX para fazer exatamente o que você está pedindo - processar um byte-stream em um loop de shell. Eu sei que soa estranho, porque, se você é como eu antes de eu aprender isso, você provavelmente está pensando, bem, puxa, eu pensei que era para lidar com opções de linha de comando. verdade, mas assim é a primeira coisa. Considere:
-thisisonelongstringconsistingofseparatecommandlineswitches
Sim, getopts
precisa lidar com isso. Ele tem que dividir esse caractere por char em um loop e retornar a você cada caractere na variável de shell $OPTARG
ou em outro que você especificar por nome, dependendo de quão específico você é quando você o chama. Além do mais, ele precisa retornar erros nas variáveis do shell e salvar seu progresso quando isso ocorrer na variável do shell $OPTIND
, de modo que ele possa retomar exatamente de onde parou se você pode de alguma forma resolver isso. E tem que fazer todo o trabalho sem invocar uma única subcamada.
Então, digamos que temos:
arg=$(seq -s '' 1000); set --
while getopts :0123456789 v -"${arg}"
do [ "$((i=$i+1<6?$i+1:0))" -gt 0 ] ||
set "$@" "$v"
done
Hmmm .... eu me pergunto se funcionou?
echo "$((${#arg}/6))" "$#"
482 482
Isso é legal ...
eval '
printf %.1s\n "${arg#'"$(printf %0$((124*6-1))d | tr 0 \?)"'}" "${124}"'
4
4
Então, como você pode ver, o comando getopts
definiu completamente a matriz para cada sexto byte na string. E não tem que ser números como este - nem deve ser até mesmo caracteres seguros para shell - e você não precisa nem especificar os caracteres de destino como eu fiz acima com 01234565789
. Eu testei isso repetidamente em muitos shells e todos eles simplesmente funcionam. Existem algumas peculiaridades - bash
irá jogar fora o primeiro caractere se ele for um caractere de espaço em branco - dash
aceita o :
colon como um parâmetro especificado, embora seja apenas o único POSIX que proíbe especificamente. Mas nada disso importa porque getopts
ainda deposita o valor atual do char do opt em $OPTARG
mesmo quando ele retorna um erro (representado por um? Atribuído ao seu var opt especificado) e caso contrário, explicitamente $OPTARG
a menos que você tenha declarado uma opção deve ter um argumento. E o espaço em branco é uma coisa boa - ele só descarta um espaço leading , o que é excelente, porque, ao trabalhar com valores desconhecidos, você pode fazer:
getopts : o -" $unknown_value"
... para iniciar o loop sem qualquer perigo de o primeiro caractere estar realmente em sua sequência de args aceita - o que resultaria em getopts
colocando a coisa toda em $OPTARG
de uma só vez - como um argumento.
Aqui está outro exemplo:
OPTIND=1
while getopts : o -" $(dd if=/dev/urandom bs=16 count=1 2>/dev/null)"
do printf '\%04o' "'$OPTARG"; done
-thisisonelongstringconsistingofseparatecommandlineswitches
4050arg=$(seq -s '' 1000); set --
while getopts :0123456789 v -"${arg}"
do [ "$((i=$i+1<6?$i+1:0))" -gt 0 ] ||
set "$@" "$v"
done
716520echo "$((${#arg}/6))" "$#"
482 482
7061eval '
printf %.1s\n "${arg#'"$(printf %0$((124*6-1))d | tr 0 \?)"'}" "${124}"'
4
4
647415getopts : o -" $unknown_value"
1215227146OPTIND=1
while getopts : o -" $(dd if=/dev/urandom bs=16 count=1 2>/dev/null)"
do printf '\%04o' "'$OPTARG"; done
%pre%4050%pre%716520%pre%7061%pre%647415%pre%1215227146%pre%5766
5766
Eu defini $OPTIND=1
na primeira linha porque usei apenas getopts
e, até você redefini-la, espera que a próxima chamada continue de onde parou. Em outras palavras, ela quer "${arg2}"
. Mas eu não sinto vontade de dar e estou fazendo uma coisa diferente agora, então eu deixo saber, redefinindo $OPTIND
em que ponto é bom ir.
Neste eu usei zsh
- que não fala sobre um espaço principal - e então o primeiro caractere é octal 40 - o caractere de espaço. Eu normalmente não uso getopts
dessa forma - normalmente eu o uso para evitar fazer um write()
para cada byte e em vez disso atribuir sua saída - que vem em uma variável - para outra variável de shell - como eu fiz acima com set
depois de uma moda. Então, quando eu estiver pronto, posso pegar a string inteira e quando eu costumo tirar o primeiro byte.