#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.