web-dev-qa-db-fra.com

comment "fichier source" en script python

J'ai un fichier texte /etc/default/foo qui contient une ligne:

FOO="/path/to/foo"

Dans mon script python, je dois faire référence à la variable FOO.

Quel est le moyen le plus simple de "source" le fichier /etc/default/foo dans mon script python, comme je le ferais avec bash?

. /etc/default/foo
9
Martin Vegter

Vous pouvez utiliser execfile :

execfile("/etc/default/foo")

Mais sachez que cela évaluera le contenu du fichier tel quel dans la source de votre programme. C'est un danger potentiel pour la sécurité, à moins que vous ne puissiez pleinement faire confiance à la source.

Cela signifie également que le fichier doit être conforme à la syntaxe python (votre exemple de fichier est).

9
jil

Juste pour donner une approche différente, notez que si votre fichier original est configuré en tant que 

export FOO=/path/to/foo

Vous pouvez créer source /etc/default/foo; python myprogram.py (ou . /etc/default/foo; python myprogram.py) et dans myprogram.py, toutes les valeurs exportées dans le fichier sourced sont visibles dans os.environ, par exemple.

import os
os.environ["FOO"] 
3
Foon

Si vous savez avec certitude qu'il ne contient que des variables de style VAR="QUOTED STRING", comme ceci:

FOO="some value"

Ensuite, vous pouvez simplement faire ceci:

>>> with open('foo.sysconfig') as fd:
...   exec(fd.read())

Ce qui vous amène:

>>> FOO
'some value'

(C'est effectivement la même chose que la solution execfile() Suggérée dans l'autre réponse.)

Cette méthode a des implications importantes en matière de sécurité; si au lieu de FOO="some value" votre fichier contenait:

os.system("rm -rf /")

Alors vous seriez en difficulté.

Alternativement, vous pouvez faire ceci:

>>> with open('foo.sysconfig') as fd:
...   settings = {var: shlex.split(value) for var, value in [line.split('=', 1) for line in fd]}

Ce qui vous donne un dictionnaire settings qui a:

>>> settings
{'FOO': ['some value']}

Cette ligne settings = {...} utilise un compréhension du dictionnaire . Vous pouvez accomplir la même chose en quelques lignes supplémentaires avec une boucle for et ainsi de suite.

Et bien sûr, si le fichier contient une extension de variable de style Shell telle que ${somevar:-value_if_not_set}, cela ne fonctionnera pas (à moins d'écrire votre propre analyseur de variable de style Shell).

3
larsks

Gardez à l'esprit que si vous avez un fichier "texte" avec ce contenu qui porte l'extension .py, vous pouvez toujours faire:

import mytextfile

print(mytestfile.FOO)

Bien entendu, cela suppose que le fichier texte est syntaxiquement correct en ce qui concerne Python. Sur un projet sur lequel j'ai travaillé, nous avons fait quelque chose de similaire. A transformé certains fichiers texte en fichiers Python. Wacky mais peut-être mérite d'être considéré.

2
Harlin

Il y a plusieurs façons de faire ce genre de chose.

  • Vous pouvez en effet import le fichier en tant que module, à condition que les données qu'il contient correspondent à la syntaxe de python. Mais le fichier en question est un .py dans le même répertoire que votre script, vous devez utiliser imp (ou importlib, selon votre version) comme ici .

  • Une autre solution (à ma préférence) peut consister à utiliser un format de données que toute bibliothèque python peut analyser (JSON me vient à l’esprit comme exemple).

/ etc/default/foo:

{"FOO":"path/to/foo"}

Et dans votre code python:

import json

with open('/etc/default/foo') as file:
    data = json.load(file)
    FOO = data["FOO"]
    ## ...
    file.close()

De cette façon, vous ne risquez pas d'exécuter du code incertain ...

Vous avez le choix, selon ce que vous préférez. Si votre script génère automatiquement un fichier de données, il serait peut-être plus simple de conserver une syntaxe simple, telle que FOO="path/to/foo", et d'utiliser imp.

J'espère que ça aide!

1
Ascam

Même réponse que @jil cependant, cette réponse est spécifique à une version historique de Python.

En Python moderne (3.0+):

exec()

remplace execfile()

1
Hack-R

La solution

Voici mon approche: analyser moi-même le fichier bash et ne traiter que les lignes d’attribution de variables telles que:

FOO="/path/to/foo"

Voici le code:

import shlex

def parse_Shell_var(line):
    """
    Parse such lines as:
        FOO="My variable foo"

    :return: a Tuple of var name and var value, such as
        ('FOO', 'My variable foo')
    """
    return shlex.split(line, posix=True)[0].split('=', 1)

if __== '__main__':
    with open('Shell_vars.sh') as f:
        Shell_vars = dict(parse_Shell_var(line) for line in f if '=' in line)
    print(Shell_vars)

Comment ça marche

Jetez un coup d'œil à cet extrait:

        Shell_vars = dict(parse_Shell_var(line) for line in f if '=' in line)

Cette ligne parcourt les lignes du script Shell et ne traite que les lignes qui ont le signe égal (pas une manière infaillible de détecter une affectation de variable, mais la plus simple). Ensuite, exécutez ces lignes dans la fonction parse_Shell_var qui utilise shlex.split pour gérer correctement les guillemets (ou leur absence). Enfin, les pièces sont assemblées dans un dictionnaire. La sortie de ce script est:

{'MOO': '/dont/have/a/cow', 'FOO': 'my variable foo', 'BAR': 'My variable bar'}

Voici le contenu de Shell_vars.sh :

FOO='my variable foo'
BAR="My variable bar"
MOO=/dont/have/a/cow
echo $FOO

Discussion

Cette approche présente quelques avantages:

  • Il n’exécute pas le shell (ni en bash ni en python), ce qui évite les effets secondaires
  • Par conséquent, son utilisation est sûre, même si le script Origin of the Shell est inconnu.
  • Il gère correctement les valeurs avec ou sans guillemets

Cette approche n’est pas parfaite, elle a quelques limitations:

  • La méthode de détection de l'affectation de variable (en recherchant la présence du signe égal) est primitive et non précise. Il existe des moyens de mieux détecter ces lignes, mais c'est le sujet d'un autre jour.
  • Il n'analyse pas correctement les valeurs qui reposent sur d'autres variables ou commandes. Cela signifie que les lignes telles que:

    FOO=$BAR
    FOO=$(pwd)
    
0
Hai Vu