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はperl
aの始まりです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
+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()
(これはコードを読み取ってフォローするのが難しい場合があります)。