首先,我不一个程序员 - 只是想学习shell脚本的基础知识并尝试一些东西。
我正在尝试为我的bash脚本创建一个函数,该函数根据用户在列表中选择的文件的文件名中的版本号创建目录。
这是功能:
lav_mappe () {
shopt -s failglob
echo "[--- Choose zip file, or x to exit ---]"
echo ""
echo ""
select zip in $SRC/*.zip
do
[[ $REPLY == x ]] && . $HJEM/build
[[ -z $zip ]] && echo "Invalid choice" && continue
echo
grep ^[0-9]{1}\.[0-9]{1,2}\.[0-9]{1,2}$ $zip; mkdir -p $MODS/out/${ver}
done
}
我也试过搞乱其他一些命令:
for ver in $zip; do
grep "^[0-9]{1}\.[0-9]{1,2}\.[0-9]{1,2}$" $zip; mkdir -p $MODS/out/${ver}
done
还有find | grep
- 但我做错了:(
但最终我的正则表达式模式“不匹配”。
我正在尝试获取用户选择的文件名,然后使用grep查找版本号(文件名中的某处x.xx.x
),并且只需创建一个目录。
有人可以给我一些指针,指示链应该是什么样的吗?我对这个功能的结构非常不确定,所以对任何帮助都表示赞赏。
编辑:
好的,这就是完整函数现在的样子:(请注意,除了创建目录之外的 sed (1)命令不是由我创建的,只是在我的代码中实现。)< / p>
答案 0 :(得分:2)
我收到了你的消息。您正在编写Bash脚本,是程序员!
您的正则表达式(RE)属于“错误”类型。 Vanilla grep
使用称为“基本正则表达式”(BRE)的形式,但您的RE采用扩展正则表达式(ERE)的形式。 BRE由香草grep
,vi
,more
等使用.ERE几乎用于其他所有内容,awk
,Perl
,{{1} },Python
,Java
等。问题是,您正在尝试在文件的内容中查找该模式,而不是在文件名中查找!
有一个.Net
命令,或者你可以使用egrep
,所以:
grep -E
(请注意,单引号比双引号更安全)。顺便说一下,你在前面使用echo $zip|grep -E '^[0-9]\.[0-9]{1,2}\.[0-9]{1,2}$'
,在末尾使用^
,这意味着文件名只包含一个版本号,但你说版本号是“文件名中的某个地方”。您不需要隐含的$
量词。
但是,您似乎也没有捕获版本号。
您可以使用{1}
(我们还需要sed
):
-E
右边的ver=$(echo $zip| sed -E 's/.*([0-9]\.[0-9]{1,2}\.[0-9]{1,2}).*/\1/')
表示“用括号组中匹配的内容替换所有内容(这就是为什么我们在正面和背面都有。*”)。
我知道,这有点笨拙。
现在我们可以执行\1
(将所有内容放在一行上没有任何优点,这会使代码难以维护):
mkdir
在这种情况下, mkdir -p "$MODS/out/$ver"
是不必要的,但是如果任何组件嵌入了空格,最好将路径名用双引号括起来。
所以,为“非程序员”做出了很好的努力,特别是在生成RE时。
现在第2课
在一般循环中使用此解决方案时要小心。您的问题专门使用${ver}
,因此我们无法预测将使用哪些文件。但是如果我们想为每个文件执行此操作呢?
在select
或for
循环中使用上述解决方案效率低下。在循环内调用外部进程总是很糟糕。如果不使用像Perl或Python这样的其他语言,我们无法对while
做任何事情。但mkdir
,其性质是迭代的,我们应该使用该功能。
另一种方法是使用 shell模式匹配而不是sed
。这种特殊的模式在shell中是不可能的,但它很难并提出其他问题。所以我们坚持使用sed
。
我们遇到的一个问题是sed
输出在每个字段之间放置一个空格。这给了我们一些问题。 echo
使用换行符“\ n”分隔每条记录,因此sed
本身不会在此处执行。我们可以用换行替换每个空格,但如果文件名中有空格则会出现问题。我们可以用echo
和globbing做一些诡计,但这会导致不必要的复杂化。相反,我们会回到古老的IFS
。通常我们不想使用ls
,shell globbing效率更高,但是我们在这里使用的功能是在每个文件名后放置一个换行符(当通过管道重定向时使用)。
ls
我在这里使用流程替换,此循环只会调用while read ver
do
mkdir "$ver"
done < <(ls $SRC/*.zip|sed -E 's/.*([0-9]{1}\.[0-9]{1,2}\.[0-9]{1,2}).*/\1/')
和ls
一次。但是,它会调用sed
程序 n 次。
第3课
抱歉,但这仍然效率低下。我们正在为每次迭代创建一个子进程,创建一个目录只需要一个内核API调用,但我们正在为此创建一个进程?让我们使用像Perl这样更复杂的语言:
mkdir
您可能希望注意到您的RE已经通过了!但是现在我们有了更多的控制权,并且没有子进程(Perl中的#!/usr/bin/perl
use warnings;
use strict;
my $SRC = '.';
for my $file (glob("$SRC/*.zip"))
{
$file =~ s/.*([0-9]{1}\.[0-9]{1,2}\.[0-9]{1,2}).*/$1/;
mkdir $file or die "Unable to create $file; $!";
}
是内置的,mkdir
也是如此。
总之,对于少量文件,上面的glob
循环会没问题。它很简单,基于shell。由于perl非常大,因此从脚本调用Perl 仅用于此可能会更慢。但是在循环内创建子进程的shell脚本是不可伸缩的。 Perl是。