我正在尝试动态查找稍后在脚本中以编程方式使用的目录。我遇到的问题是考虑到可能存在或可能不存在的空白区域。
使用以下示例,因为它输出由空格分隔的三个字符串(在本例中为路径)。让我们假设我想使用whereis
获取特定命令的手册页目录(暂时忘记有内置的方法):
$ whereis bash
bash: /bin/bash /usr/local/man/man1/bash.1.gz /usr/ports/shells/bash
我想提取任何一个目录。使用sed
,我想出了以下内容:
$ whereis bash | sed -En 's:.*[" "](.*man.*)[" "].*:\1:p'
/usr/local/man/man1/bash.1.gz
如果模式恰好位于中间,那么效果很好,但如果恰好位于字符串的开头或结尾,我必须从模式中删除空格才能使其工作(使用" port" for the pattern as example)
$ whereis bash | sed -En 's:.*[" "](.*port.*)[" "].*:\1:p'
$ whereis bash | sed -En 's:.*[" "](.*port.*).*:\1:p'
/usr/ports/shells/bash
如果我想用模式" bin"提取目录,情况也是如此。在它。
我如何"告诉" {em>可能的模式sed
包含某个字符。
我为什么要这样做?
当我尝试没有空格时,我得到以下内容:
$ whereis bash | sed -En 's:.*(.*man.*).*:\1:p'
man1/bash.1.gz /usr/ports/shells/bash
我没有获得我想要的文字的完整路径,它增加了我完全不想要的路径。这个空间是一个分隔符。
我使用过这篇文章:How to output only captured groups with sed?和帖子:sed - how to do regex groups using sed作为参考和跳跃点。
另外值得注意的是,我尝试将正则表达式\s
用于空格,但它被忽略了。我也在FreeBSD上,所以我使用-E
作为正则表达式。
如果还有另一种方法可以解决这个问题,那么我们将非常感谢正确方向上的一点。我非常擅长使用sed
和awk
。
答案 0 :(得分:1)
sed可能不是此任务的正确工具。您可以使用以下内容迭代输出:
foreach f in `whereis bash` ; do
echo $f | grep /man/
done
要解决具体的whereis问题,最好使用内置的FreeBSD选项以-b,-m和-s返回二进制,手册页或源。将它与-q(安静)选项结合使用,您就可以获得设计用于脚本的内容。所以:
whereis -mq bash
将返回/usr/local/man/man1/bash.1.gz
如果你的用例是别的,你绝对必须使用sed,这应该给你想要的东西:
whereis bash | sed -E 's|^.*[[:space:]]+([^[:space:]]+man[^[:space:]]+).*$|\1|'
FreeBSD 11正则表达式符合IEEE Std 1003.2(POSIX.2),不支持\ s \ S表示法。因此,您需要使用[[:space:]]字符类。可以通过re_format(7)手册页找到更多信息。
答案 1 :(得分:0)
如果你想使用正则表达式,你需要考虑它们是"贪心" (*尝试尽可能匹配),所以你需要通过在表达式之前查找空格来限制它(可以用\ s完成)并且只在你看到非空格时继续表达式(可以完成\ S)。
所以这应该有效:
whereis bash | sed -En 's:.*\s(\S*man\S*).*:\1:p'
虽然我发现你可以在bash函数中更轻松地处理这个问题,在这种情况下你可以一次处理一个单词,你可以使用更简单的 globs 而不是正则表达式。
例如:
find_manpage() {
local tool=$1
local path
set -- $(whereis "${tool}")
for path ; do
if [[ "${path}" == *man* ]] ; then
echo "${path}"
return 0
fi
done
return 1
}
并使用它:
find_manpage bash
或者:
manpage_path=$(find_manpage bash)
您可以轻松扩展该功能以采用"模式"作为第二个参数并匹配它,使它更通用而不仅仅是找到联机帮助页。
我希望这有帮助!