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
Perlでループを書き直すだけです。
for my $file (glob '*.mrc') {
( my $newname = $file ) =~ s/\..*/.mrc/;
rename $file, $newname or warn "$file: $!";
}
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をとっただけなので、何もしません。
Perl
の=END_OF_Perl@
は、 pod
(ドキュメント) セクションの始まりであるため、Perl
によって無視されます。
変数をPerl
からbash
に渡す場合は、それらを環境にエクスポートする必要があることに注意してください(ただし、引数を使用することもできます。ここでは、受け取った引数のリストをPerl
スクリプトからbash
スクリプトに転送します)。
$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 perlrun
は、sh
+ Perl
polyglotの別の例を示していますが、今回はsh
が最初に呼び出されます(she-bangsをサポートしないシステムの場合)。
switchインタープリターを使用することはできませんが、新しいシェルプロセスを生成してからPerlに戻ることができます。
my $sh_code = <<'END_SH'
for f in *.mrc; do mv -- "$f" "${f%%.*}".mrc ; done
END_SH
system 'bash', '-c', $sh_code
または、現在のPerlプロセスをシェルに置き換えることができます
exec 'bash', '-c', $sh_code
しかし、@ chorobaが答えるように、Perlは汎用言語であり、ほぼすべてのタスクを処理できます。
すべてを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
ですが、_-execdir
_を理解するfind
を使用しています):
_#!/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
_を使用すると、指定されたShellコマンドが、見つかったファイルのディレクトリを作業ディレクトリとして実行されます。 _{}
_(およびサブシェル内の_$1
_)は、見つかったファイルのベース名になります。
_tif2mrc
_ needsを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("..");
を使用すると、1つのディレクトリレベルが高くなりすぎます)。
これを適切に行うには、前回のシェルの例のように、置き換えられた作業ディレクトリでコマンドを実行するか、Perlの最初のchdir()
のリターンコードを適切にチェックします(これにより、コードが生成される可能性があります)読みにくく、従うのが難しい)。