Как не интерпретировать строку как команду

Вопрос или проблема

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

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

$DB_HOST = "LegitDbHost";

А в целевом файле есть строка следующего вида:

$DB_HOST = "testDbHost";

Мне нужно заменить целевую строку на исходную, и я хочу вывести что-то вроде этого:

Заменяем строку 12: '$DB_HOST = "testDbHost";' ==с==> '$DB_HOST = "LegitDbHost";'(y/n): 

Пока что у меня есть следующее:

toReplace="$DB_HOST";

sourceLine=$(cat ./sourceFile.php | grep -m 1 "$toReplace[[:space:],=]");
destLineNr=$(cat ./destFile.php | grep -n -m 1 "$toReplace[[:space:],=]" | grep -Eo '^[^:]+');
destLine=$(cat ./destFile.php | grep -m 1 "$toReplace[[:space:],=]");
read -p "Заменяем строку $destLineNr: $destLine ==с==> $sourceLine.(y/n): ";

Однако, мой вывод выглядит следующим образом:

==с==> $DB_HOST    ST   = "testDbHost";.(y/n): 

Я думаю, это потому, что в sourceLine есть ;, и это интерпретируется как конец команды. Я просто не знаю, как это обойти.

Надеюсь, это понятно.

Правка:
как было предложено, я попробовал использовать echo -n

toReplace="$DB_HOST";

sourceLine=$(cat ./sourceFile.php | grep -m 1 "$toReplace[[:space:],=]");
destLineNr=$(cat ./destFile.php | grep -n -m 1 "$toReplace[[:space:],=]" | grep -Eo '^[^:]+');
destLine=$(cat ./destFile.php | grep -m 1 "$toReplace[[:space:],=]");

echo -n "Заменяем строку $destLineNr: "
echo -n $destLine
echo -n " ===с===> "
echo -n $sourceLine

это выводит:

===с===> $DB_HOST = "testDbHost";OS";

Попробуйте:

#! /bin/zsh -
die() {
  print -ru2 -- "$@"
  exit 1
}

srcFile=./sourceFile.php
dstFile=./destFile.php

toReplace="$DB_HOST"

sourceLine=$(<"$srcFile" grep -Pm1 "\Q$toReplace\E[\s,=]") ||
  die "Не могу найти $toReplace в $srcFile"

<"$dstFile" grep -n -Pm1 "\Q$toReplace\E[\s,=]" |
  IFS=: read -r destLineNr destLine ||
  die "Не могу найти $toReplace в $dstFile"

if
  read -q "?Заменяем строку $destLineNr: ${(q+)destLine} ==с==> ${(q+)sourceLine}? (y/n): "
then
  (
     export destLineNr destLine
     perl -lpi -e '$_ = $ENV{destLine} if $. == $ENV{destLineNr}' -- "$dstFile"
  )
fi

Используя ${(q+)line} для расширения параметров zsh, вы убедитесь, что символы CR в этой переменной (поскольку, похоже, в ваших входных файлах используются разделители MSDOS CRLF вместо Unix LF) отображаются как C-M, а не отправляются как есть в терминал (для терминала символ CR заставляет курсор перемещаться обратно в начало строки (делает возврат каретки для телетайпов)).

Ответ или решение

Чтобы избежать интерпретации строки как команды в вашем сценарии копирования и редактирования файлов, необходимо правильно обрабатывать переменные и значения, содержащие специальные символы. Рассмотрим вашу задачу более детально, предложив варианты решения с объяснениями.

Проблема

Вы пытаетесь заменить строку в одном файле (целевой файл) строкой из другого файла (исходный файл) и вывести соответствующее сообщение с переменными. Проблема возникает из-за того, что некоторые символы в переменных, такие как ;, могут интерпретироваться как конец команды или вызывать другие нежелательные эффекты в оболочке.

Решение

  1. Обработка строк: Для того чтобы избежать неверной интерпретации, нам необходимо использовать безопасные механизмы обработки строк.

  2. Использование printf вместо echo: printf более предсказуем в обработке специальных символов и лучше подходит для форматирования выводимых данных.

  3. Использование кода, который сохраняет «черные» символы: Например, использование переменной с "$..." вместо $(...) может помочь сохранить форматирование строки.

Пример кода

Вот предложение вашего скрипта с учетом вышеупомянутых нюансов:

#!/bin/bash

# Определяем файлы
srcFile="./sourceFile.php"
dstFile="./destFile.php"

# Строка для замены
toReplace="\$DB_HOST"

# Получаем строку из исходного файла
sourceLine=$(grep -m 1 -E "\Q$toReplace\E[\s=]" "$srcFile")
if [ -z "$sourceLine" ]; then
    echo "Не удалось найти $toReplace в $srcFile" >&2
    exit 1
fi

# Получаем строку и номер из целевого файла
destLineInfo=$(grep -n -m 1 -E "\Q$toReplace\E[\s=]" "$dstFile")
if [ -z "$destLineInfo" ]; then
    echo "Не удалось найти $toReplace в $dstFile" >&2
    exit 1
fi

# Извлекаем номер строки и строку
destLineNr=$(echo "$destLineInfo" | cut -d: -f1)
destLine=$(echo "$destLineInfo" | cut -d: -f2-)

# Выводим сообщение с заменой
read -p "Замена строки $destLineNr: \"$destLine\" ==с==> \"$sourceLine\"?(y/n): " confirm

if [[ $confirm == [yY] ]]; then
    # Выполняем замену
    sed -i "${destLineNr}s/.*/$sourceLine/" "$dstFile"
    echo "Строка успешно заменена."
else
    echo "Замена отменена."
fi

Пояснения к коду:

  • Использование grep: Мы ищем строку, используя grep -E, что позволяет нам использоать регулярные выражения, где \Q и \E обрабатывают специальные символы как обычные.

  • Безопасная обработка переменных: Мы используем двойные кавычки везде, когда обращаемся к переменным, что предотвращает нежелательное расширение или интерпретацию переменных.

  • Вывод с помощью read: Этот способ позволяет сделать вывод более пользовательским, предоставляя пользователю возможность подтвердить замену.

  • Использование sed для замены: Мы используем sed для фактической замены строки в файле, что является стандартным и эффективным способом редактирования текстовых файлов в UNIX.

Заключение

Настоящее решение делает скрипт более надежным и понятным, минимизируя шансы на ошибку интерпретации строк. Следование этим принципам также способствует улучшению качества кода, что важно в профессиональной среде разработки.

Оцените материал
Добавить комментарий

Капча загружается...