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


9

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

myhost:1234

而port是可选的,即:

myhost

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

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

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

在Bash中,我可以这样做:

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

但这不适用于dash

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


4
如果要支持IPv6,请确保在最后一个冒号上进行分割,并且不要在方括号内的冒号上进行分割
Ferrybig

@Ferrybig %%使它变得贪婪(而不是%),因此它实际上至少部分是这样做的;它不起作用##
jpaugh

Answers:


18

做就是了:

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

您可能需要更改case $1case ${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}

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

该split + glob运算符会妨碍您的操作,并在其余时间造成很大的损害,以至于看起来很公平,可以在需要时使用它(尽管我会同意使用它非常麻烦,尤其是考虑到POSIX sh没有对于本地范围支持,既不是变量($IFS这里),也不选项(noglob在这里)(虽然ash和衍生物喜欢dash是一些做(与AT&T的实现一起的那些的kshzshbash4.4和以上))。

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

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

7
在2018年,作为新年的决议,我们所有人都应该停止编写将随IPv6中断的脚本。
Philippos

@Philippos太迟了两个星期!
罗恩

@RonJohn:迟到了二十年,不知何故。
Philippos '18

6

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

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


1

here字符串只是单行here文档的语法快捷方式。

$ set myhost:1234
$ IFS=: read A B <<EOF
> $1
> EOF
$ echo "$A"
myhost
$ echo "B"
1234
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.