1

まず第一に、私はプログラマーではありません。シェル スクリプトの基本を学び、いくつかのことを試しているだけです。

ユーザーがリストで選択したファイルのファイル名のバージョン番号に基づいてディレクトリを作成する 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—しかし、私はそれを間違っています:(

しかし、正規表現パターンに「一致しません」と言ってしまいます。

私は、ユーザーが選択したファイル名を取得しようとしています。次に、バージョン番号 (x.xx.x ファイル名のどこかに常にあります) を grep し、それでディレクトリを最終的に作成します。

コマンドチェーンがどのように見えるべきか、誰かが私にいくつかの指針を与えることができますか? 関数の構造についてはよくわからないので、助けていただければ幸いです。

編集:

わかりました、これは完全な関数が今どのように見えるかです: (ディレクトリ作成以外のsed (1) コマンドは私が作成したものではなく、コードに実装されていることに注意してください。)

Pastebin (ロングコード)

4

1 に答える 1

3

お知らせがあります。あなたはBashスクリプトを書いています、あなたプログラマーです!

正規表現(RE)は「間違った」タイプです。バニラgrepは「基本正規表現」(BRE)と呼ばれる形式を使用しますが、REは拡張正規表現(ERE)の形式です。BREは、バニラ、、、grepなどで使用されます。EREは、他のほぼすべての、、、、、などで使用されます。問題は、ファイル名ではなく、ファイルの内容でそのパターンを検索しようとしていることです。vimoreawkPerlPythonJava.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(すべてを1行にまとめるメリットはなく、コードの保守が難しくなります)。

mkdir -p "$MODS/out/$ver"

${ver}この場合は不要ですが、コンポーネントのいずれかに空白が埋め込まれている場合に備えて、パス名を二重引用符で囲むことをお勧めします。

したがって、特にそのREを生成する際には、「非プログラマー」のために十分な努力を払ってください。

さて、レッスン2です

一般的なループでこのソリューションを使用する場合は注意が必要です。あなたの質問は特にを使用しselectているため、どのファイルが使用されるかを予測することはできません。しかし、すべてのファイルに対してこれを実行したい場合はどうでしょうか。

上記のソリューションをforまたはwhileループで使用すると、非効率になります。ループ内で外部プロセスを呼び出すことは常に悪いことです。mkdirPerlやPythonのような別の言語を使用せずに私たちができることは何もありません。ただしsed、その性質上、反復的であるため、その機能を使用する必要があります。

1つの代替方法は、の代わりにシェルパターンマッチングsedを使用することです。この特定のパターンはシェルでは不可能ではありませんが、それは困難であり、他の疑問を提起します。だから、に固執しましょうsed

私たちが抱えている問題は、echo出力が各フィールドの間にスペースを置くことです。それは私たちにいくつかの問題を与えます。 sed各レコードを改行「\n」で区切るため、ここechoではそれ自体では機能しません。各スペースを改行で置き換えることはできますが、ファイル名内にスペースがある場合は問題になります。グロブを使っていくつかのトリックを行うことはできますIFSが、それは不必要な複雑化につながります。したがって、代わりに古き良きにフォールバックしlsます。通常は使用したくないので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を使用していますが、このループは1回だけ呼び出しますsed。しかし、それはmkdirプログラムをn回呼び出します。

レッスン3

申し訳ありませんが、それでも非効率的です。反復ごとに子プロセスを作成しています。ディレクトリを作成するには、カーネルAPI呼び出しが1つだけ必要ですが、そのためのプロセスを作成していますか?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; $!";
} 

あなたのREがここまで到達したことに注意してください!しかし、今ではより多くの制御が可能になり、子プロセスはありません(mkdirPerlには組み込みのプロセスがありますglob)。

結論として、ファイルの数が少ない場合は、sed上記のループで問題ありません。シンプルでシェルベースです。これだけのためにスクリプトからPerlを呼び出すと、perlが非常に大きいため、おそらく遅くなります。ただし、ループ内に子プロセスを作成するシェルスクリプトはスケーラブルではありません。Perlはです。

于 2013-03-16T11:30:10.580 に答える