web-dev-qa-db-ja.com

テキストのような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これを行う最もエレガントな(「Python」)方法は何ですか?

43
Maxim Veksler

massedit.py( http://github.com/elmotec/massedit )は、正規表現だけを残して足場を作成します。まだベータ版ですが、フィードバックを探しています。

python -m massedit -e "re.sub(r'^# deb', 'deb', line)" /etc/apt/sources.list

差分形式で差異(前/後)を表示します。

-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)
20
elmotec

次のようにできます:

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)は、sed/Perlのs/pattern/replace /と同等です。

編集:例の構文を修正

50
David Miller

自家製のsed置換を純粋なPython withno外部コマンドまたは追加の依存関係で作成することは、高貴な地雷を抱えた高貴なタスクです。誰が考えただろうか?

それにもかかわらず、それは実行可能です。それも望ましいです。 「平文ファイルをいじる必要がありますが、Python、2つのプラスチック製靴ひも、バンカー級のマラスキーノチェリーのかびの生えた缶しか持っていません。助けてください。」

この回答では、不快なnot-awesomenessなしで、以前の回答のすばらしさを組み合わせた最高のソリューションを提供します。 plundraが指摘しているように、David Millerの それ以外の場合は最高の答え は、目的のファイルを非原子的に書き込み、したがって競合状態を招きます(たとえば、そのファイルを同時に読み取ろうとする他のスレッドやプロセスから)。良くないね。 Plundraの それ以外の場合は優れた答え は、さらに多くの致命的なエンコードエラー、重大なセキュリティ脆弱性(権限やその他のメタデータの保持に失敗)を導入しながら、thatの問題を解決します元のファイルの)、および正規表現を低レベルの文字インデックスに置き換える時期尚早な最適化。それも悪いです。

素晴らしい、団結!

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')
22
Cecil Curry

これは非常に異なるアプローチです。他の答えを編集したくありません。 3.1を使用していないため、withをネストしています(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(...)を好む。

12
plundra

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
M. Adel

試す https://pypi.python.org/pypi/pysed

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

3
dslackw

新しいPythonモジュールをインストールせずにsedコマンドを本当に使用したい場合は、次のようにするだけです。

import subprocess
subprocess.call("sed command")
2
Brad Jasperson

次のようなことができます:

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()

別の方法として(これは組織の観点からはより良いです)、sources.listピース(1つのエントリ/ 1つのリポジトリ)に分割し、/etc/apt/sources.list.d/

2
barti_ddu

エレガントについては定かではありませんが、少なくともかなり読みやすいはずです。 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を使用します。また、前に切り捨てる前に巻き戻すのを忘れていました。

2
plundra

テキストを検索して置換できるようにしたいだけでなく、挿入したコンテンツに一致するグループも含めたいと思いました。そのための短いスクリプトを書きました。

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
1
turtlemonvh

これが1モジュール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')
1
Matt McClure

Pythonには正規表現モジュール(import re)があります。 Perlで行われているように使用したくない理由。 Perl正規表現のすべての機能を備えています

0
Arunmu

Cecil Curry には素晴らしい答えがありますが、彼の答えは複数行の正規表現に対してのみ機能します。複数行の正規表現はほとんど使用されませんが、時には便利です。

以下は、sed_inplace関数の改善点です。これにより、要求された場合に複数行の正規表現で機能できるようになります。

警告:複数行モードでは、ファイル全体を読み込んでから正規表現の置換を実行するため、小さなモードのファイルでのみこのモードを使用します-ギガバイトサイズのファイルでこれを実行しないでください。複数行モードで実行している場合。

import re, shutil, tempfile

def sed_inplace(filename, pattern, repl, multiline = False):
    '''
    Perform the pure-Python equivalent of in-place `sed` substitution: e.g.,
    `sed -i -e 's/'${pattern}'/'${repl}' "${filename}"`.
    '''
    re_flags = 0
    if multiline:
        re_flags = re.M

    # For efficiency, precompile the passed regular expression.
    pattern_compiled = re.compile(pattern, re_flags)

    # 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:
            if multiline:
                content = src_file.read()
                tmp_file.write(pattern_compiled.sub(repl, content))
            else:
                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)

from os.path import expanduser
sed_inplace('%s/.gitconfig' % expanduser("~"), r'^(\[user\]$\n[ \t]*name = ).*$(\n[ \t]*email = ).*', r'\1John Doe\[email protected]', multiline=True)
0
Keithel