我有一个包含两个块的脚本:第一个块是用 perl 编写的,第二个块是用 bash 编写的
如何在脚本中间切换 shell(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 中只有两个字符串(":"
和q@quoted-string@
) 一起进行或运算,因此无操作。
=END_OF_PERL@
inperl
是 a 的开始pod
(文档)部分因此被忽略perl
。
请注意,如果您想从perl
to传递变量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
这是多语言脚本的一个示例,该脚本是多种语言的有效代码。如果你喜欢这些技巧,你可以看看那个更极端的这里或那个代码高尔夫问答。
perldoc perlrun
sh
显示了+多语言的另一个示例perl
,但这次sh
首先被调用(对于不支持 she-bangs 的系统)。
答案3
你不能转变解释器,但您可以生成一个新的 shell 进程,然后返回到 perl:
my $sh_code = <<'END_SH'
for f in *.mrc; do mv -- "$f" "${f%%.*}".mrc ; done
END_SH
system 'bash', '-c', $sh_code
或者你可以用shell替换当前的perl进程
exec 'bash', '-c', $sh_code
但正如 @choroba 回答的那样,perl 是一种通用语言,可以处理几乎任何任务。
答案4
您可以在 Perl 中完成所有操作,也可以在 shell 脚本中完成所有操作。不过,混合语言通常有点混乱。
shell脚本版本可能看起来就像是这个(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
或者,稍微更惯用(现在使用 plain 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
,将使用找到的文件的目录作为其工作目录来执行给定的 shell 命令。{}
(并且$1
在子 shell 中)将是找到的文件的基本名称。
如果tif2mrc
需要在 TIFF 文件的目录中运行,然后将循环更改为
for d in *.frames; do
printf 'Processing "%s"...\n' "$d"
(cd "$d" && tif2mrc -s *.tif "../${d%%.*}.mrc" )
done
请注意,在 Perl 中,使用反引号执行 shell 命令将返回该命令的输出。您可以改为使用
system("tif2mrc -s *.tif $stackname");
如果您对 的输出不感兴趣tif2mrc
。
此外,读取来回更改目录的脚本或程序也会令人困惑。如果目录不存在,它还可能导致不需要的事情(chdir("..");
然后会使您的目录级别上升得太高)。
要正确执行此操作,请使用移位的工作目录执行命令,如我的上一个 shell 示例,或者正确检查 Perl 中初始的返回代码chdir()
(这可能仍然会导致代码可能难以阅读和遵循) 。