/bin/sh 中的冒号分割字符串

/bin/sh 中的冒号分割字符串

我的dash脚本采用 形式的参数hostname:port,即:

myhost:1234

而端口是可选的,即:

myhost

我需要将主机和端口读取到单独的变量中。对于第一种情况,我可以这样做:

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

但在第二种情况下,当端口被省略时,这不起作用;echo ${1##*:}仅返回主机名,而不是空字符串。

在 Bash 中,我可以这样做:

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

但这在dash.

我可以:在破折号中拆分字符串,而不调用外部程序(awktr等)吗?

答案1

做就是了:

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

您可能需要更改 tocase $1case ${1##*[]]}考虑$1类似的值[::1](没有港口部分)。

要拆分,您可以使用分割+全局运算符(保留参数扩展不带引号),因为这就是它的用途:

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

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

(尽管这不允许主机名包含冒号(如上面的 IPv6 地址))。

split+glob 运算符妨碍并造成如此大的伤害其余时间,只要需要就可以使用它,这似乎是公平的(不过,我同意使用起来非常麻烦,特别是考虑到 POSIXsh不支持本地范围,既不支持变量($IFS此处)也不支持选项) (noglob此处)(尽管ash和 类似的衍生物dash是其中一些(以及kshzshbash4.4 及以上版本的 AT&T 实现))。

请注意,它IFS=: read A B <<< "$1"本身有一些问题:

  • 您忘记了,-r这意味着反斜杠将经过一些特殊处理。
  • 它会分成[::1]:443and[:1]:443不是[and 空字符串(为此您需要IFS=: read -r A B rest_ignoredor [::1]and 443(为此您不能使用该方法)
  • 它会删除第一次出现换行符之后的所有内容,因此它不能与任意字符串一起使用(除非您-d ''zshor中使用bash并且数据不包含 NUL 字符,但请注意herestrings(或heredocs)确实添加了一个额外的换行符!)
  • in zsh(语法来源)和bash,这里的字符串是使用临时文件实现的,因此它通常比使用${x#y}或 split+glob 运算符效率低。

答案2

:只需在单独的语句中删除;另外,从输入中删除 $host 以获取端口:

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

答案3

另一个想法:

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

答案4

这里字符串只是单行此处文档的语法快捷方式。

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

相关内容