我的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中使用它?
答案 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 Jackman和anubhava提供的答案很有帮助,但使用 GNU 扩展程序并非在所有平台上都可用(grep -P
,awk
,带有多个字符。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 }}也适用于ksh
和typeset
您通常在那里使用bash
; zsh
忽略declare
输出开头的空行。[[ -n $varDef ]]
使用默认sed
值(内部字段分隔符)而发生的 - 为了保留尾随空格,只需使用read
而不是$IFS
。IFS= read
)(而不是管道(read
),以确保变量在当前shell中定义< / em>(而不是在子shell 中,这会导致当前shell看不到变量。)有关上述while ... <(sed ...)
命令POSIX兼容的一些背景信息:
sed ... | while ...
强制执行基本正则表达式,这会消除许多功能(例如量词sed
和sed
,交替(?
}))并使转义变得更加繁琐(例如,+
和|
必须(
- 转义)。)
也不支持转义序列,例如传递给\
的替换字符串中的sed
,因此ANSI-C quoting是用于使用\n
将s
- 转义实际换行符拼接到替换字符串中。作为非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