Bash / Sed / Grep:解析/捕获子字符串

时间:2018-08-01 06:02:11

标签: bash macos sed grep substring

我已经在bash脚本中以字符串形式生成了一组文件路径,所有形式都是这样:

./foo/bar/filename.proto

可以有任意数量的子文件夹/斜杠,但是它们都具有.proto扩展名。

我想修剪./开头和结尾的filename.proto使其看起来像这样:

foo/bar

从其他解决方案改编和调试它的难度令人惊讶。我尝试过:

grep -Po "\.\/(.*)\/[^\/]+\.proto"

sed -n 's/\.\/\(.*\)\/[^\/]+\.proto/\1/p'

我尝试用转义和未转义的括号进行sed。供参考,我目前正在Mac上工作,并且想要最跨平台兼容的解决方案。

我可以在Python中相当容易地做到这一点,但我想避免调用另一个脚本来完成此工作的复杂性。

要让您了解其工作原理,到目前为止,我的完整脚本如下所示:

#!/bin/bash
consume_single_folder () {
  do_stuff $1
}

find . -name \*.proto|while read fname; do
  echo "$fname" |sed -n 's/\.\/\(.*\)\/[^\/]+\.proto/\1/p' | consume_single_folder
done

感谢您的帮助。谢谢!

编辑:

要清楚,我已经在regex101.com上测试了我的正则表达式,看起来还不错:

\.\/(.*)\/[^\/]+\.proto

应该贪婪,捕获第一个和最后一个斜杠之间的所有内容。

2 个答案:

答案 0 :(得分:1)

看起来像dirname可以帮助您:

$ dirname "./foo/bar/filename.proto"
./foo/bar

./前被删除:

$ dirname "./foo/bar/filename.proto"  | sed "s/\.\///g"
foo/bar

您还可以添加sort | uniq避免重复:

find . -name \*.proto|while read fname; do
  echo "$fname" | xargs dirname | sed "s/\.\///g" | consume_single_folder
done

在MacOS和Linux上运行

答案 1 :(得分:1)

请不要使用regex101之类的网站来测试sed正则表达式-工具之间以及各种实现之间的语法和功能差异很大。请参见Why does my regular expression work in X but not in Y?differences between various sed implementations < / p>

对于您给定的示例,将+更改为*将起作用(BRE和ERE之间的查找差异)

$ fname='./foo/bar/filename.proto'
$ echo "$fname" | sed -n 's/\.\/\(.*\)\/[^\/]*\.proto/\1/p'
foo/bar
$ # or use a different delimiter
$ echo "$fname" | sed 's|\./\(.*\)/[^/]*\.proto|\1|'
foo/bar
$ # further simplification as find already filters by extension
$ echo "$fname" | sed 's|\./\(.*\)/.*|\1|'
foo/bar

此外,我建议您阅读Why is looping over find's output bad practice?并相应地更改find的语法