【问题标题】:bash 4: Generic access to substring (n) of string by arbitrary delimiter?bash 4:通过任意分隔符对字符串的子字符串(n)进行通用访问?
【发布时间】:2016-01-26 21:30:29
【问题描述】:

假设我有以下字符串:x="number 1;number 2;number 3"

通过${x%%";"*}访问first子字符串成功,通过${x##*";"}访问last子字符串:

$ x="number 1;number 2;number 3"
$ echo "front : ${x%%";"*}"  #front-most-part
number 1
$ echo "back  : ${x##*";"}"  #back-most-part
number 3
$

如何访问中间部分:(例如number 2)?
如果我有(很多......)更多部分而不是三个部分,是否有更好的方法来做到这一点?
换句话说:是否有访问子字符串编号的通用方法。n 字符串 yyy,由字符串 xxx 分隔,其中 xxx 是任意字符串/分隔符?


我已经阅读了How do I split a string on a delimiter in Bash?,但我特别不想遍历字符串,而是直接访问给定的子字符串。

这个具体不问也不拆分成数组,而是拆分成子字符串。

【问题讨论】:

  • 试试这个cut -d';' -f2 <<< $x
  • @Kalanidhi 那 only 访问第二个子字符串,最后我想对所有子字符串进行分隔访问(这实际上发生在循环和对任何子字符串是必需的)。
  • @dev-null 不,我要求拆分为字符串,而不是数组(这实际上发生在多维数组的循环中,所以我不想要数组的子数组)。

标签: string bash substring


【解决方案1】:

使用固定索引:

x="number 1;number 2;number 3"

# Split input into fields by ';' and read the 2nd field into $f2
# Note the need for the *2nd* `unused`, otherwise f2 would 
# receive the 2nd field *plus the remainder of the line*.
IFS=';' read -r unused f2 unused <<<"$x"

echo "$f2"

一般来说,使用数组:

x="number 1;number 2;number 3"

# Split input int fields by ';' and read all resulting fields
# into an *array* (-a).
IFS=';' read -r -a fields <<<"$x"

# Access the desired field.
ndx=1
echo "${fields[ndx]}"

约束

使用IFS,指定I内部F字段S分隔符字符的特殊变量,总是意思是:

  • 只有单个,文字字符可以作为字段分隔符。

    • 但是,您可以指定多个个字符,在这种情况下,任何个字符都被视为分隔符。
  • 默认分隔符为$' \t\n' - 即空格、制表符和换行符,并且始终考虑其中的运行(多个连续实例) 单个分隔符;例如,'a b'2 个字段 - 多个空格算作一个分隔符。

  • 相比之下,与任何其他字符,运行中的字符被单独考虑,因此单独的字段;例如,'a;;b'3 个字段 - 每个 ; 是它自己的分隔符,所以 ;; 之间有一个空字段。

read -r -a ... &lt;&lt;&lt;... 技术通常效果很好,只要

  • 输入是单行
  • 您不必担心尾随的空字段会被丢弃

如果您需要解决上述问题的完全通用、稳健的解决方案, 使用以下变体,在@gniourf_gniourf answer here 中进行了解释:

sep=';' 
IFS="$sep" read -r -d '' -a fields < <(printf "%s${sep}\0" "$x")    

注意需要使用-d ''一次读取多行输入,并且需要用另一个分隔符实例终止输入以保留尾随空字段;需要尾随 \0 以确保 read 的退出代码为 0

【讨论】:

  • 比 Stackoverflow 更快,让我接受它作为正确答案;)谢谢!
  • 请用它来打断一个字符串:IFS=';' read -r -d '' -a fields &lt; &lt;(printf '%s;\0' "$x")。在链接的问题中查看我的答案。
  • @gniourf_gniourf:好点,我已经更新了答案(和 ++ 用于您的链接答案)。
【解决方案2】:

不要使用:

创建一个分隔符为;的数组:

x="number 1;number 2;number 3"
_IFS=$IFS; IFS=';'
arr=($x)
IFS=$_IFS

echo ${arr[0]} # number 1
echo ${arr[1]} # number 2
echo ${arr[2]} # number 3

【讨论】:

  • 在分隔符上“拆分”字符串(我应该说是为了引入错误)是 the 损坏的方法,不幸的是,它被广泛传播,因为它是主题路径名扩展(通配符)。然后“修复”是使用set -f(但这并不能解决所有问题,因为它仍然会连接多个连续的空字段);在这一点上,您可能会觉得您正在与 反对 shell:那是因为您使用的是 antipattern。 链接的问题显示了拆分字符串的规范方法:IFS=\; read -r -d '' -a arr &lt; &lt;(printf '%s;\0' "$x")(而且只有一行!)。
  • 当我说 它仍然会连接多个连续的空字段时,我的意思是当与IFS 的空格一起使用时(这里不是这种情况)。虽然在这种情况下,如果最后一个字段为空,它将删除它。
  • @gniourf_gniourf:globbing 参数是有效的(并且必须设置和恢复 2 个配置项(IFSset -f)最终使这个解决方案变得很麻烦(尽管它与read ... &lt;&lt;&lt;... 相比可能具有轻微的性能优势,并且与read ... &lt; &lt;(printf ...) 相比可能更多)。但是,据我所知,考虑到制表符、空格、换行符的 runs 并没有逃脱 shell single 分隔符(连接多个连续的空字段) - 此逻辑内置于 IFS,因此它会影响数组文字语法以及 read
  • @mklement0 当IFS 设置为空格字符时会发生这种情况,例如空格或制表符或换行符(因为 Bash 以特殊方式处理这些字符)。试试看:a='a b'ab 之间有 2 个空格),然后是 IFS=' '; ary=( $a )。您会看到空字段被丢弃。虽然这实际上看起来像我们一般想要的,但是当尝试使用IFS=$'\n'; a=( $(echo a; echo; echo b) ) 来吞食数组中命令的输出时可能会令人惊讶。这里没有保留空行……这是好是坏并不重要;只是需要注意的事情!
  • @gniourf_gniourf:完全同意,我更新的答案描述了所有这些。我的意思是:IFS 的这种行为是不可避免的,而不是 this 的缺点,($x) 方法 - 我现在怀疑你从来没有暗示过,您只是指出了一个一般限制 - 所以我认为我们完全同意。
猜你喜欢
  • 2016-04-28
  • 1970-01-01
  • 1970-01-01
  • 2012-07-31
  • 2014-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多