Все утилиты, допускающие редактирование файлов в -месте, делают это путем внутренней записи результата во временный файл и последующей замены исходного файла этим временным файлом после завершения операции.
Файлы можно изменить на месте, но вы сможете только перезаписать существующие данные и/или увеличить длину файла. Это можно сделать с помощью утилиты dd
, например:
$ cat file.txt
hello world
abc abc 123 123
$ cat insert.txt
hello!
$ dd if=insert.txt of=file.txt bs=1 seek=6 conv=notrunc
7+0 records in
7+0 records out
7 bytes transferred in 0.000 secs (30918 bytes/sec)
$ cat file.txt
hello hello!
bc abc 123 123
Здесь мы вставляем то, что находится в insert.txt
, в file.txt
, сначала просматривая 6 байтов вперед в файле (, обходя hello
и пробел ), а затем изменяя его. conv=notrunc
предотвращает усечение выходного файла в конце операции записи.
Если бы if=insert.txt
было опущено, можно было бы вставить любой текст с клавиатуры. Обратите внимание, что термин «вставка» здесь неправильный. «Перезапись» может лучше описать то, что происходит (, посмотрите, как первый символ второй строки перезаписывается новой строкой в концеinsert.txt
).
Однако вам вряд ли захочется редактировать файл таким образом.
Использование соответствия префикса имени переменной и косвенного обращения к переменной вbash
:
proj_env_repo_db_username=username
proj_env_repo_db_host=host
proj_env_repo_db_port=port
for variable in "${!proj_env_repo@}"; do
export "TF_ENV${variable#proj_env_repo}"="${!variable}"
done
Цикл использует "${!proj_env_repo@}"
для создания списка имен переменных, которые имеют префикс имени proj_env_repo
. В каждой итерации $variable
будет именем одной из этих переменных.
Внутри цикла export
используется для создания новой переменной среды путем удаления префикса proj_env_repo
и замены его строкой TF_ENV
. Значение для новой переменной окружения получается с помощью косвенной переменной ${!variable}
, т. е. значения переменной, имя которой хранится в $variable
.
Чтобы дополнительно сбросить исходную переменную, используйте unset "$variable"
после export
до конца цикла.
Тестовый запуск с включенной трассировкой:
$ bash -x script.sh
+ proj_env_repo_db_username=username
+ proj_env_repo_db_host=host
+ proj_env_repo_db_port=port
+ for variable in "${!proj_env_repo@}"
+ export TF_ENV_db_host=host
+ TF_ENV_db_host=host
+ for variable in "${!proj_env_repo@}"
+ export TF_ENV_db_port=port
+ TF_ENV_db_port=port
+ for variable in "${!proj_env_repo@}"
+ export TF_ENV_db_username=username
+ TF_ENV_db_username=username
Как функция, принимающая префикс старого имени в качестве первого аргумента и новый префикс в качестве второго аргумента:
rename_var () {
# Make sure arguments are valid as variable name prefixes
if ! [[ $1 =~ ^[a-zA-Z_][a-zA-Z_0-9]*$ ]] ||
! [[ $2 =~ ^[a-zA-Z_][a-zA-Z_0-9]*$ ]]
then
echo 'bad variable name prefix' >&2
return 1
fi
eval 'for variable in "${!'"$1"'@}"; do
export "'"$2"'${variable#'"$1"'}"="${!variable}"
done'
}
Здесь мы прибегаем к использованию eval
поверх цикла, поскольку bash
не поддерживает синтаксис ${!$1@}
. Функция создает соответствующий шелл-код (в виде строки )для переименования переменных в соответствии со значениями $1
и$2
(1-го и 2-го аргументов, переданных функции ), а затем использует eval
для выполнения этого шелл-кода.
Эту функцию можно использовать как
rename_var project_env_repo TF_ENV
... или, используя переменные,
rename_var "$old_variable_prefix" "$new_variable_prefix"
Примечание :При выполнении подобных действий (с использованием eval
пользовательского ввода )вы должны проверить, что код, который вы оцениваете, действителен и соответствует вашим ожиданиям. это быть. В данном случае это означает проверку $1
и $2
как допустимых префиксов имен переменных.В противном случае хотя бы кавычки и }
вызовут синтаксические ошибки в eval
, и есть вероятность внедрения команд.
Примечание :Впервые (я думаю ), что у меня когда-либо было использованиеeval
. Я бы никогда не поставил себя в положение, когда мне нужно использовать приведенный выше код, но я, очевидно, не знаю предыстории вопроса, так что это не настоящая критика вопроса (, который сам по себе интересен. ).
Похожие (на сайте Software Engineering):
while IFS='=' read -r name value; do
new_name="TF_ENV${name#proj_env_repo}"
export "$new_name=$value"
unset "$name"
done < <( printenv | grep proj_env_repo )
Секретные ингредиенты здесь:
IFS='=' read -r name value
--использует знак = для разделения входящей строки на составные части. for v in `printenv | grep proj_env_repo`
do
eval `echo "$v=\$$v" | sed -e 's/proj_env_repo/TF_ENV/'`
unset $v
done
echo
печатает:
proj_env_repo_db_username=$proj_env_repo_db_username
sed
преобразует первый экземпляр proj_env_repo
в TF_ENV
.
TF_ENV_db_username=$proj_env_repo_db_username
eval
оценивает этот оператор.
unset
обеспечивает удаление старой переменной.