web-dev-qa-db-ja.com

bashスクリプトのPerl正規表現が機能しないのはなぜですか? MacOSターミナル

私は自分の仕事のワークフローのためにAppleScriptsから離れて、代わりにバックグラウンドで実行できるより単純なものを作成しようとしています。このタスクでは、毎晩35〜40個のファイル(異なる品質の7個または8個のファイルの5つのレンディション)が与えられ、ファイル名の一部を抽出する必要があります。

例として、これらのファイルの1つの(省略された)バッチがどのように見えるかを次に示します。

各ファイルには5つのレンディションがあります

    ab_12_345_01_dest_xxxxxxxxxx_640x360_1000.jpg
    ab_12_345_01_dest_xxxxxxxxxx_768x432_3000.jpg
    ab_12_345_01_dest_xxxxxxxxxx_960x540_5000.jpg
    ab_12_345_01_dest_xxxxxxxxxx_1280x720_7000.jpg
    ab_12_345_01_dest_xxxxxxxxxx_1920x1080_9000.jpg

そして、ファイルはすべてそのように名前が付けられています(最高のレンディションを使用して、理由はすぐにわかります):

    ab_12_345_01_dest_xxxxxxxxxx_1920x1080_9000.jpg
    ab_12_345_02_dest_yyyyyyyyyy_1920x1080_9000.jpg
    ab_12_345_03_dest_zzzzzzzzzz_1920x1080_9000.jpg
    ab_12_345_part1_aaaaaaaaaa_1920x1080_9000.jpg
    ab_12_345_part2_bbbbbbbbbb_1920x1080_9000.jpg
    ab_12_345_part3_special_cccccccccc_1920x1080_9000.jpg
    ab_12_345_part4_dddddddddd_1920x1080_9000.jpg
    ab_12_345_04_dest_special_eeeeeeeeee_1920x1080_9000.jpg

したがって、私の目標は、ファイル名の9000部分を使用して、それぞれの最高のレンディションのみをgrepすることです(コピーするのに最も時間がかかるため、そこにある場合は、残りのファイルもそこにあります)。最後から2番目の_。これまでのところ、パート1は実行できましたが、パート2は実行できませんでした。

これを行うと、最高のレンディションのみのリストを取得できます。

    $ ls | grep 9000
    ab_12_345_01_dest_xxxxxxxxxx_1920x1080_9000.jpg
    ab_12_345_02_dest_yyyyyyyyyy_1920x1080_9000.jpg
    ab_12_345_03_dest_zzzzzzzzzz_1920x1080_9000.jpg
    ab_12_345_part1_aaaaaaaaaa_1920x1080_9000.jpg
    ab_12_345_part2_bbbbbbbbbb_1920x1080_9000.jpg
    ab_12_345_part3_special_cccccccccc_1920x1080_9000.jpg
    ab_12_345_part4_dddddddddd_1920x1080_9000.jpg
    ab_12_345_04_dest_special_eeeeeeeeee_1920x1080_9000.jpg

次に、ls | grep 9000 | Perl -pe '/^.+(?=_.+_.+)/mgを試してみました(すべてのオンライン正規表現テスター、具体的には、私が見つけたPerl正規表現テスターが機能すると言ったことに基づいて):

    $ ls | grep 9000 | Perl -pe '/^.+(?=_.+_.+)/mg`
    ab_12_345_01_dest_xxxxxxxxxx
    ab_12_345_02_dest_yyyyyyyyyy
    ab_12_345_03_dest_zzzzzzzzzz
    ab_12_345_part1_aaaaaaaaaa
    ab_12_345_part2_bbbbbbbbbb
    ab_12_345_part3_special_cccccccccc
    ab_12_345_part4_dddddddddd
    ab_12_345_04_dest_special_eeeeeeeeee

しかし、Perlにパイプしたことがない場合と同じ結果が得られました。私はもともとawkでこれを実装しようとしましたが、入力していたコマンドがかなり長くなり、RegExがその方法かもしれないと考えました。ただし、(文字列の先頭から数えた_ではなく)最後から2番目の_で一致を停止するには、前向きな先読みが必要であり、{$NL=$(NL-1)=""; print $0}を設定したときにawkは最後の__を保持していました。 。

1
Alex Torma

Perlコマンドでは、-pオプションがあるため、常に行を出力します。マッチパートは何もしません。

-nが必要で、一致する部分を印刷します。

ls -1 *9000.jpg \
| Perl -lne 'print $1 if /^(.+)(?=_.+_.+)/'

ファイル名には改行が含まれている可能性があるため、これを変更してゼロで区切られたファイル名を読み取る必要がありますが、必要ない場合は次のようにします。

printf '%s\0' *9000.jpg \
| Perl -lne 'INIT{ $/ = "\0"}; print $1 if /^(.+)(?=_.+_.+)/'

または、forループでファイル名を読み取り、シェルのみのパラメーター展開を使用できます。

for f in *9000.jpg; do printf '%s\n' "${f%_*_*}"; done

これはあなたの仕事により適しているかもしれません。 (=>「ファイル名に行ベースのテキスト編集ツールを使用しないでください。」@ Kusalananda)

1
pLumo

ファイルリストをフィルタリングするためにlsからgrepにパイプする必要はありません。

ls *9000.jpg

また、grepを使用すると、名前の他の場所に9000が含まれているファイルがすべて選択されます。

正規表現に問題はなく、Perlだけに問題があります。 grepを使用すると、必要なものを取得できます

ls *9000.jpg | grep -Po "^.+(?=_.+_.+)"

これを行う別の方法は、

find . -iname "*9000.jpg" -exec sh -c 'basename ${1%_*_*}' sh {} \;

findlsと同じことをします

展開${1%_*_*}は、最後から2番目の_から文字列の終わりまで文字を削除し、basenamefindが結果に含めるファイルパスを削除します。

構成

-exec sh -c `blah blah` sh {} \;

findで使用することを学ぶ価値は十分にあり、@ Kusalanandaには良い投稿があります ここ

-execfindにその出力で 'blah blah'を実行するように指示します。\;は各結果で 'blah blah'を実行することを意味します。sh -c 'put some script in here'はあなたがしたいことです結果を処理し、最後にsh {}findからの出力をsh -cで定義されたスクリプトに戻します。

0
bu5hman