一个古老的(在 initramfs 内部)的版本ipconfig
要求其用户输入仅提供最多 7 个冒号分隔的元素,例如:
ip=client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf
ipconfig
当用户提供超过 7 个元素时会导致错误。
因此,额外的(2 个 DNS 解析器)应该被砍掉。
subshell
这可以在with内部完成cut
,例如:
validated_input=$(echo ${user_input} | cut -f1,2,3,4,5,6,7 -d:)
如何使用参数扩展/替换来cut
编写这样的代码?(b)ash
没有:
- 启动子shell/子进程(管道)
- IFS-争论/破坏
由于 (1) 速度,请参见使用 bash 变量替换代替 cut/awk和(2)学习。
换句话说:如何查找第 n(第 7)个字符出现并删除/修剪从那里直到字符串末尾的所有内容?
答案1
这仅使用参数扩展:
${var%:"${var#*:*:*:*:*:*:*:}"}
例子:
$ var=client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf:morefields:another:youwantanother:haveanother:
$ echo "${var%:"${var#*:*:*:*:*:*:*:}"}"
client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf
感谢 ilkkachu 提出了对尾随的修复:
!
${parameter#word}
${parameter##word}
该单词被扩展以产生一个模式,就像文件名扩展一样(请参阅文件名扩展)。如果模式与参数扩展值的开头匹配,则扩展的结果是具有最短匹配模式(“#”情况)或最长匹配模式(“##”情况)的参数扩展值已删除。如果参数是“@”或“',模式移除操作依次应用于每个位置参数,扩展是结果列表。如果参数是一个数组变量,下标为“@”或“',模式删除操作依次应用于数组的每个成员,扩展就是结果列表。
这将尝试匹配开始你的参数,如果是的话,它会删除它。
例子:
$ var=a:b:c:d:e:f:g:h:i
$ echo "${var#a}"
:b:c:d:e:f:g:h:i
$ echo "${var#a:b:}"
c:d:e:f:g:h:i
$ echo "${var#*:*:}"
c:d:e:f:g:h:i
$ echo "${var##*:}" # Two hashes make it greedy
i
${parameter%word}
${parameter%%word}
该单词被扩展以产生一个模式,就像文件名扩展一样。如果模式与参数扩展值的尾部部分匹配,则扩展的结果是具有最短匹配模式(“%”情况)或最长匹配模式(“%%”情况)的参数值已删除。如果参数是“@”或“',模式移除操作依次应用于每个位置参数,扩展是结果列表。如果参数是一个数组变量,下标为“@”或“',模式删除操作依次应用于数组的每个成员,扩展就是结果列表。
这将尝试匹配结尾你的参数,如果是的话,它会删除它。
例子:
$ var=a:b:c:d:e:f:g:h:i
$ echo "${var%i}"
a:b:c:d:e:f:g:h:
$ echo "${var%:h:i}"
a:b:c:d:e:f:g
$ echo "${var%:*:*}"
a:b:c:d:e:f:g
$ echo "${var%%:*}" # Two %s make it greedy
a
所以在答案中:
${var%:"${var#*:*:*:*:*:*:*:}"}
(请注意周围的引号,${var#...}
以便将其视为要从 末尾删除的文字字符串(而不是模式)$var
)。
当应用于:
var=client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf:morefields:another:youwantanother:haveanother:
${var#*:*:*:*:*:*:*:}
=morefields:another:youwantanother:haveanother:
其内部扩展${var%: ... }
如下:
${var%:morefields:another:youwantanother:haveanother:}
所以你是说给我:
client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf:morefields:another:youwantanother:haveanother:
但把:morefields:another:youwantanother:haveanother:
末端剪掉。
Bash 参考手册 (3.5.3)
答案2
使用正则表达式:
$ var=a:b:c:d:e:f:g:h:i:j:k:l
$ [[ $var =~ ([^:]*:){6}([^:]*) ]] && echo "${BASH_REMATCH[0]}"
a:b:c:d:e:f:g
这应该在标准 shell 中工作:
#!/bin/sh
var=a:b:c:d:e:f:g:h:i:j:k:l
while true; do
case "$var" in
*:*:*:*:*:*:*:*) var=${var%:*} ;;
*) break ;;
esac
done
echo "$var"
或者,如果我们允许设置IFS
持续时间read
:
$ IFS=: read -a arr <<< "$var"
$ arr=("${arr[@]:0:7}")
$ echo "${arr[@]}"
a b c d e f g
$ printf "%s:%s:%s:%s:%s:%s:%s\n" "${arr[0]}" "${arr[1]}" "${arr[2]}" "${arr[3]}" "${arr[4]}" "${arr[5]}" "${arr[6]}"
a:b:c:d:e:f:g
答案3
在zsh
:
$ ip=client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf:x:y:z
$ echo ${(j(:))${"${(@s(:))ip}"[1,7]}}
client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf
${(s(:))var}
分裂:
"${(@)...}"
:确保保留空元素(如"$@"
)${var[1,7]}
元素 1 至 7${(j(:))var}
将元素加入:
或者你可以这样做:
$ set -o extendedglob
$ echo ${(M)ip##([^:]#:)(#c0,6)[^:]#}
client-ip:server-ip:gw-ip:netmask:hostname:device:autoconf
${var##pattern}
就像在 ksh 中一样,剥去与模式匹配的最长前导部分。(M)
: 扩展到中号固定部分而不是剥离它x#
: 0 个或多个x
s (如 regexp*
)(#c0,6)
从 0 到 6 的前一个原子(如 ksh 的{x,y}(...)
)