Как заменить значение в строке
Всем привет!
Пытаюсь автоматизировать работу freeradius для замены значений в конфиге.
Вводные данные:
- Словарь для оборудования
APCвradius/dictionary - Блок значений для пользователя в виде,
где необходимо менять значения внутри квадратных скобок, всего значений от 1 до 24, одновременное количество значений неизвестно (т.е. пользователь может работать как с одним значением, напримерipetrov Auth-Type = ntlm_auth, APC-Service-Type = Outlet, APC-Outlets = "1[1,3,5];"APC-Outlets = "1[24];", так и с несколькими, напримерAPC-Outlets = "1[11,18,22];" - Пользователей, естественно, под сотню. А значит и таких блоков в файле
radius/users.confбудет под сотню. Не обязательно, что все сто пользователей будут работать одновременно, но иметь блоки пользователей необходимо заранее (а может, и нет?)
Задача:
- Иметь возможность динамически менять значения в
APC-Outlets = "1[11,18,22];"для нужного пользователя.
Как это вижу я:
- Пользователь запускает программу, программа принимает значение
usernameчерез командуwhoami, пользователь указывает нужные ему значения через запятую
./script.sh 1,2,3
ИЛИ программа предлагает ввести нужны значения черезinput - Далее выбирается рабочая директория freeradius
workdir=/etc/freeradius/3.0/mods-config/files/ - Программа находит блок пользователя
usernameв файле users.conf - Программа меняет значение внутри скобок
APC-Outlets = "1[];"на введенные значения изinput
Все бы хорошо, но мой стаж программирования на Python позволяет сделать только следующее:
- Пробую создать блок целиком, меняя значение переменной:
username = input('Имя пользователя: ')
apc_outlets = str(input('Номера розеток: '))
file_path = ('test.conf')
block = (username + '\n'
'\t' + '\t' + '\t' + '\t' + "Auth-Type = ntlm_auth," + '\n'
'\t' + '\t' + '\t' + '\t' + "APC-Service-Type = Outlet," + '\n'
'\t' + '\t' + '\t' + '\t' + 'APC-Outlets = "1[' + apc_outlets + '];"' + '\n' + '\n')
with open(file_path, 'a+') as file:
file.write(block)
Окей, как создавать блоки я понял, пробую менять значение внутри блока у переменной apc_outlets:
with open(file_path, 'a+') as file:
apc_outlets_new = apc_outlets
apc_replaced = block.replace(apc_outlets, apc_outlets_new)
file.write(apc_replaced)
И тут я понял, что метод replace не меняет значение внутри строки, а записывает новую строку с новым значением, т.е. сейчас я имею два блока с одним юзернеймом, но с разными значениями APC-Outlets = "1[];". Увы, это не то, что я хотел.
Ладно, попробуем посчитать строки, будем отталкиваться от них
username = input('Имя пользователя: ')
apc_outlets = str(input('Номера розеток: '))
apc = 'APC-Outlets = '
file_path = ('test.conf')
with open(file_path, 'a+') as file:
n = 0
for line in file:
n += 1
if username in line:
print(username, 'найден в строке', n)
n = 0
for line_two in file:
n += 1
if apc in line_two:
print(apc, 'найден в строке', n)
break
Отлично! Я могу найти номер строки переменной username, а потом найти номер нужной мне строки с параметрами APC-Outlets = "1[];"
И все. Дальше не могу сдвинуться с места уже неделю. Я не могу понять, как можно осуществить замену значений без удаления всего содержимого файла (например, через with open(file_path, 'r+') as file:) или без создания нового содержимого через метод replace.
Есть ли какой-то вариант решения моей задачи? Как можно менять значение в подстроке, не меняя больше ничего?
Мне очень жаль, что пришлось написать такую простыню, но я не представляю, как можно описать задачу по иному.
Возможно, вы подскажете новый вариант решения этой задачи.
Ответы (1 шт):
Вот реализовал центральную функцию modify_config() которая делает нужные действия. Её вызывает некая main() в которой читается файл плюс имя пользователя и аутлеты, модифицируется конфиг через modify_config() и записывается результирующий конфиг.
Поиск и чтение/запись именно нужного конфиг файла не делал, т.к. посчитал что основная трудность это именно внесение изменений в конфиг.
import re
def modify_config(text, user, outlets):
cuser, res, ufound, fixed = None, '', False, False
for iline, line in enumerate(text.splitlines()):
if line.strip():
m = re.fullmatch(r'^([a-zA-Z0-9_\-]+?)\s*$', line)
if m:
cuser = m.group(1)
if cuser == user:
ufound = True
else:
m = re.fullmatch(r'^\s+(.+?)\s*\=\s*(.+?)\s*$', line)
assert m, f'Unmatched line "{line}" at line {iline}!'
l, r = m.group(1), m.group(2)
if cuser == user and l == 'APC-Outlets':
m2 = re.fullmatch(r'^\".*\[(.*)\].*\"$', r)
assert m2, f'Unmatched outlets "{r}" at line {iline}!'
line = line[:m.span(2)[0] + m2.span(1)[0]] + outlets + line[m.span(2)[0] + m2.span(1)[1]:]
m3 = re.fullmatch(r'^\s+APC-Outlets\s*\=\s*\".*\[' + outlets + r'\]\.*\"\s*$', line)
assert m3, f'Unmatched resulting outlets "{line}" at line {iline}!'
fixed = True
res += line + '\n'
assert ufound, f'User "{user}" not found in config!'
assert fixed, f'No modifications made!'
return res
def main():
user = input('Username: ').strip()
outlets = ','.join([str(int(e)) for e in re.split(r'[\s,;]+', input('Outlets: ').strip()) if e.strip()])
with open('0296.txt', 'r', encoding = 'utf-8-sig') as f:
text = f.read()
rtext = modify_config(text, user, outlets)
with open('0296.txt2', 'w', encoding = 'utf-8') as f:
f.write(rtext)
if __name__ == '__main__':
main()
Пример входного файла:
ipetrov
Auth-Type = ntlm_auth,
APC-Service-Type = Outlet,
APC-Outlets = "1[1,3,5];"
isidorov
Auth-Type = ntlm_auth,
APC-Service-Type = Outlet,
APC-Outlets = "1[];"
Пример ввода консоли:
Username: ipetrov
Outlets: 1,2,3
Пример результирующего файла:
ipetrov
Auth-Type = ntlm_auth,
APC-Service-Type = Outlet,
APC-Outlets = "1[1,2,3];"
isidorov
Auth-Type = ntlm_auth,
APC-Service-Type = Outlet,
APC-Outlets = "1[];"