Dividir string em dois pontos em / bin / sh

9

Meu dashscript usa um parâmetro na forma de hostname:port, ou seja:

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, eu 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 dash.

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

Martin Vegter
fonte
4
Certifique-se de divisão na última cólon, se você quiser dar suporte IPv6, e não dividir em dois pontos dentro de colchetes
Ferrybig
O @Ferrybig o %%torna ganancioso (ao contrário de %), então ele realmente faz isso, pelo menos em parte; não iria funcionar ##.
jpaugh

Respostas:

18

Apenas faça:

case $1 in
  (*:*) host=${1%:*} port=${1##*:};;
  (*)   host=$1      port=$default_port;;
esac

Você pode alterar case $1para case ${1##*[]]}para contabilizar valores de $1like [::1](um endereço IPv6 sem parte da porta ).

Para dividir, você pode usar o operador split + glob (deixe uma expansão de parâmetro sem aspas), pois é para isso que serve:

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 no endereço IPv6 acima)).

Esse operador split + glob atrapalha e causa tanto dano o resto do tempo que parece justo que seja usado sempre que necessário (porém, eu concordo que é muito complicado de usar, especialmente considerando que o POSIX shnão tem suporte para escopo local, nem para variáveis ( $IFSaqui) nem para opções ( noglobaqui) (embora ashe derivados gosto dashsão algumas das que fazer (em conjunto com a AT & T implementações ksh, zshe bash4.4 e acima)).

Observe que ele IFS=: read A B <<< "$1"possui alguns problemas:

  • você esqueceu o -rque significa que a barra invertida passará por um processamento especial.
  • seria dividido [::1]:443em [e em :1]:443vez de [e a cadeia vazia (da qual você precisaria IFS=: read -r A B rest_ignoredou [::1]e 443(da qual você não pode usar essa abordagem)
  • ele retira tudo após a primeira ocorrência de um caractere de nova linha, por isso não pode ser usado com strings arbitrárias (a menos que você usar -d ''em zshou bashe os dados não contém caracteres NUL, mas, em seguida, note que herestrings (ou heredocs) não adicionar um caractere de nova linha extra!)
  • in 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 dividir + operadores glob.
Stéphane Chazelas
fonte
7
Em 2018, como uma resolução de ano novo, todos devemos parar de escrever scripts que quebrem com o IPv6.
Philippos
@ Philippos tarde demais por duas semanas!
precisa saber é o seguinte
@ RonJohn: Tarde demais por duas décadas, de alguma forma.
Philippos
6

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

host=${1%:*}
port=${1#"$host"}
port=${port#:}
choroba
fonte
3

Outro pensamento:

host=${1%:*}
port=${1##*:}
[ "$port" = "$1" ] && port=''
Glenn Jackman
fonte
1

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

$ set myhost:1234
$ IFS=: read A B <<EOF
> $1
> EOF
$ echo "$A"
myhost
$ echo "B"
1234
chepner
fonte