Есть скрипт с perl shebang, нужно переключиться на bash посередине

Вы правы, это означает 'если $PATH существует - и не является нулевым - тогда добавьте :$PATH'.

Вам нужно проверить, существует ли $PATH, потому что вы не хотите добавлять ведущее (или последующее) двоеточие, если $PATH не определен. Имя каталога нулевой длины (null) в пути, как в :/usr/local/bin:/usr/bin, или /usr/local/bin:/usr/bin:, или /usr/local/bin::/usr/bin, означает поиск в текущем каталоге.

Выдержка из man bash:

   PATH   ...
          A zero-length (null) directory name in the value of PATH indicates 
          the current directory.  A  null  directory name may appear as two 
          adjacent colons, or as an initial or trailing colon.
          ...

Это, вероятно, не то, что вы хотите сделать.

Следующие две строки делают то же самое:

PATH=":/bin"        # search current directory, then /bin
PATH=".:/bin"
4
18.09.2017, 01:59
4 ответа

Просто перепишите цикл на Perl:

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

Вы не можете переключать интерпретаторы, но вы можете создать новый процесс оболочки, а затем вернуться к 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 — это язык общего назначения, и он может справиться практически с любой задачей.

7
27.01.2020, 20:44

Все можно было сделать на 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, который понимает-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("..");затем поднимет вас на один уровень каталога выше ).

Чтобы правильно это сделать, либо выполните команду со смещенным рабочим каталогом, как в моем последнем примере с оболочкой, либо правильно проверьте код возврата исходного chdir()в Perl (, это потенциально все равно приведет к коду, который может быть трудно читать и следовать ).

2
27.01.2020, 20:44

Игнорируя задачу 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@), объединенных по ИЛИ, поэтому -op.

=END_OF_PERL@в 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

Это один из примеров многоязычного сценария, который является допустимым кодом более чем на одном языке. Если вам нравятся эти трюки, вы можете взглянуть на , более экстремальный здесь или codegolf Q&A .

perldoc perlrunпоказывает еще один пример полиглота sh+ perl, но на этот раз shвызывается первым (для систем, которые не поддерживают -удары ).

19
27.01.2020, 20:44

Теги

Похожие вопросы