如何将特定分隔符之间的文本捕获到shell变量中?

6

我在指定变量时遇到了一些问题。我有一个包含普通文本的文件,在其中某个位置有括号[ ](整个文件中只有1对括号),以及它们之间的一些文本。我需要在shell(bash)变量中捕获这些括号内的文本。请问我该如何做?


你是指shell变量吗?你正在使用Bash吗? - strager
忘了提一下,是在Bash中。 - Hyph
10个回答

9

Bash/sed:

VARIABLE=$(tr -d '\n' filename | sed -n -e '/\[[^]]/s/^[^[]*\[\([^]]*\)].*$/\1/p')

如果那段文字难以理解,这里有一些解释:

VARIABLE=`subexpression`      Assigns the variable VARIABLE to the output of the subexpression.

tr -d '\n' filename  Reads filename, deletes newline characters, and prints the result to sed's input

sed -n -e 'command'  Executes the sed command without printing any lines

/\[[^]]/             Execute the command only on lines which contain [some text]

s/                   Substitute
^[^[]*               Match any non-[ text
\[                   Match [
\([^]]*\)            Match any non-] text into group 1
]                    Match ]
.*$                  Match any text
/\1/                 Replaces the line with group 1
p                    Prints the line

非常接近我的代码 - 你忘记带文件名了! - Jonathan Leffler
@Leffler,哎呀,谢谢!我测试的时候有这个问题,但是在解决方案中忘了加上它。 - strager
好的 - 你已经有了一个解决方案,可以将[...]放在一行上;我有一个解决方案,可以将[放在一行上,将]放在另一行上。如何结合起来让两个都能工作? - Jonathan Leffler
@Jonathan:在Perl中,这很简单(它也适用于多个非重叠括号):perl -0777 -ne'print $1 while /[(.*?)]/gs' infile.txt - jfs
1
请考虑使用$()而不是``。它是POSIX兼容的,更易于阅读和消除歧义。 - camh

6

我想指出的是,虽然大部分提出的解决方案可能有效,但你没有必要fork另一个shell并生成多个进程来完成这样一个简单的任务。

shell已经为你提供了所有需要的工具:

$ var='foo[bar] pinch'
$ var=${var#*[}; var=${var%%]*}
$ echo "$var"
bar

这三行能否改成四行呢?也就是说,是否可以使用此方法从两端剥离而无需再次分配$var? - Muhamed Huseinbašić

3

Sed 不是必需的:

var=`egrep -o '\[.*\]' FILENAME | tr -d ][`

但它仅适用于单行匹配。

2
使用Bash内置的正则表达式匹配似乎是另一种方法:
var='foo[bar] pinch'
[[ "$var" =~ [^\]\[]*\[([^\[]*)\].* ]]   # Bash 3.0
var="${BASH_REMATCH[1]}"
echo "$var"

1

这个怎么样:

shell_variable=$(sed -ne '/\[/,/\]/{s/^.*\[//;s/\].*//;p;}' $file)

在 Solaris 10 的 Korn shell 下对我有效,应该在 Bash 下也可以使用。在 Bourne shell 中用反引号替换 '$(...)'。

编辑:当在一行上给出 [ 和 ] 时有效。对于单行情况,也可以使用:

shell_variable=$(sed -n -e '/\[[^]]*$/,/\]/{s/^.*\[//;s/\].*//;p;}' \
                        -e '/\[.*\]/s/^.*\[\([^]]*\)\].*$/\1/p' $file)

第一个 '-e' 处理多行展开;第二个 '-e' 处理单行情况。第一个 '-e' 表示:

  • 从同一行上不跟随闭括号 ] 的开括号 [ 开始
  • 直到包含闭括号 ] 的那一行结束,
  • 用空字符串替换开括号和其中的任何字符,
  • 用空字符串替换闭括号后面的所有内容,并且
  • 打印结果。

第二个 '-e' 表示:

  • 对于任何同时包含开括号和闭括号的行
  • 将模式替换为“包括开括号在内的字符”、“不包括闭括号在内的字符”(并记住这些字符)、“从闭括号开始的内容”与中间的记住的字符相匹配,
  • 打印结果。

对于多行情况:

$ file=xxx
$ cat xxx
sdsajdlajsdl
asdajsdkjsaldjsal
sdasdsad [aaaa
bbbbbbb
cccc] asdjsalkdjsaldjlsaj
asdjsalkdjlksjdlaj
asdasjdlkjsaldja
$ shell_variable=$(sed -n -e '/\[[^]]*$/,/\]/{s/^.*\[//;s/\].*//;p;}' \
                          -e '/\[.*\]/s/^.*\[\([^]]*\)\].*$/\1/p' $file)
$ echo $shell_variable
aaaa bbbbbbb cccc
$

而对于单行情况:

$ cat xxx
sdsajdlajsdl
asdajsdkjsaldjsal
sdasdsad [aaaa bbbbbbb cccc] asdjsalkdjsaldjlsaj
asdjsalkdjlksjdlaj
asdasjdlkjsaldja
$
$ shell_variable=$(sed -n -e '/\[[^]]*$/,/\]/{s/^.*\[//;s/\].*//;p;}' \
                          -e '/\[.*\]/s/^.*\[\([^]]*\)\].*$/\1/p' $file)
$ echo $shell_variable
aaaa bbbbbbb cccc
$

大约在这里,用Perl完成整个任务变得更简单了,只需将文件读入内存并在两个多行替换操作中编辑结果字符串。


1

假设您正在询问有关bash变量的问题:

$ export YOUR_VAR=$(perl -ne'print $1 if /\[(.*?)\]/' your_file.txt)

如果括号在同一行上,则以上内容有效。


一旦你超过了某个点,Perl 比 sed 更容易。 - Jonathan Leffler

0
var=`grep -e '\[.*\]' test.txt | sed -e 's/.*\[\(.*\)\].*/\1/' infile.txt`

0

感谢大家,我使用了Strager的版本,完美地解决了问题,再次感谢...

var=`grep -e '\[.*\]' test.txt | sed -e 's/.*\[\(.*\)\].*/\1/' infile.txt`

考虑到这些信息,是否询问为什么没有选择Strager的版本作为“答案”是不合理的? - Jonathan Leffler

0

反斜杠(BSL)被吃掉了...

var='foo[bar] pinch' 
[[ "$var" =~ [^\]\[]*\[([^\[]*)\].* ]]   # Bash 3.0 
# Just in case ...: 
[[ "$var" =~ [^BSL]BSL[]*BSL[([^BSL[]*)BSL].* ]]   # Bash 3.0 
var="${BASH_REMATCH[1]}" 
echo "$var" 

0

提取文本的两个简单步骤。

  1. 在 [ 处分割变量并获取右侧部分
  2. 在 ] 处分割变量并获取左侧部分
cb0$ var='foo[bar] pinch'
cb0$ var=${var#*[}
cb0$ var=${var%]*} && echo $var
bar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接