Простой поиск и замена без регулярного выражения

У меня есть файл с различными подстановочными знаками, которые я хочу заменить из оболочки bash script. У меня есть следующее, которое отлично работает, пока одна из переменных не содержит символов, специально предназначенных для регулярных выражений:

VERSION="1.0"
perl -i -pe "s/VERSION/${VERSION}/g" txtfile.txt   # no problems here

APP_NAME="../../path/to/myapp"
perl -i -pe "s/APP_NAME/${APP_NAME}/g" txtfile.txt  # Error!

Поэтому вместо этого я хочу что-то, что просто выполняет буквальную замену текста, а не регулярное выражение. Существуют ли какие-либо простые однострочные вызовы с perl или другим инструментом, который это сделает?

Ответы

Ответ 1

Как насчет следующего:

perl -i -pe "s|APP_NAME|\\Q${APP_NAME}|g" txtfile.txt

Так как вертикальная полоса не является юридическим символом как часть пути, вам хорошо идти.

Ответ 2

"Правильный" способ сделать это состоит в том, чтобы избежать содержимого переменных оболочки, чтобы они не воспринимались как специальные символы регулярных выражений. Вы можете сделать это в Perl с \Q, как в

s/APP_NAME/\Q${APP_NAME}/g

но при вызове из оболочки script обратная косая черта должна быть удвоена, чтобы избежать ее потери, например

perl -i -pe "s/APP_NAME/\\Q${APP_NAME}/g" txtfile.txt

Но я предлагаю, чтобы было намного проще написать весь script в Perl

Ответ 3

Вам не нужно использовать регулярное выражение для этого:

perl -pe '
  foreach $var ("VERSION", "APP_NAME") {
    while (($i = index($_, $var)) != -1) {
      substr($_, $i, length($var)) = $ENV{$var};
    }
  }
'

Убедитесь, что вы export ваши переменные.

Ответ 4

Мне не очень нравится этот ответ, потому что должен быть лучший способ сделать буквальную замену в perl. \Q является загадочным. Использование quotemeta добавляет дополнительные строки кода.

Но... Вы можете использовать substr для замены части строки.

#!/usr/bin/perl
my $name = "Jess.*";
my $sentence = "Hi, my name is Jess.*, dude.\n";
my $new_name = "Prince//";
my $name_idx = index $sentence, $name;
if ($name_idx >= 0) {
    substr ($sentence, $name_idx, length($name), $new_name ); 
}
print $sentence;

Вывод:

Hi, my name is Prince//, dude.

Ответ 5

<суб >

 perl -i -pe "s/APP_NAME/\Q${APP_NAME}\E/g" txtfile.txt

Забастовкa > суб >

По какой-то причине это не совсем так, как рекламируется. Эта вариация, похоже, работает, хотя:

 perl -i -pe "\$r = qq/\Q${APP_NAME}\E/; s/APP_NAME/\$r/go"

Обоснование: http://perldoc.perl.org/perlre.html#Escape-sequences

Ответ 6

Вы можете использовать регулярное выражение, но избегать любых специальных символов.

Что-то вроде этого может работать.

APP_NAME="../../path/to/myapp"
APP_NAME=`echo "$APP_NAME" | sed -e '{s:/:\/:}'`
perl -i -pe "s/APP_NAME/${APP_NAME}/g" txtfile.txt

Ответ 7

Мне удалось получить рабочее решение, частично основанное на кусочках и ответах других народов:

app_name='../../path/to/myapp'
perl -pe "\$r = q/${app_name//\//\\/}/; s/APP_NAME/\$r/g" <<<'APP_NAME'

Это создает переменную perl $r из результата расширения параметра оболочки:

${app_name//\//\\/}

${            # open parameter expansion
app_name      # variable name 
//            # start global substitution
\/            # match / (backslash-escaped to avoid being interpreted as delimiter)
/             # delimiter
\\/           # replace with \/ (literal backslash needs to be escaped)
}             # close parameter expansion

Вся эта работа необходима для предотвращения обработки косой черты внутри этой переменной как синтаксиса perl, которая в противном случае закрыла бы кавычки q// вокруг строки.

В заменяемой части используйте переменную $r (экранируется $, чтобы она не обрабатывалась как переменная оболочки в двойных кавычках).

Тестирование:

$ app_name='../../path/to/myapp'
$ perl -pe "\$r = q/${app_name//\//\\/}/; s/APP_NAME/\$r/g" <<<'APP_NAME'
../../path/to/myapp