如何使用关键字进行字符串分隔?

时间:2014-07-25 13:58:41

标签: bash shell unix ksh

我的STRING如下所示。每个键之间没有特定的分隔符。唯一的方法是确定密钥使用关键字" key_1"或" key_2"等。

所有键都以"键_"开头。而且永远不会出现在另一个人的价值中:

STRING="key_1=mislanious_string1 key_2=miscellaneous_string2"

我想要输出如下。

echo $STRING1应打印:

key_1=mislanious_string1

echo $STRING2应打印:

key_2=mislanious_string2

e.g: 如果STRING =" key_1 = foobarzkey_2 = bash" ,然后输出应该看起来像,STRING1 = key_1 = foobarz和STRING2 = key_2 = bash。

可能有更多的键,如key_1,key_2,key_3等。每个键都以"键_"开头。而且永远不会出现在另一个人的价值中:

如何在UNIX bash shell中使用它?

4 个答案:

答案 0 :(得分:6)

使用grep -P(PCRE)支持输入中的多个键值对:

STRING="key_1=mislanious_string1key_2=miscellaneous_string2key_3=fookey_4=BASH"
grep -oP 'key_[^=]+=.*?(?=key_|$)' <<< "$STRING"
key_1=mislanious_string1
key_2=miscellaneous_string2
key_3=foo
key_4=BASH

要将它们存储到BASH数组中,您可以使用:

read -d '' -ra arr < <(grep -oP 'key_[^=]+=.*?(?=key_|$)' <<< "$STRING")
printf "%s\n" "${arr[@]}"
key_1=mislanious_string1
key_2=miscellaneous_string2
key_3=foo
key_4=BASH

declare -p arr
declare -a arr='([0]="key_1=mislanious_string1" [1]="key_2=miscellaneous_string2" [2]="key_3=foo" [3]="key_4=BASH")'

UPDATE :: 这是一种纯BASH (非gnu)分割这些字符串的方法。我们首先在每次出现key_字符串之前插入一个不可见的字符,然后用它来分割字符串:

STRING="key_1=mislanious_string1key_2=miscellaneous_string2key_3=fookey_4=BASH"
c=$'\x06'
s="${STRING//key_/${c}key_}"
arr=()
while [[ "$s" =~ ${c}(key_[^=]+=[^${c}]+)(.*) ]]; do
   arr+=( "${BASH_REMATCH[1]}" )
   s="${BASH_REMATCH[2]}"
done

然后测试:

printf "<%s>\n" "${arr[@]}"
<key_1=mislanious_string1>
<key_2=miscellaneous_string2>
<key_3=foo>
<key_4=BASH>

答案 1 :(得分:3)

我最喜欢anubhava的grep -oP解决方案。这是一个awk解决方案:

STRING="key_15=foobarzkey_3=bash"
awk -v RS="key_" 'NR>1{split($0, a, /=/); print "STRING" a[1] "=" RS $0}' <<< "$STRING"
STRING15=key_15=foobarz
STRING3=key_3=bash

因此,将该输出创建为shell变量

eval $(awk -v RS="key_" 'NR>1{split($0, a, /=/); print "STRING" a[1] "=" RS $0}' <<< "$STRING")
echo $STRING3    # => key_3=bash
echo $STRING15   # => key_15=foobarz

答案 2 :(得分:1)

这个答案最初并没有识别出前面没有空格的键。这已得到修复。在目前的形式中,这个答案提供了作为便携式解决方案的价值。如果您不同意,请告诉我。

Glenn Jackmananubhava提供的答案很有帮助,但使用 GNU 扩展程序并非在所有平台上都可用grep -Pawk,带有多个字符。RS值。

此处有一个符合POSIX标准 sed解决方案,应该可以在大多数平台上运行,使用bash,{{1} },或ksh作为shell:

zsh

请注意,小写变量名称(str='key_1=mislanious_string1 key_2=miscellaneous_string2key_3=last' while read -r varDef; do [[ -n $varDef ]] && typeset "$varDef" done < <(sed 's/\(key_\([0-9]\{1,\}\)=\)/\'$'\n''string\2=\1/g' <<<"$str") #'# Print the variables created ($string1, $string2, $string3). typeset -p ${!string@} ,...)用于防止与环境变量的潜在冲突。

  • string1用于将字符串拆分为键值标记,每个标记位于各自的行上,前面是所需的目标变量名称和sed,有效地输出 shell变量赋值;例如,对于=,sed命令传出:
    key_1
  • string1=key_1=mislanious_string1 循环然后读取每个输出行并使用while声明并分配变量(请注意typeset被选为typeset兼容性 - 而{{1 }}也适用于kshtypeset您通常在那里使用bash; zsh忽略declare输出开头的空行。
  • 注意:此解决方案根据值修剪尾随空格,与问题中的示例一致。这种修剪是由于[[ -n $varDef ]]使用默认sed值(内部字段分隔符)而发生的 - 为了保留尾随空格,只需使用read而不是$IFS
  • 还要注意,需要使用进程替换来提供输入(IFS= read)(而不是管道(read),以确保变量在当前shell中定义< / em>(而不是在子shell 中,这会导致当前shell看不到变量。)

有关上述while ... <(sed ...)命令POSIX兼容的一些背景信息:

  • POSIX仅为sed ... | while ...强制执行基本正则表达式,这会消除许多功能(例如量词sedsed,交替(? }))并使转义变得更加繁琐(例如,+|必须( - 转义)。
  • POSIX )也不支持转义序列,例如传递给\替换字符串中的sed,因此ANSI-C quoting是用于使用\ns - 转义实际换行符拼接到替换字符串中。

作为非POSIX GNU \扩展有用的示例,这里是一个充分利用GNU sed功能的等效命令(< em>扩展正则表达式,支持$'\n'),从而产生更短且更易读的命令:

sed

答案 3 :(得分:0)

有时可以忽略最简单的解决方案:

STRING="key_1=mislanious_string1key_2=miscellaneous_string2"

read STRING1 STRING2<<<${STRING//key_/ key_}

echo $STRING1
echo $STRING2