Teilung der Zeichenfolge am Doppelpunkt in / bin / sh

9

Mein dashSkript verwendet einen Parameter in Form von hostname:port:

myhost:1234

Während der Port optional ist, dh:

myhost

Ich muss den Host und den Port in separate Variablen lesen. Im ersten Fall kann ich tun:

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

Dies funktioniert jedoch nicht im zweiten Fall, wenn der Port weggelassen wurde. echo ${1##*:}Gibt einfach den Hostnamen anstelle einer leeren Zeichenfolge zurück.

In Bash könnte ich tun:

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

Das funktioniert aber nicht dash.

Kann ich aufgeteilt auf Zeichenfolge :in Bindestrich, ohne externe Programme aufzurufen ( awk, trusw.)?

Martin Vegter
quelle
4
Stellen Sie sicher, dass Sie den letzten Doppelpunkt teilen, wenn Sie IPv6 unterstützen möchten, und nicht den Doppelpunkt in eckigen Klammern
Ferrybig
@Ferrybig %%macht es gierig (im Gegensatz zu %), also tut es dies tatsächlich, zumindest teilweise; es würde nicht funktionieren mit ##.
Jpaugh

Antworten:

18

Mach einfach:

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

Möglicherweise möchten Sie das to ändern case $1, case ${1##*[]]}um Werte $1wie " zu berücksichtigen" [::1](eine IPv6-Adresse ohne Portteil ).

Zum Teilen können Sie den Operator split + glob verwenden (lassen Sie eine Parametererweiterung nicht in Anführungszeichen), da dies schließlich der Fall ist:

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

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

(obwohl dies keine Hostnamen zulässt, die einen Doppelpunkt enthalten (wie für diese IPv6-Adresse oben)).

Dieser Split + Glob-Operator stört und verursacht den Rest der Zeit so viel Schaden, dass es nur fair erscheint, ihn bei Bedarf zu verwenden (ich stimme jedoch zu, dass die Verwendung sehr umständlich ist, insbesondere wenn man bedenkt, dass POSIX shkeine hat Unterstützung für lokalen Bereich, weder für Variablen ( $IFShier) noch für Optionen ( noglobhier) (obwohl ashund Derivate wie dashsind einige von denen , die tun (zusammen mit AT & T - Implementierungen ksh, zshund bash4.4 und höher)).

Beachten Sie, dass IFS=: read A B <<< "$1"es einige eigene Probleme gibt:

  • Sie haben das vergessen, -rwas bedeutet, dass der Backslash einer speziellen Verarbeitung unterzogen wird.
  • es würde [::1]:443in [und :1]:443anstelle von [und in die leere Zeichenfolge aufgeteilt (für die Sie IFS=: read -r A B rest_ignoredoder [::1]und benötigen würden 443(für die Sie diesen Ansatz nicht verwenden können)
  • Es entfernt alles nach dem ersten Auftreten eines Zeilenumbruchs, sodass es nicht mit beliebigen Zeichenfolgen verwendet werden kann (es sei denn, Sie verwenden -d ''in zshoder bashund die Daten enthalten keine NUL-Zeichen. Beachten Sie jedoch, dass Herestrings (oder Heredocs) eine hinzufügen extra Zeilenumbruch!)
  • in zsh(woher die Syntax stammt) und bash, hier werden Zeichenfolgen mithilfe temporärer Dateien implementiert, sodass sie im Allgemeinen weniger effizient sind als die Verwendung von ${x#y}oder split + glob-Operatoren.
Stéphane Chazelas
quelle
7
Als Neujahrsvorsatz sollten wir 2018 alle aufhören, Skripte zu schreiben, die mit IPv6 brechen.
Philippos
@Philippos um zwei Wochen zu spät!
RonJohn
@ RonJohn: Irgendwie um zwei Jahrzehnte zu spät.
Philippos
6

Entfernen Sie einfach die :in einer separaten Anweisung; Entfernen Sie außerdem $ host aus der Eingabe, um den Port abzurufen:

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

Ein anderer Gedanke:

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

Eine Here-Zeichenfolge ist nur eine syntaktische Verknüpfung für ein einzeiliges Here-Dokument.

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