Perl обрабатывает ARGV в сценарии с параметрами -p и -f - PullRequest
0 голосов
/ 08 января 2019

У меня есть Perl-скрипт, который я вызываю с опциями -p и -f. Я хотел бы передать параметры командной строки в ARGV в сценарии.

Например, opl.pl - это скрипт, который объединяет каждую строку, которая не начинается с xx , с предыдущей строкой, которая начинается с xx , с '# ' в качестве разделителя после пометки ранее существующих ' # ' символов:

# Usage: perl -pf opl.pl file.txt
BEGIN {$recmark = @ARGV[0] if $#ARGV; }
$recmark  = "xx" if (! defined $recmark);
chomp;
print "\n" if /$recmark/;
s/#/\_\_hash\_\_/g;
$_ .= "#"

Скрипт работает, когда в командной строке нет дополнительных параметров. Например, perl -pf oplx.pl filexx.txt с filexx.txt:

xx line #1
line 2
line 3
xx line 4
line 5

Производит (приблизительно):

xx line __hash__1#line 2#line 3
xx line 4#line 5

Я бы хотел использовать perl -pf oplx.pl filexyy.txt yy с fileyy.txt:

yy line #1
line 2
line 3
yy line 4
line 5

произвести (приблизительно):

yy line __hash__1#line 2#line 3
yy line 4#line 5

К сожалению, perl анализирует аргумент командной строки yy как имя файла, а не как аргумент.

Ответы [ 3 ]

0 голосов
/ 08 января 2019

Из справочной страницы perlrun(1):

-p
заставляет Perl предполагать следующий цикл вокруг вашей программы, который заставляет его перебирать аргументы имени файла наподобие sed:

 LINE:
   while (<>) {
       ...             # your program goes here
   } continue {
       print or die "-p destination: $!\n";
   }

Наиболее подходящее использование переключателя -p для однострочников, где каждый аргумент файла обрабатывается по очереди, строка за строкой, а результат выполнения программы выводится на стандартный вывод.

Угловые скобки Perl, которые неявно добавляются переключателем -p, принимают файловый дескриптор в качестве ввода и выполняют итерацию по каждой строке до достижения EOF:

while(<$opened_file_handle>) {
    …
}

HOWEVER , если дескриптор файла не передан, угловые скобки по умолчанию будут равны @ARGV, рассматривая каждый доступный аргумент как имя файла. Если @ARGV пусто, <> возвращается к стандартному вводу (эквивалентно использованию <STDIN>).

Если вы хотите передать оба аргумента и имен файлов в командной строке, у вас есть два варианта:

  1. Упорядочите аргументы так, чтобы аргументы, не относящиеся к файлу, были первыми, например:

    perl -f opt.pl ABC XYZ file1.txt file2.txt
    

А в вашем скрипте:

my $first = shift;  # Modifies @ARGV in-place, placing "ABC" in $first
my $second = shift; # Same again, this time plucking "XYZ" from @ARGV and putting it in `$second`
  1. Или используйте модуль Getopt::Long для передачи аргументов без имени файла в качестве переключателей (или «опций»):

    perl -f opt.pl --foo ABC --bar XYZ  file1.txt file2.txt …
    

И Perl-код для этого:

use Getopt::Long;
my $foo = "";
my $bar = "";
GetOptions("foo=s" => \$foo, "bar=s" => \$bar);

Использование Getopt::Long является более чистым (и рекомендуемым способом) для передачи аргументов при обработке списка файлов.

Надеюсь, это поможет!

0 голосов
/ 08 января 2019

Подумайте об использовании переменной среды в качестве альтернативы перебора аргументов командной строки.

recmark=yy perl -pf opl.pl file1 file2 ...

BEGIN { $recmark = $ENV{recmark} // "xx" };
...
0 голосов
/ 08 января 2019

Командный переключатель -n

заставляет Perl выполнять следующий цикл вокруг вашей программы, который заставляет его перебирать аргументы имени файла, вроде sed -n или awk:

LINE:
  while (<>) {
     ...        # your program goes here
  }

, где <> дескриптор файла является специальным

Ввод из <> происходит либо из стандартного ввода, либо из каждого файла, указанного в командной строке.

Другими словами, он читает строки из всех файлов, указанных в командной строке. -p делает то же самое, за исключением того, что он также печатает $_ каждый раз до конца.

Эти имена файлов находятся в @ARGV переменной , которая в вашем примере имеет filexyy.txt и yy, и которые, таким образом, обрабатываются как имена файлов.

Одно решение: удалите необходимые параметры (yy здесь) из @ARGV, в блоке BEGIN. Тогда операция <> действительно будет иметь только имена файлов для работы.

Это поднимает вопрос о желаемом интерфейсе вашей программы. Если вы хотите, чтобы этот параметр был указан последним в командной строке

my $param;
BEGIN {
    $param = pop @ARGV;
}

, поскольку pop удаляет из задней части массива; если вы хотите, чтобы параметр был задан первым, используйте shift . Обратите внимание, что ваш $recmark также должен быть удален из @ARGV.

Отслеживание всего этого подвержено ошибкам и неудобно как для использования, так и для дальнейшей работы.

Было бы гораздо лучше обработать эти аргументы, используя хороший модуль, такой как Getopt :: Long . Затем вы можете дать им имена, легко менять интерфейс по мере необходимости и правильно проверять каждый вызов модулем.

Также обратите внимание, что с именами файлов в @ARGV, которые остаются после того, как вы (или Getopt::Long) сделали с опциями, вы можете обрабатывать все строки из всех файлов внутри

while (<>) { ... }

с использованием того же <>, упомянутого выше. Внутри скрипта это намного лучше, чем -p.

...