web-dev-qa-db-ja.com

/ bin / shのコロンで文字列を分割

私のdashスクリプトは、hostname:portの形式のパラメーターを受け取ります。つまり、

myhost:1234

一方、ポートはオプションです。

myhost

ホストとポートを別々の変数に読み込む必要があります。最初のケースでは、私は行うことができます:

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

しかし、ポートが省略されている場合、それは2番目のケースでは機能しません。 echo ${1##*:}は、空の文字列ではなく、単にホスト名を返します。

バッシュでは、私はできる:

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

しかし、それはdashでは機能しません。

外部プログラム(awktrなど)を呼び出さずに、:の文字列をダッシュ​​で分割できますか?

9
Martin Vegter

ただやる:

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

case $1のようなcase ${1##*[]]}の値に対応するために$1[::1]に変更したい場合があります(port部分)。

分割するには、split + glob演算子(パラメーター展開は引用符で囲まないでください)を使用できます。

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

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

(ただし、(上記のIPv6アドレスのように)コロンを含むホスト名は許可されません)。

そのsplit + glob演算子が邪魔になり、非常に害を及ぼす 残りの時間は、必要なときにのみ使用することが公平であるように思われます(ただし、次のことは非常に煩雑であることに同意します) POSIX shはローカルスコープをサポートしておらず、変数($IFSはこちら)もオプション(noglobはこちら)もサポートしていない(特にashおよびdashのような派生物は、いくつかの派生物です(kshzshおよびbash 4.4以上のAT&T実装とともに))。

IFS=: read A B <<< "$1"にはいくつかの問題があることに注意してください。

  • -rを忘れた場合は、バックスラッシュが特別な処理を受けることを意味します。
  • [::1]:443と空の文字列([または:1]:443が必要な場合)の代わりに、[IFS=: read -r A B rest_ignored[::1]に分割します。 443(その方法は使用できません)
  • 改行文字が最初に出現した後はすべて削除されるため、任意の文字列では使用できません(zshまたはbash-d ''を使用し、データがtにはNUL文字が含まれていますが、ここでherestrings(またはheredocs)が余分な改行文字を追加することに注意してください!)
  • zsh(構文の由来)とbashでは、ここで文字列は一時ファイルを使用して実装されるため、${x#y}またはsplit + glob演算子を使用するよりも一般的に効率がよくありません。
18

別のステートメントで:を削除するだけです。また、入力から$ Hostを削除してポートを取得します。

Host=${1%:*}
port=${1#"$Host"}
port=${port#:}
6
choroba

別の考え:

Host=${1%:*}
port=${1##*:}
[ "$port" = "$1" ] && port=''
3
glenn jackman

Here文字列は、1行のhereドキュメントの構文上のショートカットにすぎません。

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