我在sh
(Mac OSX 10.6)中有这个小脚本来查看一系列文件.谷歌此时已停止提供帮助:
files="*.jpg"
for f in $files
do
echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
name=$?
echo $name
done
Run Code Online (Sandbox Code Playgroud)
到目前为止(显然,对于你的shell大师)$name
只有0,1或2,具体取决于是否grep
发现文件名与提供的内容匹配.我想要的是捕获parens中的内容([a-z]+)
并将其存储到变量中.
如果可能的话,我只想使用它grep
.如果没有,请不要使用Python或Perl等,sed
或类似的东西 - 我是shell的新手,并希望从*nix纯粹的角度来攻击它.
另外,作为一个超酷的bonu,我很好奇如何在shell中连接字符串?我捕获的组是$ name中存储的字符串"somename",我想在其末尾添加字符串".jpg",是cat $name '.jpg'
吗?
如果你有时间,请解释一下发生了什么.
Pau*_*ce. 469
如果你正在使用Bash,你甚至不必使用grep
:
files="*.jpg"
regex="[0-9]+_([a-z]+)_[0-9a-z]*"
for f in $files # unquoted in order to allow the glob to expand
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
Run Code Online (Sandbox Code Playgroud)
将正则表达式放在变量中会更好.如果按字面意思包含,某些模式将无效.
这使用的 =~
是Bash的正则表达式匹配运算符.匹配的结果将保存到名为的数组中$BASH_REMATCH
.第一个捕获组存储在索引1中,第二个(如果有)存储在索引2中,等等.索引零是完全匹配.
您应该知道,如果没有锚点,这个正则表达式(以及使用的正则表达式grep
)将匹配以下任何示例和更多,这可能不是您正在寻找的:
123_abc_d4e5
xyz123_abc_d4e5
123_abc_d4e5.xyz
xyz123_abc_d4e5.xyz
Run Code Online (Sandbox Code Playgroud)
要消除第二个和第四个例子,请使用这样的正则表达式:
^[0-9]+_([a-z]+)_[0-9a-z]*
Run Code Online (Sandbox Code Playgroud)
表示字符串必须以一个或多个数字开头.克拉代表字符串的开头.如果在正则表达式的末尾添加一个美元符号,如下所示:
^[0-9]+_([a-z]+)_[0-9a-z]*$
Run Code Online (Sandbox Code Playgroud)
然后第三个例子也将被删除,因为点不在正则表达式中的字符之间,而美元符号表示字符串的结尾.请注意,第四个示例也会使此匹配失败.
如果您有GNU grep
(大约2.5或更高版本,我认为,当\K
添加运算符时):
name=$(echo "$f" | grep -Po '(?i)[0-9]+_\K[a-z]+(?=_[0-9a-z]*)').jpg
Run Code Online (Sandbox Code Playgroud)
的\K
操作者(可变长度向后看)导致前述图案相匹配,但不包括在结果中的匹配.固定长度的等价物是(?<=)
- 模式将包括在右括号之前.\K
如果量词可以匹配不同长度的字符串(例如,)+
,则必须使用.*
{2,4}
所述(?=)
操作者匹配固定的或可变长度的模式和被称为"超前".它也不包括结果中匹配的字符串.
为了使匹配不区分大小写,使用(?i)
运算符.它会影响其后的模式,因此它的位置非常重要.
可能需要根据文件名中是否有其他字符来调整正则表达式.您将注意到,在这种情况下,我展示了在捕获子字符串的同时连接字符串的示例.
Rob*_*obM 134
对于纯粹的grep
,这是不可能的,至少不是一般的.
但是如果你的模式是合适的,你可以grep
在管道中多次使用,首先将你的行减少到一个已知的格式,然后只提取你想要的位.(虽然工具喜欢cut
并且sed
在这方面要好得多).
假设为了论证你的模式有点简单:[0-9]+_([a-z]+)_
你可以这样提取:
echo $name | grep -Ei '[0-9]+_[a-z]+_' | grep -oEi '[a-z]+'
Run Code Online (Sandbox Code Playgroud)
第一个grep
将删除任何与您的整体模式不匹配的行,第二个grep
(已--only-matching
指定)将显示名称的alpha部分.这只能起作用,因为这种模式是合适的:"alpha部分"具体到足以拉出你想要的东西.
(旁白:我个人使用grep
+ cut
来实现你的目标:echo $name | grep {pattern} | cut -d _ -f 2
.这可以cut
通过拆分分隔符将行解析为字段_
,并返回字段2(字段编号从1开始)).
Unix哲学是拥有一个工具,做一件事,并做得很好,并将它们结合起来实现非平凡的任务,所以我认为grep
+ sed
etc是一种更为Unix的做事方式:-)
小智 88
我意识到答案已经被接受了,但是从"严格的*nix纯粹主义角度"来看,这似乎是正确的工具,pcregrep
似乎还没有被提及.尝试更改线条:
echo $f | grep -oEi '[0-9]+_([a-z]+)_[0-9a-z]*'
name=$?
Run Code Online (Sandbox Code Playgroud)
以下内容:
name=$(echo $f | pcregrep -o1 -Ei '[0-9]+_([a-z]+)_[0-9a-z]*')
Run Code Online (Sandbox Code Playgroud)
仅获取捕获组1的内容.
该pcregrep
工具使用了您已经使用的所有相同语法grep
,但实现了您需要的功能.
该参数的-o
工作方式与grep
版本相同,如果它是裸的,但它也接受一个数字参数pcregrep
,表示您要显示的捕获组.
使用此解决方案,脚本中只需要进行最少的更改.您只需将一个模块化实用程序替换为另一个,并调整参数.
有趣的注意事项:您可以使用多个-o参数按照它们在行上显示的顺序返回多个捕获组.
cob*_*bal 25
我相信只有grep是不可能的
对于sed:
name=`echo $f | sed -E 's/([0-9]+_([a-z]+)_[0-9a-z]*)|.*/\2/'`
Run Code Online (Sandbox Code Playgroud)
不过我会奖励奖金:
echo "$name.jpg"
Run Code Online (Sandbox Code Playgroud)
小智 25
str="1w 2d 1h"
regex="([0-9])w ([0-9])d ([0-9])h"
if [[ $str =~ $regex ]]
then
week="${BASH_REMATCH[1]}"
day="${BASH_REMATCH[2]}"
hour="${BASH_REMATCH[3]}"
echo $week --- $day ---- $hour
fi
Run Code Online (Sandbox Code Playgroud)
输出:1 --- 2 ---- 1
ops*_*psb 16
这是一个使用gawk的解决方案.这是我发现我需要经常使用的东西所以我为它创建了一个函数
function regex1 { gawk 'match($0,/'$1'/, ary) {print ary['${2:-'1'}']}'; }
Run Code Online (Sandbox Code Playgroud)
使用只是做
$ echo 'hello world' | regex1 'hello\s(.*)'
world
Run Code Online (Sandbox Code Playgroud)
给您的建议 - 您可以使用参数扩展来删除从最后一个下划线开始的名称部分,并且在开始处类似:
f=001_abc_0za.jpg
work=${f%_*}
name=${work#*_}
Run Code Online (Sandbox Code Playgroud)
那么name
就会有价值abc
。
请参阅 Apple开发人员文档,向前搜索“参数扩展”。
归档时间: |
|
查看次数: |
372961 次 |
最近记录: |