在 /bin/sh 中的冒号上拆分字符串

Mar*_*ter 9 shell dash string

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

myhost:1234
Run Code Online (Sandbox Code Playgroud)

而端口是可选的,即:

myhost
Run Code Online (Sandbox Code Playgroud)

我需要将主机和端口读入单独的变量。在第一种情况下,我可以这样做:

HOST=${1%%:*}
PORT=${1##*:}
Run Code Online (Sandbox Code Playgroud)

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

在 Bash 中,我可以这样做:

IFS=: read A B <<< asdf:111
Run Code Online (Sandbox Code Playgroud)

但这在dash.

能否在拆分字符串:在仪表板,而无需调用外部程序(awktr,等)?

Sté*_*las 18

做就是了:

case $1 in
  (*:*) host=${1%:*} port=${1##*:};;
  (*)   host=$1      port=$default_port;;
esac
Run Code Online (Sandbox Code Playgroud)

您可能希望更改case $1tocase ${1##*[]]}以考虑$1like 的值[::1](没有端口部分的 IPv6 地址)。

要拆分,您可以使用split+glob运算符(不带引号的参数扩展),因为这毕竟是它的用途:

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

host=$1 port=${2:-$default_port}
Run Code Online (Sandbox Code Playgroud)

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

那个 split+glob 运算符会妨碍并在其余时间造成如此大的伤害以至于在需要时使用它似乎是公平的(不过,我同意使用它非常麻烦,特别是考虑到 POSIXsh没有支持局部作用域,既不支持变量($IFS此处)也不支持选项(noglob此处)(尽管ash和派生类dash是其中的一些(与ksh,zshbash4.4 及更高版本的AT&T 实现一起))。

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

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

  • 2018年,作为新年的决心,我们都应该停止编写会打破IPv6的脚本。 (7认同)

cho*_*oba 6

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

host=${1%:*}
port=${1#"$host"}
port=${port#:}
Run Code Online (Sandbox Code Playgroud)