根据文件名的一部分创建目录

时间:2013-03-16 10:56:42

标签: regex bash function shell grep

首先,我一个程序员 - 只是想学习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>

Pastebin (Long code.)

1 个答案:

答案 0 :(得分:2)

我收到了你的消息。您正在编写Bash脚本,程序员!

您的正则表达式(RE)属于“错误”类型。 Vanilla grep使用称为“基本正则表达式”(BRE)的形式,但您的RE采用扩展正则表达式(ERE)的形式。 BRE由香草grepvimore等使用.ERE几乎用于其他所有内容,awkPerl,{{1} },PythonJava等。问题是,您正在尝试在文件的内容中查找该模式,而不是在文件名中查找!

有一个.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},因此我们无法预测将使用哪些文件。但是如果我们想为每个文件执行此操作呢?

selectfor循环中使用上述解决方案效率低下。在循环内调用外部进程总是很糟糕。如果不使用像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是。

相关问题