有一个带有perl shebang的脚本,中间需要切换到bash

有一个带有perl shebang的脚本,中间需要切换到bash

我有一个包含两个块的脚本:第一个块是用 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

请注意,如果您想从perlto传递变量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 perlrunsh显示了+多语言的另一个示例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 -csh -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()(这可能仍然会导致代码可能难以阅读和遵循) 。

相关内容