Регулярные выражения Perl в системных вызовах

#regex #perl #command-line

#регулярное выражение #perl #командная строка

Вопрос:

Следующая однострочная версия Perl работает так, как я ожидаю; она удаляет из моего файла начальные и конечные пробелы и заменяет промежуточные пробелы одной табуляцией:

 $ perl -pi -le 's/^s //; s/s $//; s#s #t#g;' file
  

Что меня озадачивает, так это то, почему я не могу заставить это работать с помощью system вызова из моего Perl-кода:

 system "perl -pi -le 's/^s //; s/s $//; s#s #t#g;' file";  # '$' backslashed
  

В чем здесь проблема?

Комментарии:

1. Дикие непроверенные предположения: удвоить ваши?

2. Уже пробовал это, безуспешно

3. Что $? (он же $CHILD_ERROR ) говорит?

4. Я тоже рискнул пойти по system @args пути, но у меня закончились идеи

5. @Axeman : На данный момент у меня нет доступа к * nix, не могу сказать

Ответ №1:

Нет необходимости запускать другой процесс для -i, но в целом вам следует локализовать некоторые глобальные переменные:

 sub do_stuff {
  my $file = shift;
  local ($_, $., $ARGV, *ARGV);
  local ( $^I, @ARGV ) = ( '.bak', $file );

  while ( <> ) {
    s/..../..../;
    print;
  }
}
  

Ответ №2:

 say "perl -pi -le 's/^s //; s/s $//; s#s #t#g;' file";
  

создает

 Unrecognized escape s passed through at -e line 1.
Unrecognized escape s passed through at -e line 1.
Unrecognized escape s passed through at -e line 1.
perl -pi -le 's/^s //; s/s $//; s#s #   #g;' file
  

Вы хотите

 system("perl -pi -le 's/^\s //; s/\s $//; s#\s #\t#g;' file");
  

На самом деле, зачем вообще вызывать оболочку?

 system('perl', '-i', '-ple' 's/^\s //; s/\s $//; s#\s #\t#g;', 'file');
  

Комментарии:

1. system @args Подход выглядит многообещающим… все встанет на свои места, когда я получу в свои руки * nix box

Ответ №3:

Вы забываете, что обратная косая черта анализируется сначала как строка, заключенная в двойные кавычки Perl, а затем как команда командной строки.

Эквивалент командной строки:

 perl -pi -le 's/^s //; s/s $//; s#s #t#g;' file
  

в скрипте Perl это

 system "perl -pi -le 's/^\s //; s/\s $//; s#\s #\t#g;' file"
  

Хотя глупо запускать другую копию Perl только для этого. Вероятно, вам лучше использовать еще несколько строк кода и выполнять это в том же процессе.

Комментарии:

1. Знаете, я почти уверен, что пробовал это раньше, и это не сработало.

2. @Zaid, ты, наверное, где-то пропустил обратную косую черту.

3. Опция «еще на несколько строк» — это то, что я в конечном итоге реализовал… но не смог понять, почему это не работает.

Ответ №4:

runrig показал, что вам не нужно создавать подпроцесс только для использования -i. Внутреннее использование -i немного странно, поэтому есть две более чистые альтернативы. Первое — использовать Tie::File, что довольно просто.

Другой способ заключается в записи во временный файл с помощью File::Temp, что, по сути, и делает -i .

 my $tmp = File::Temp->new;
open my $fh, "<", $file or die "Can't open $file: $!";

while(<$fh>) {
    s/.../.../;
    print $tmp $_;
}

my $tmpfile = $tmp->filename;
rename $tmpfile, $file or die "Can't rename $tmpfile to $file: $!";
  

Комментарии:

1. Если вы собираетесь утруждать себя выполнением всей тяжелой работы самостоятельно, вам следует обязательно закрыть оба дескриптора и проверить их возвращаемые результаты, допустив ошибку, если они завершатся неудачей. В противном случае я не вижу, что в этом чище.

2. @tchrist Это чище, потому что не нужно локализовывать шесть магических переменных и включать непонятную магию. Я могу по пальцам пересчитать, сколько раз мои проблемы решались явной проверкой close() , но те случаи, когда мне приходилось ломать голову над кодом, использующим магические переменные, требовали бы толпы людей. Честно говоря, любое из них подходит, если оно помещено в подпрограмму: edit_file($file, $search, $replace); Важно то, что OP задавал неправильный вопрос. 🙂

3. IIRC, заполненность файловой системы — один из примеров, когда проверка close() полезна.

4. @DVK да, проверка закрытия имеет свои преимущества. Я взвесил это значение, и я просто не зациклен на этом. Особенно в примерах кода. Особенно, особенно когда это имеет мало общего с вопросом OP. 😛 В конечном итоге autodie просто сделает это за меня. 🙂

Ответ №5:

Двойные кавычки интерпретируют все.

попробуйте:

 system "perl -pi -le 's/^\s //; s/\s $//; s#\s #\t#g;' file";
  

Ответ №6:

Заключенные в двойные кавычки ' не имеют функциональной ценности. Ваша строка будет отредактирована Perl. Мои тесты показывают, что вы теряете s выражения. Это также интерполирует 't' . tab — хотя, возможно, проблема не в этом.

Вот Data::Dumper возьмем:

 $s = 'perl -pi -le 's/^s //; s/s $//; s#s #    #g;' file';
  

Ответ №7:

для Linux попробуйте

 system q(perl -pi -le 's/^s //; s/s $//; s#s #t#g;' file); # comments here
  

для M$ windows:

 system q(perl -pi -le "s/^s //; s/s $//; s#s #t#g;" file); # comments here
  

Ответ №8:

Это как раз та ситуация, для которой Ларри дал нам альтернативные символы кавычек. Мы хотели бы использовать одинарные кавычки для аргумента system(), чтобы нам не приходилось выяснять, что нуждается в обратной косой черте, но нам также нужны одинарные кавычки в самой строке, заключенной в кавычки. Итак, на помощь приходят альтернативные символы кавычек!

 system q(perl -p -le 's/^s //; s/s $//; s#s #t#g;' file);
  

Теперь вы можете скопировать / вставить то, что работает в командной строке, непосредственно в ваш исходный код Perl.