是否有“| cut -f1,2,3 -d:”的参数替换/扩展替代方案,又名在第 n 个字符出现后(包括第 n 个字符出现)进行修剪?

是否有“| cut -f1,2,3 -d:”的参数替换/扩展替代方案,又名在第 n 个字符出现后(包括第 n 个字符出现)进行修剪?

一个古老的(在 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 个或多个xs (如 regexp *
  • (#c0,6)从 0 到 6 的前一个原子(如 ksh 的{x,y}(...)

相关内容