/ Bin / sh içinde iki nokta üst üste dizeyi böl


9

Benim dashkomut dosyası biçiminde bir parametre alır hostname:portyani:

myhost:1234

Bağlantı noktası isteğe bağlıdır, yani:

myhost

Ana bilgisayar ve bağlantı noktasını ayrı değişkenlere okumam gerekiyor. İlk durumda şunları yapabilirim:

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

Ancak bu, bağlantı noktası atlandığında ikinci durumda çalışmaz; echo ${1##*:}boş bir dize yerine anasistem adı döndürür.

Bash'te şunları yapabilirim:

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

Ama bu işe yaramıyor dash.

Ben dize bölmek Can :dış programlar (söz etmeksizin çizgi içinde awk, trvs.)?


4
IPv6'yı desteklemek istiyorsanız son
köşeye

@ Ferrybig %%açgözlü yapar (aksine %), aslında bunu yapar, en azından kısmen; ile çalışmaz ##.
jpaugh

Yanıtlar:


18

Sadece yap:

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

Sen değiştirmek isteyebilirsiniz case $1için case ${1##*[]]}değerleri için hesaba $1gibi [::1](olmadan IPv6 adresi liman kısmında).

Bölmek için split + glob operatörünü kullanabilirsiniz (bir parametre genişletmesini derecelendirilmemiş olarak bırakın).

set -o noglob # disable glob part
IFS=:         # split on colon
set -- $1     # split+glob

host=$1 port=${2:-$default_port}

(ancak bu, iki nokta üst üste içeren ana makine adlarına izin vermez (yukarıdaki IPv6 adresi için olduğu gibi)).

Bu split + glob operatörü yoluna girer ve o kadar çok zarara neden olur ki , gerektiğinde sadece adil görünecektir (yine de, özellikle POSIX'in shhiç olmadığı düşünülerek kullanmanın çok hantal olduğunu kabul edeceğim. değişkenler için (de, yerel kapsam destek $IFSburaya) ne amaç için ( noglobburada) (olsa ashtürevleri gibi ve dashbirlikte AT & T uygulamaları ile (do olanlardan bazıları ksh, zshve bashyukarıda 4,4 ve)).

Bunun IFS=: read A B <<< "$1"birkaç sorunu olduğunu unutmayın :

  • Unuttuğun -rbazı özel işleme tabi olacak ters eğik çizgi hangi araçları.
  • o bölmek [::1]:443içine [ve :1]:443yerine [(ve boş dize kendisi için ihtiyacım olacağını IFS=: read -r A B rest_ignoredya [::1]ve 443) (bunlar için bu yaklaşımı kullanamazsınız
  • Kullanmak sürece (keyfi dizeleri ile kullanılamaz nedenle, yeni satır karakteriyle ilk geçtiği geçmiş herşeyi şeritler -d ''halinde zshveya bashve veriler herestrings (veya heredoc boş karakter ama sonra dikkat etmeyen bir eklerim) ekstra yeni satır karakteri!)
  • içinde zsh(sözdizimi nereden geldiğini) ve bashgenellikle kullanmaktan daha az verimli yüzden, burada dizeleri, geçici dosyaları kullanılarak uygulanan ${x#y}veya bölünmüş + glob operatörler.

7
2018'de, bir Yeni Yıl kararı olarak, hepimiz IPv6 ile kesilecek komut dosyaları yazmayı bırakmalıyız.
Philippos

@Philippos iki hafta geç kaldı!
RonJohn

@ RonJohn: Yirmi yıl çok geç, her nasılsa.
Philippos

6

Sadece :ayrı bir ifadede kaldırın ; Ayrıca, bağlantı noktasını almak için $ host'u girişten kaldırın:

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

3

Başka bir düşünce:

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

1

Burada dizge, burada tek satırlık bir belge için sözdizimsel bir kısayoldur.

$ set myhost:1234
$ IFS=: read A B <<EOF
> $1
> EOF
$ echo "$A"
myhost
$ echo "B"
1234
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.