Dividir string em dois pontos em / bin / sh

9

Meu script dash usa um parâmetro na forma de hostname:port , por exemplo:

myhost:1234

Considerando que a porta é opcional, ou seja:

myhost

Eu preciso ler o host e a porta em variáveis separadas. No primeiro caso, posso fazer:

HOST=${1%%:*}
PORT=${1##*:}

Mas isso não funciona no segundo caso, quando a porta foi omitida; echo ${1##*:} simplesmente retorna o nome do host, em vez de uma string vazia.

No Bash, eu poderia fazer:

IFS=: read A B <<< asdf:111

Mas isso não funciona em dash .

Posso dividir a string em : no traço, sem invocar programas externos ( awk , tr , etc.)?

    
por Martin Vegter 15.01.2018 / 09:21

4 respostas

17

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 de IFS=: read -r A B rest_ignored ou [::1] e 443 (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 '' em zsh ou bash 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) e bash , aqui as strings são implementadas usando arquivos temporários, portanto, geralmente é menos eficiente do que usar ${x#y} ou operadores split + glob.
por 15.01.2018 / 09:27
6

Basta remover o : em uma instrução separada; também, remova $ host da entrada para obter a porta:

host=${1%:*}
port=${1#"$host"}
port=${port#:}
    
por 15.01.2018 / 09:28
3

Outro pensamento:

host=${1%:*}
port=${1##*:}
[ "$port" = "$1" ] && port=''
    
por 15.01.2018 / 15:32
1

Uma string aqui é apenas um atalho sintático para um documento aqui de uma única linha.

$ set myhost:1234
$ IFS=: read A B <<EOF
> $1
> EOF
$ echo "$A"
myhost
$ echo "B"
1234
    
por 15.01.2018 / 20:43