GNU/Linux >> Tutoriels Linux >  >> Linux

Comment remplacer sed comme texte par python?

Vous pouvez le faire comme ceci :

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

L'instruction with garantit que le fichier est fermé correctement et rouvre le fichier dans "w" mode vide le fichier avant que vous y écriviez. re.sub(pattern, replace, string) est l'équivalent de s/pattern/replace/ dans sed/perl.

Modifier : syntaxe fixe dans l'exemple


Création d'un sed maison remplacement en Python pur par non commandes externes ou dépendances supplémentaires est une noble tâche chargée de nobles mines terrestres. Qui aurait cru ?

Néanmoins, c'est faisable. C'est aussi souhaitable. Nous sommes tous passés par là, les gens :"J'ai besoin de munge certains fichiers en texte clair, mais je n'ai que du Python, deux lacets en plastique et une boîte moisie de cerises au marasquin de qualité bunker. Aide."

Dans cette réponse, nous proposons une solution de premier ordre combinant la génialité des réponses précédentes sans tout ce désagréable pas -superbe. Comme le note plundra, la réponse par ailleurs excellente de David Miller écrit le fichier souhaité de manière non atomique et invite donc les conditions de concurrence (par exemple, à partir d'autres threads et/ou processus essayant de lire simultanément ce fichier). C'est mauvais. La réponse par ailleurs excellente de Plundra résout cela problème tout en introduisant encore plus - y compris de nombreuses erreurs d'encodage fatales, une vulnérabilité de sécurité critique (ne pas conserver les autorisations et autres métadonnées du fichier d'origine) et une optimisation prématurée remplaçant les expressions régulières par une indexation de caractères de bas niveau. C'est aussi mauvais.

Génial, unissez-vous !

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

massedit.py (http://github.com/elmotec/massedit) fait l'échafaudage pour vous en laissant juste la regex à écrire. Il est encore en version bêta, mais nous attendons vos commentaires.

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

affichera les différences (avant/après) au format diff.

Ajoutez l'option -w pour écrire les modifications dans le fichier d'origine :

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

Alternativement, vous pouvez maintenant utiliser l'API :

>>> import massedit
>>> filenames = ['/etc/apt/sources.list']
>>> massedit.edit_files(filenames, ["re.sub(r'^# deb', 'deb', line)"], dry_run=True)

C'est une approche tellement différente, je ne veux pas modifier mon autre réponse.Imbriqué with puisque je n'utilise pas 3.1 (Où with A() as a, B() as b: fonctionne).

Peut-être un peu exagéré de changer sources.list, mais je veux le mettre là-bas pour de futures recherches.

#!/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)

Cela devrait garantir l'absence de conditions de concurrence entre les autres personnes lisant le fichier. Oh, et je préfère str.startswith(...) lorsque vous pouvez vous passer d'une expression régulière.


Linux
  1. Manipulation de texte en ligne de commande avec sed

  2. Comment remplacer une chaîne par une chaîne contenant une barre oblique avec Sed ?

  3. Comment déplacer une ligne vers le haut ou vers le bas d'une ligne dans un fichier texte ?

  4. Remplacer la plage de lignes par la plage de lignes (sed ou autres) ?

  5. Comment utiliser sed pour remplacer la variable d'un fichier de configuration ?

Manipuler du texte avec sed et grep

Comment insérer du texte à la 1ère ligne d'un fichier à l'aide de sed ?

remplacer la nième occurrence de chaîne dans chaque ligne d'un fichier texte

Comment remplacer un fichier en jar par une ligne de commande sous Linux ?

sed :comment remplacer la ligne si elle est trouvée ou ajouter à la fin du fichier si elle n'est pas trouvée ?

Comment remplacer récursivement les caractères par sed ?