Perl Shebangを含むスクリプトがあり、途中でBashに切り替える必要があります。

Perl Shebangを含むスクリプトがあり、途中でBashに切り替える必要があります。

2つのブロックを持つスクリプトがあります。最初のブロックはPerlで書かれ、2番目のブロックはBashで書かれました。

スクリプトの途中でシェル(perl --> bash)を切り替える方法は?スクリプトは以下に添付されています。

#! /usr/bin/perl -w
#
my @dirs = glob("*.frames");
foreach $dir (@dirs) {
   print "working on $dir\n";
   chdir $dir;
   my @digitfiles = glob ("RawImage_?.tif"); #need to make all files have 2-digit numbering for sort order to be correct
   foreach $file (@digitfiles) {
      my $newfile = $file;
      $newfile =~ s/RawImage_/RawImage_0/;
      rename $file,$newfile;
   }
   my $stackname = "../" . $dir . ".mrc";
   `tif2mrc -s *.tif $stackname`; #IMOD program to stack: -s option means treat input as signed INT
   chdir "../"; #go back up
}

#!/usr/bin/env bash
for f in *.mrc; do mv -- "$f" "${f%%.*}".mrc ; done

答え1

Perlでループを再構築してください。

for my $file (glob '*.mrc') {
    ( my $newname = $file ) =~ s/\..*/.mrc/;
    rename $file, $newname or warn "$file: $!";
}

答え2

無視するXYの問題このトピックの質問に答えるには:

#! /usr/bin/perl
":" || q@<<"=END_OF_PERL"@;

# perl code here

exec "bash", "--", $0, @ARGV;
=END_OF_PERL@

# bash code here

bash=END_OF_PERL@この行の前の部分は、次の理由で無視されます。

: || something <<"=END_OF_PERL@"
...
=END_OF_PERL@

そして最初の行では、Perlには2つの文字列(":"q@quoted-string@) が一緒に OR され、演算は行われません。

=END_OF_PERL@inはperlaの始まりですpod(文書)したがって、一部は無視されますperl

perlからに変数を渡すには、そのbash変数を環境にエクスポートする必要があります(パラメータを使用することもできますが、ここではスクリプトから受け取ったパラメータのリストをスクリプトに渡しますperlbash

$ENV{ENV_VAR} = $perl_var;

コードは、このperlセクションが現在の作業ディレクトリを変更しないと仮定します。それ以外の$0場合は相対パスの場合に渡されますbash。この問題を解決するには、最初からスクリプトの絶対パスを取得できます。

#! /usr/bin/perl
":" || q@<<"=END_OF_PERL"@;
use Cwd "fast_abs_path";
my $script_path = fast_abs_path $0;

# perl code here

exec "bash", "--", $script_path, @ARGV;
=END_OF_PERL@

# bash code here

これは、複数言語で有効なコードである多言語スクリプトの例です。このヒントが気に入った場合は確認してください。もっと極端なことここまたはそれコードゴルフQ&A

perldoc perlrunsh+multilingualの別の例が示されていますが、perl今回shは最初に呼び出されます(Shabanをサポートしていないシステムの場合)。

答え3

できない変化ただし、新しいシェルプロセスを作成してPerlに戻ることはできます。

my $sh_code = <<'END_SH'
for f in *.mrc; do mv -- "$f" "${f%%.*}".mrc ; done
END_SH

system 'bash', '-c', $sh_code

あるいは、現在のPerlプロセスをShellに置き換えることもできます。

exec 'bash', '-c', $sh_code

しかし、@chorobaが答えたように、Perlはほとんどすべてのタスクを処理できる汎用言語です。

答え4

Perlですべての操作を実行したり、シェルスクリプトですべての操作を実行したりできます。しかし、混在言語は多少混乱することが多いです。

シェルスクリプトのバージョンは次のとおりです。それはまるでこれは(bash例えば):

#!/bin/bash

dirs=( *.frames )

for dir in "${dirs[@]}"; do
    printf 'Working on "%s"\n' "$dir"

    cd "$dir"

    digitfiles=( RawImage_?.tif )

    for file in "${digitfiles[@]}"; do
        newfile="RawImage_0${file#RawImage_}"
        mv "$file" "$newfile"
    done

    stackname="../$dir.mrc"
    tif2mrc -s *.tif "$stackname"

    cd ..
done

for f in *.mrc; do
    mv -- "$f" "${f%%.*}".mrc
done

または少し慣用的です(通常は使用しますshが、find理解できるaを使用します-execdir)。

#!/bin/sh

echo 'Renaming TIFF files'

find *.frames -type f -name 'RawImage_?.tif' \
    -execdir sh -c 'mv "$1" "RawImage_0${1#RawImage_}"' sh {} ';'

for d in *.frames; do
    printf 'Processing "%s"...\n' "$d"
    tif2mrc -s "$d"/*.tif "${d%%.*}.mrc"
done

(どちらの例もファイル名生成に対してのみテストされました。)

名前が変更された各ファイルのいくつかの出力を取得するにsh -c変更するか 。sh -cx-print-execdir

を使用すると、-execdir見つかったファイルのディレクトリを作業ディレクトリとして使用して、指定されたシェルコマンドが実行されます。{}(そして$1サブシェル内で)は見つかったファイルのデフォルト名になります。

もしtif2mrc 必要TIFFファイルのディレクトリから実行し、ループを次に変更します。

for d in *.frames; do
    printf 'Processing "%s"...\n' "$d"
    (cd "$d" && tif2mrc -s *.tif "../${d%%.*}.mrc" )
done

Perlでは、バックティックを使用してシェルコマンドを実行すると、そのコマンドの出力が返されます。代わりに使用できます

system("tif2mrc -s *.tif $stackname");

の出力に興味がなければtif2mrc

また、ディレクトリを前後に変更するスクリプトやプログラムを読むことも混乱することがあります。ディレクトリが存在しない場合は、望ましくないことが発生する可能性があります(chdir("..");ディレクトリのレベルを上げすぎます)。

これを正しく実行するには、最後のシェルの例のように移動した作業ディレクトリでコマンドを実行するか、Perlの最初の戻りコードを正しく確認してくださいchdir()(これはコードを読み取ってフォローするのが難しい場合があります)。

関連情報