Как сделать sed, как текст, заменить на python?
Я хотел бы включить все apt-репозитории в этом файле
cat /etc/apt/sources.list
## Note, this file is written by cloud-init on first boot of an instance
## modifications made here will not survive a re-bundle.
## if you wish to make changes you can:
## a.) add 'apt_preserve_sources_list: true' to /etc/cloud/cloud.cfg
## or do the same in user-data
## b.) add sources in /etc/apt/sources.list.d
#
# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to
# newer versions of the distribution.
deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick main
deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick main
## Major bug fix updates produced after the final release of the
## distribution.
deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-updates main
deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-updates main
## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
## team. Also, please note that software in universe WILL NOT receive any
## review or updates from the Ubuntu security team.
deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick universe
deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick universe
deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-updates universe
deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-updates universe
## N.B. software from this repository is ENTIRELY UNSUPPORTED by the Ubuntu
## team, and may not be under a free licence. Please satisfy yourself as to
## your rights to use the software. Also, please note that software in
## multiverse WILL NOT receive any review or updates from the Ubuntu
## security team.
# deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick multiverse
# deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick multiverse
# deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-updates multiverse
# deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-updates multiverse
## Uncomment the following two lines to add software from the 'backports'
## repository.
## N.B. software from this repository may not have been tested as
## extensively as that contained in the main release, although it includes
## newer versions of some applications which may provide useful features.
## Also, please note that software in backports WILL NOT receive any review
## or updates from the Ubuntu security team.
# deb http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-backports main restricted universe multiverse
# deb-src http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ maverick-backports main restricted universe multiverse
## Uncomment the following two lines to add software from Canonical's
## 'partner' repository.
## This software is not part of Ubuntu, but is offered by Canonical and the
## respective vendors as a service to Ubuntu users.
# deb http://archive.canonical.com/ubuntu maverick partner
# deb-src http://archive.canonical.com/ubuntu maverick partner
deb http://security.ubuntu.com/ubuntu maverick-security main
deb-src http://security.ubuntu.com/ubuntu maverick-security main
deb http://security.ubuntu.com/ubuntu maverick-security universe
deb-src http://security.ubuntu.com/ubuntu maverick-security universe
# deb http://security.ubuntu.com/ubuntu maverick-security multiverse
# deb-src http://security.ubuntu.com/ubuntu maverick-security multiverse
С sed это простой sed -i 's/^# deb/deb/' /etc/apt/sources.list
самый элегантный ( "питонический" ) способ сделать это?
Ответы
Ответ 1
massedit.py(http://github.com/elmotec/massedit) делает строительные леса для вас, оставляя только регулярное выражение для записи. Он все еще находится в бета-версии, но мы ищем отзывы.
python -m massedit -e "re.sub(r'^# deb', 'deb', line)" /etc/apt/sources.list
покажет различия (до/после) в формате diff.
Добавьте параметр -w для записи изменений в исходный файл:
python -m massedit -e "re.sub(r'^# deb', 'deb', line)" -w /etc/apt/sources.list
Кроме того, теперь вы можете использовать api:
>>> import massedit
>>> filenames = ['/etc/apt/sources.list']
>>> massedit.edit_files(filenames, ["re.sub(r'^# deb', 'deb', line)"], dry_run=True)
Ответ 2
Вы можете сделать это вот так:
with open("/etc/apt/sources.list", "r") as sources:
lines = sources.readlines()
with open("/etc/apt/sources.list", "w") as sources:
for line in lines:
sources.write(re.sub(r'^# deb', 'deb', line))
Инструкция with гарантирует, что файл будет закрыт правильно, и повторное открытие файла в режиме "w"
освобождает файл перед его записью. re.sub(pattern, replace, string) является эквивалентом s/pattern/replace/in sed/perl.
Изменить: фиксированный синтаксис в примере
Ответ 3
Создание домашней замены sed
в чистом Python без внешних команд или дополнительных зависимостей - это благородная задача, насыщенная благородными наземными минами. Кто бы мог подумать?
Тем не менее, это возможно. Это также желательно. Мы все были там, люди: "Мне нужно проложить некоторые файлы с открытым текстом, но у меня есть только Python, два пластиковых шнурка и заплесневелая банка из винных мараскинских сортов".
В этом ответе мы предлагаем лучшее в своем роде решение, позволяющее объединить удивительность предыдущих ответов без всякой этой неприятной не-удивительности. В качестве замечаний plundra, David Miller в противном случае первоклассный ответ записывает нужный файл неатомно и, следовательно, приглашает условия гонки (например, из других потоков и/или процессов, пытающихся одновременно прочитайте этот файл). Это плохо. Plundra в противном случае отличный ответ решает эту проблему, в то же время представляя еще больше - включая многочисленные ошибки фатального кодирования, критическую уязвимость безопасности (неспособность сохранить разрешения и другие метаданные исходного файла), и преждевременная оптимизация, заменяющая регулярные выражения с низкоуровневой индексацией символов. Это также плохо.
Удивительно, объединяйтесь!
import re, shutil, tempfile
def sed_inplace(filename, pattern, repl):
'''
Perform the pure-Python equivalent of in-place `sed` substitution: e.g.,
`sed -i -e 's/'${pattern}'/'${repl}' "${filename}"`.
'''
# For efficiency, precompile the passed regular expression.
pattern_compiled = re.compile(pattern)
# For portability, NamedTemporaryFile() defaults to mode "w+b" (i.e., binary
# writing with updating). This is usually a good thing. In this case,
# however, binary writing imposes non-trivial encoding constraints trivially
# resolved by switching to text writing. Let do that.
with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp_file:
with open(filename) as src_file:
for line in src_file:
tmp_file.write(pattern_compiled.sub(repl, line))
# Overwrite the original file with the munged temporary file in a
# manner preserving file attributes (e.g., permissions).
shutil.copystat(filename, tmp_file.name)
shutil.move(tmp_file.name, filename)
# Do it for Johnny.
sed_inplace('/etc/apt/sources.list', r'^\# deb', 'deb')
Ответ 4
Это такой подход, я не хочу редактировать другой ответ.
Вложенный with
, так как я не использую 3.1 (где with A() as a, B() as b:
работает).
Может быть немного переборщить, чтобы изменить sources.list, но я хочу поместить его там для будущих поисков.
#!/usr/bin/env python
from shutil import move
from tempfile import NamedTemporaryFile
with NamedTemporaryFile(delete=False) as tmp_sources:
with open("sources.list") as sources_file:
for line in sources_file:
if line.startswith("# deb"):
tmp_sources.write(line[2:])
else:
tmp_sources.write(line)
move(tmp_sources.name, sources_file.name)
Это должно гарантировать отсутствие условий гонки других людей, читающих файл.
О, и я предпочитаю str.startswith(...), когда вы можете обойтись без регулярного выражения.
Ответ 5
Если вы используете Python3, следующий модуль поможет вам:
https://github.com/mahmoudadel2/pysed
wget https://raw.githubusercontent.com/mahmoudadel2/pysed/master/pysed.py
Поместите файл модуля в свой путь к модулю Python3, затем:
import pysed
pysed.replace(<Old string>, <Replacement String>, <Text File>)
pysed.rmlinematch(<Unwanted string>, <Text File>)
pysed.rmlinenumber(<Unwanted Line Number>, <Text File>)
Ответ 6
Попробуйте https://pypi.python.org/pypi/pysed
pysed -r '# deb' 'deb'/etc/apt/sources.list
Ответ 7
Не уверен в элегантности, но, по крайней мере, это должно быть довольно удобочитаемо. Для sources.list прекрасно читать все строки перед рукой, для чего-то большего вы можете захотеть изменить "на месте" при прохождении через него.
#!/usr/bin/env python
# Open file for reading and writing
with open("sources.list", "r+") as sources_file:
# Read all the lines
lines = sources_file.readlines()
# Rewind and truncate
sources_file.seek(0)
sources_file.truncate()
# Loop through the lines, adding them back to the file.
for line in lines:
if line.startswith("# deb"):
sources_file.write(line[2:])
else:
sources_file.write(line)
EDIT: используйте with
-statement для лучшей обработки файлов. Также забыл перемотать перед усечением раньше.
Ответ 8
Вы можете сделать что-то вроде:
p = re.compile("^\# *deb", re.MULTILINE)
text = open("sources.list", "r").read()
f = open("sources.list", "w")
f.write(p.sub("deb", text))
f.close()
В качестве альтернативы (imho, это лучше с организационной точки зрения) вы могли бы разделить sources.list
на куски (одну запись/один репозиторий) и поместить их под /etc/apt/sources.list.d/
Ответ 9
Здесь одномодовая замена Python для perl -p
:
# Provide compatibility with `perl -p`
# Usage:
#
# python -mloop_over_stdin_lines '<program>'
# In, `<program>`, use the variable `line` to read and change the current line.
# Example:
#
# python -mloop_over_stdin_lines 'line = re.sub("pattern", "replacement", line)'
# From the perlrun documentation:
#
# -p causes Perl to assume the following loop around your
# program, which makes it iterate over filename arguments
# somewhat like sed:
#
# LINE:
# while (<>) {
# ... # your program goes here
# } continue {
# print or die "-p destination: $!\n";
# }
#
# If a file named by an argument cannot be opened for some
# reason, Perl warns you about it, and moves on to the next
# file. Note that the lines are printed automatically. An
# error occurring during printing is treated as fatal. To
# suppress printing use the -n switch. A -p overrides a -n
# switch.
#
# "BEGIN" and "END" blocks may be used to capture control
# before or after the implicit loop, just as in awk.
#
import re
import sys
for line in sys.stdin:
exec(sys.argv[1], globals(), locals())
try:
print line,
except:
sys.exit('-p destination: $!\n')
Ответ 10
Если вы действительно хотите использовать команду sed
без установки нового модуля Python, вы можете просто сделать следующее:
import subprocess
subprocess.call("sed command")
Ответ 11
Я хотел иметь возможность находить и заменять текст, но также включать сопоставленные группы в содержимое, которое я вставляю. Я написал этот короткий script для этого:
https://gist.github.com/turtlemonvh/0743a1c63d1d27df3f17
Ключевым компонентом этого является то, что выглядит примерно так:
print(re.sub(pattern, template, text).rstrip("\n"))
Вот пример того, как это работает:
# Find everything that looks like 'dog' or 'cat' followed by a space and a number
pattern = "((cat|dog) (\d+))"
# Replace with 'turtle' and the number. '3' because the number is the 3rd matched group.
# The double '\' is needed because you need to escape '\' when running this in a python shell
template = "turtle \\3"
# The text to operate on
text = "cat 976 is my favorite"
Вызов этой функции с помощью этой функции дает:
turtle 976 is my favorite
Ответ 12
У Python есть модуль регулярных выражений (import re). почему вы не хотите использовать его, как это сделано в perl. Он имеет все функции регулярного выражения perl