从Grep RegEx捕获组
sh
(Mac OSX 10.6)中有这个小脚本来查看文件数组。 Google在这一点上停止了帮助:
files="*.jpg" for f in $files do echo $f | grep -oEi '[0-9]+_([az]+)_[0-9a-z]*' name=$? echo $name done
到目前为止(显然,对于你的shell的大师) $name
只能保存0,1或2,这取决于如果grep
发现文件名符合提供的问题。 我想要捕捉什么是parens ([az]+)
,并将其存储到一个variables 。
如果可能,我只想使用grep
。 如果没有,请不要Python或Perl等sed
或类似的东西 – 我是新的壳,并希望从* nix纯化angular攻击这个。
另外,作为一个超酷的bonu s,我很好奇我如何在shell中连接string? 我捕获的是在$ name存储的string“somename”,我想添加string“.jpg”到它的末尾,我可以cat $name '.jpg'
?
请解释发生了什么,如果你有时间的话。
如果你使用Bash,你甚至不需要使用grep
:
files="*.jpg" regex="[0-9]+_([az]+)_[0-9a-z]*" for f in $files do if [[ $f =~ $regex ]] then name="${BASH_REMATCH[1]}" echo "${name}.jpg" # concatenate strings name="${name}.jpg" # same thing stored in a variable else echo "$f doesn't match" >&2 # this could get noisy if there are a lot of non-matching files fi done
将正则expression式放在一个variables中更好。 如果包含字面,某些模式将不起作用。
这使用=~
Bash的正则expression式匹配运算符。 匹配的结果被保存到一个名为$BASH_REMATCH
的数组中。 第一个捕获组存储在索引1中,索引2中的第二个(如果有的话)等。索引零是完全匹配。
你应该知道,如果没有锚,这个正则expression式(和使用grep
的那个)将匹配以下任何一个例子和更多,这可能不是你正在寻找的:
123_abc_d4e5 xyz123_abc_d4e5 123_abc_d4e5.xyz xyz123_abc_d4e5.xyz
要消除第二个和第四个例子,使你的正则expression式是这样的:
^[0-9]+_([az]+)_[0-9a-z]*
其中说,string必须以一个或多个数字开始。 克拉代表string的开始。 如果在正则expression式的末尾添加美元符号,如下所示:
^[0-9]+_([az]+)_[0-9a-z]*$
那么第三个例子也将被消除,因为点不在正则expression式中的字符之间,美元符号表示string的结尾。 请注意,第四个例子也没有通过这个匹配。
如果你有GNU grep
(约2.5或更高版本,我认为,当\K
运营商被添加时):
name=$(echo "$f" | grep -Po '(?i)[0-9]+_\K[az]+(?=_[0-9a-z]*)').jpg
\K
运算符(可变长度后顾)导致前面的模式匹配,但不包含结果中的匹配。 固定长度的等价物是(?<=)
– 该模式将包括在右括号之前。 如果量词可以匹配不同长度的string(例如+
, *
, {2,4}
),则必须使用\K
(?=)
运算符匹配固定或可变长度的模式,称为“预见”。 它也不包含结果中的匹配string。
为了使匹配不区分大小写,使用(?i)
运算符。 它会影响后续的模式,所以它的地位非常重要。
正则expression式可能需要根据文件名中是否有其他字符进行调整。 你会注意到,在这种情况下,我展示了一个在捕获子串的同时串联一个string的例子。
纯粹的grep
是不可能的,至less不是一般的。
但是如果你的模式是合适的,你可以在pipe道中多次使用grep
来首先将你的行减less到一个已知的格式,然后提取你想要的位。 (虽然像cut
和sed
这样的工具在这方面要好得多)。
假设为了争辩,你的模式有点简单: [0-9]+_([az]+)_
你可以这样解压:
echo $name | grep -Ei '[0-9]+_[az]+_' | grep -oEi '[az]+'
第一个grep
会删除所有与你的整个--only-matching
不匹配的行,第二个grep
( --only-matching
指定的--only-matching
)会显示这个名字的alpha部分。 这只适用于模式,因为这个模式是合适的:“alpha部分”足够具体,可以抽出你想要的东西。
(另外:就个人而言,我会用grep
+ cut
来达到你的效果: echo $name | grep {pattern} | cut -d _ -f 2
这样就可以将行分割成分隔符, ,并且只返回字段2(字段号从1开始))。
Unix的哲学是有工具做一件事,做得很好,并把它们结合起来以实现非平凡的任务,所以我认为grep
+ sed
等是一个更加unix的做事方式:-)
我意识到这个答案已经被接受了,但是从“严格的”nix纯粹主义的angular度来看,似乎这个工作的正确工具是pcregrep
,这似乎还没有被提及。 尝试改变线条:
echo $f | grep -oEi '[0-9]+_([az]+)_[0-9a-z]*' name=$?
如下:
name=$(echo $f | pcregrep -o1 -Ei '[0-9]+_([az]+)_[0-9a-z]*')
只获取捕获组1的内容。
pcregrep
工具利用了你已经用过的所有语法,但是实现了你所需要的function。
参数-o
和grep
版本一样工作,如果它是裸露的,但它也接受pcregrep
一个数字参数,这个参数表示你想要显示哪个捕获组。
有了这个解决scheme,脚本中只需要最less的修改就可以了。 您只需更换一个模块化实用程序,并调整参数。
有趣的注意:您可以使用多个-o参数按照它们在行上出现的顺序返回多个捕获组。
我相信不可能只是grep
对于sed:
name=`echo $f | sed -E 's/([0-9]+_([az]+)_[0-9a-z]*)|.*/\2/'`
我会刺杀奖金,虽然:
echo "$name.jpg"
这是一个使用gawk的解决scheme。 这是我觉得我需要经常使用,所以我创build了一个函数
function regex1 { gawk 'match($0,/'$1'/, ary) {print ary['${2:-'1'}']}'; }
使用只是做
$ echo 'hello world' | regex1 'hello\s(.*)' world
一个build议 – 你可以使用参数扩展从最后一个下划线开始删除名字的一部分,同样在开始时:
f=001_abc_0za.jpg work=${f%_*} name=${work#*_}
那么name
将具有abc
值。
请参阅Apple 开发人员文档 ,向前search“参数扩展”。
如果你有bash,你可以使用扩展的globbing
shopt -s extglob shopt -s nullglob shopt -s nocaseglob for file in +([0-9])_+([az])_+([a-z0-9]).jpg do IFS="_" set -- $file echo "This is your captured output : $2" done
要么
ls +([0-9])_+([az])_+([a-z0-9]).jpg | while read file do IFS="_" set -- $file echo "This is your captured output : $2" done