Вопрос или проблема
Я пишу скрипт, который должен копировать систему клиента и изменять некоторые строки в файле.
Есть исходный файл, в котором есть строка следующего вида:
$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 заставляет курсор перемещаться обратно в начало строки (делает возврат каретки для телетайпов)).
Ответ или решение
Чтобы избежать интерпретации строки как команды в вашем сценарии копирования и редактирования файлов, необходимо правильно обрабатывать переменные и значения, содержащие специальные символы. Рассмотрим вашу задачу более детально, предложив варианты решения с объяснениями.
Проблема
Вы пытаетесь заменить строку в одном файле (целевой файл) строкой из другого файла (исходный файл) и вывести соответствующее сообщение с переменными. Проблема возникает из-за того, что некоторые символы в переменных, такие как ;
, могут интерпретироваться как конец команды или вызывать другие нежелательные эффекты в оболочке.
Решение
-
Обработка строк: Для того чтобы избежать неверной интерпретации, нам необходимо использовать безопасные механизмы обработки строк.
-
Использование
printf
вместоecho
:printf
более предсказуем в обработке специальных символов и лучше подходит для форматирования выводимых данных. -
Использование кода, который сохраняет «черные» символы: Например, использование переменной с
"$..."
вместо$(...)
может помочь сохранить форматирование строки.
Пример кода
Вот предложение вашего скрипта с учетом вышеупомянутых нюансов:
#!/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.
Заключение
Настоящее решение делает скрипт более надежным и понятным, минимизируя шансы на ошибку интерпретации строк. Следование этим принципам также способствует улучшению качества кода, что важно в профессиональной среде разработки.