Basta fazer:
case $1 in
(*:*) host=${1%:*} port=${1##*:};;
(*) host=$1 port=$default_port;;
esac
Você pode querer alterar o case $1
para case ${1##*[]]}
para considerar valores de $1
como [::1]
(um endereço IPv6 sem a parte porta ).
Para dividir, você pode usar o operador split + glob (deixe uma expansão de parâmetro sem aspas), afinal, é para isso:
set -o noglob # disable glob part
IFS=: # split on colon
set -- $1 # split+glob
host=$1 port=${2:-$default_port}
(embora isso não permita nomes de host que contenham dois pontos (como para o endereço IPv6 acima)).
Esse operador split + glob fica no caminho e causa tanto dano no resto do tempo que parece justo que seja usado sempre que for necessário (embora, eu concordei que é muito difícil de usar especialmente considerando que POSIX sh
não tem suporte para escopo local, nem para variáveis ( $IFS
aqui) nem para opções ( noglob
here) (embora ash
e derivados como dash
são alguns dos que fazem (junto com as implementações AT & T de ksh
, zsh
e bash
4.4 e acima)).
Observe que IFS=: read A B <<< "$1"
tem alguns problemas próprios:
- você esqueceu o
-r
, o que significa que a barra invertida passará por algum processamento especial. - dividiria
[::1]:443
em[
e:1]:443
em vez de[
e a string vazia (para a qual você precisaria deIFS=: read -r A B rest_ignored
ou[::1]
e443
(para o qual você pode) t use essa abordagem) - elimina tudo além da primeira ocorrência de um caractere de nova linha, por isso não pode ser usado com sequências arbitrárias (a menos que você use
-d ''
emzsh
oubash
e os dados não contenham caracteres NUL, mas, em seguida, observe que herestrings (ou heredocs) adicionam um caractere extra de nova linha!) - em
zsh
(de onde vem a sintaxe) ebash
, aqui as strings são implementadas usando arquivos temporários, portanto, geralmente é menos eficiente do que usar${x#y}
ou operadores split + glob.