Как сделать sed как текст заменить на python? - PullRequest
40 голосов
/ 13 декабря 2010

Я хотел бы включить все репозитории 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 Какой самый элегантный ("питонический") способ сделать это?

Ответы [ 12 ]

46 голосов
/ 13 декабря 2010

Вы можете сделать это так:

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 / в sed / perl.

Редактировать: фиксированный синтаксис в примере

21 голосов
/ 19 июля 2015

Создание собственной замены sed на чистом Python с помощью нет внешних команд или дополнительных зависимостей - благородная задача, обремененная благородными наземными минами. Кто бы мог подумать?

Тем не менее, это возможно. Это также желательно. Мы все были там, люди: «Мне нужно взломать несколько текстовых файлов, но у меня есть только Python, два пластиковых шнурка и заплесневелая банка с вишней Maraschino бункерного качества. Помощь».

В этом ответе мы предлагаем лучшее в своем роде решение, объединяющее удивительность предыдущих ответов без всей этой неприятной , а не -видности. Как отмечает Плендра, Дэвид Миллер , в противном случае первоклассный ответ , записывает желаемый файл неатомарным образом и, следовательно, предлагает условия гонки (например, из других потоков и / или процессов, пытающихся одновременно прочитать этот файл). Это плохо. отличный ответ 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's 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')
19 голосов
/ 04 июля 2012

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)
12 голосов
/ 13 декабря 2010

Это такой другой подход, я не хочу редактировать свой другой ответ.Вложенный 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 (...), когда вы можете обойтись без регулярных выражений.

6 голосов
/ 29 апреля 2014

Если вы используете 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>)
3 голосов
/ 23 июня 2014

Попробуйте https://pypi.python.org/pypi/pysed

pysed -r '# deb' 'deb' /etc/apt/sources.list

2 голосов
/ 24 июня 2014

Если вы действительно хотите использовать команду sed без установки нового модуля Python, вы можете просто сделать следующее:

import subprocess
subprocess.call("sed command")
2 голосов
/ 13 декабря 2010

Вы можете сделать что-то вроде:

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/

2 голосов
/ 13 декабря 2010

Не уверен насчет элегантности, но это должно быть довольно читабельно, по крайней мере. Для списка 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)

РЕДАКТИРОВАТЬ : Используйте with -статью для лучшей обработки файла. Также забыл перемотать перед усечением раньше.

1 голос
/ 02 февраля 2016

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

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...