web-dev-qa-db-fra.com

Pourquoi utiliser les méthodes de module os de Python au lieu d'exécuter directement les commandes Shell?

J'essaie de comprendre quelle est la motivation qui pousse les fonctions de bibliothèque de Python à exécuter des tâches spécifiques à un système d'exploitation, telles que la création de fichiers/répertoires, la modification d'attributs de fichiers, etc. au lieu de simplement exécuter ces commandes via os.system() ou subprocess.call()?

Par exemple, pourquoi voudrais-je utiliser os.chmod Au lieu de os.system("chmod...")?

Je comprends qu’il est plus "pythonique" d’utiliser autant que possible les méthodes de bibliothèque disponibles de Python au lieu d’exécuter directement des commandes Shell. Mais y a-t-il une autre motivation derrière cela du point de vue de la fonctionnalité?

Je ne parle que d’exécuter ici des commandes Shell simples d’une ligne. Lorsque nous avons besoin de plus de contrôle sur l'exécution de la tâche, je comprends que l'utilisation du module subprocess a plus de sens, par exemple.

156
Koderok
  1. C'est plus rapide, os.system Et subprocess.call Créent de nouveaux processus inutiles pour quelque chose d'aussi simple. En fait, os.system Et subprocess.call Avec l'argument Shell créent généralement au moins deux nouveaux processus: le premier étant le shell et le second la commande que vous ' re en cours d’exécution (si ce n’est pas un shell intégré comme test).

  2. Certaines commandes sont inutiles dans un processus séparé. Par exemple, si vous exécutez os.spawn("cd dir/"), le répertoire de travail actuel du processus enfant sera modifié, mais pas le processus Python. Vous devez utiliser os.chdir pour ça.

  3. Vous n'avez pas à vous soucier de spécial caractères interprétés par le shell. os.chmod(path, mode) fonctionnera quel que soit le nom du fichier, alors que os.spawn("chmod 777 " + path) échouera horriblement si le nom du fichier ressemble à ; rm -rf ~. (Notez que vous pouvez contourner ce problème si vous utilisez subprocess.call Sans l'argument Shell.)

  4. Vous n'avez pas à vous soucier de les noms de fichiers commençant par un tiret. os.chmod("--quiet", mode) modifiera les autorisations du fichier nommé --quiet, mais os.spawn("chmod 777 --quiet") échouera, car --quiet est interprété comme un argument. Ceci est vrai même pour subprocess.call(["chmod", "777", "--quiet"]).

  5. Vous avez moins d'inquiétudes multiplate-forme et inter-shell, car la bibliothèque standard de Python est censée s'occuper de cela pour vous. Votre système a-t-il la commande chmod? Est-il installé? Prend-il en charge les paramètres que vous attendez? Le module os essaiera d’être aussi multi-plateforme que possible et de documenter lorsque ce n’est pas possible.

  6. Si la commande que vous exécutez a sortie qui vous intéressent, vous devez l'analyser, ce qui est plus compliqué qu'il n'y paraît, car vous risqueriez d'oublier les coins-cas (noms de fichiers avec des espaces, des tabulations et des nouvelles lignes). en eux), même lorsque vous ne vous souciez pas de la portabilité.

325
Flimm

C'est plus sûr. Pour vous donner une idée, voici un exemple de script

import os
file = raw_input("Please enter a file: ")
os.system("chmod 777 " + file)

Si l'entrée de l'utilisateur était test; rm -rf ~ ceci effacerait alors le répertoire de base.

C'est pourquoi il est plus sûr d'utiliser la fonction intégrée.

Par conséquent, pourquoi vous devriez également utiliser un sous-processus plutôt que un système.

133
iProgram

Il y a quatre cas forts pour préférer les méthodes plus spécifiques de Python dans le module os à l'aide de os.system ou du subprocess module lors de l'exécution d'une commande:

  • Redondance - La création d'un autre processus est redondante et entraîne une perte de temps et de ressources.
  • Portabilité - La plupart des méthodes du module os sont disponibles sur plusieurs plates-formes, tandis que de nombreuses commandes du shell sont spécifiques au système d'exploitation.
  • Compréhension des résultats - Créer un processus pour exécuter des commandes arbitraires vous oblige à analyser les résultats de la sortie et à comprendre si et pourquoi une commande a mal agi.
  • Sécurité - Un processus peut potentiellement exécuter n'importe quelle commande qui lui est donnée. Cette conception est faible et peut être évitée en utilisant des méthodes spécifiques dans le module os.

Redondance (voir code redondant ):

Vous êtes en train d'exécuter un "intermédiaire" redondant sur votre chemin vers les éventuels appels système (chmod dans votre exemple). Cet intermédiaire est un nouveau processus ou sous-shell.

De os.system :

Exécutez la commande (une chaîne) dans un sous-shell ...

Et subprocess est juste un module pour générer de nouveaux processus.

Vous pouvez faire ce dont vous avez besoin sans engendrer ces processus.

Portabilité (voir portabilité du code source ):

Le module os a pour but de fournir des services génériques au système d'exploitation. Sa description commence par:

Ce module fournit un moyen portable d’utiliser les fonctionnalités dépendantes du système d’exploitation.

Vous pouvez utiliser os.listdir sur Windows et Unix. Essayer d'utiliser os.system/subprocess pour cette fonctionnalité vous obligera à maintenir deux appels (pour ls/dir) et à vérifier le système d'exploitation utilisé. sur. Ce n’est pas aussi portable et causera encore plus de frustration plus tard (voir Traitement de la sortie ).

Comprendre les résultats de la commande:

Supposons que vous souhaitiez répertorier les fichiers dans un répertoire.

Si vous utilisez os.system("ls")/subprocess.call(['ls']), vous ne pouvez récupérer que la sortie du processus, qui est en gros une chaîne contenant les noms de fichiers.

Comment pouvez-vous distinguer un fichier avec un espace dans son nom à partir de deux fichiers?

Que faire si vous n'avez pas la permission de lister les fichiers?

Comment mapper les données sur des objets python?

Celles-ci ne sont là que par hasard et, bien qu’il existe des solutions à ces problèmes, pourquoi résoudre à nouveau un problème qui a été résolu pour vous?

Ceci est un exemple de suivre le principe ne pas répéter (souvent désigné sous le nom de "DRY") par pas en répétant une implémentation qui existe déjà et qui est librement disponible pour vous.

Sécurité:

os.system Et subprocess sont puissants. C'est bien quand vous avez besoin de ce pouvoir, mais c'est dangereux quand vous n'en avez pas. Lorsque vous utilisez os.listdir, Vous savez qu'il ne peut rien faire d'autre que répertorier des fichiers ou générer une erreur. Lorsque vous utilisez os.system Ou subprocess pour obtenir le même comportement, vous pouvez éventuellement finir par faire quelque chose que vous ne vouliez pas faire.

Sécurité des injections (voir Exemples d'injections de shell ) :

Si vous utilisez les entrées de l'utilisateur comme nouvelle commande, vous lui avez essentiellement attribué un shell. Cela ressemble beaucoup à une injection SQL fournissant un shell dans la base de données à l'utilisateur.

Un exemple serait une commande de la forme:

# ... read some user input
os.system(user_input + " some continutation")

Cela peut être facilement exploité afin d’exécuter n’importe quel code arbitraire en utilisant l’entrée: NASTY COMMAND;# Pour créer l’éventuel:

os.system("NASTY COMMAND; # some continuation")

De nombreuses commandes de ce type peuvent mettre votre système en danger.

60
Reut Sharabani

Pour une raison simple - lorsque vous appelez une fonction Shell, il crée un sous-shell qui est détruit après la création de votre commande. Par conséquent, si vous changez de répertoire dans un shell - cela n'affectera pas votre environnement en Python.

De plus, la création de sous-shell prend beaucoup de temps. Par conséquent, l’utilisation directe des commandes du système d’exploitation aura une incidence sur vos performances.

EDIT

J'ai eu des tests de chronométrage en cours:

In [379]: %timeit os.chmod('Documents/recipes.txt', 0755)
10000 loops, best of 3: 215 us per loop

In [380]: %timeit os.system('chmod 0755 Documents/recipes.txt')
100 loops, best of 3: 2.47 ms per loop

In [382]: %timeit call(['chmod', '0755', 'Documents/recipes.txt'])
100 loops, best of 3: 2.93 ms per loop

La fonction interne s'exécute plus de 10 fois plus vite

EDIT2

Il peut arriver que l'invocation d'un exécutable externe donne de meilleurs résultats que Python packages - Je viens de me souvenir d'un courrier électronique envoyé par un de mes collègues dont les performances étaient de gzip appelé via le sous-processus était bien supérieur aux performances d'un paquet Python qu'il avait utilisé. Mais certainement pas lorsqu'il s'agit de packages de système d'exploitation standard émulant des commandes standard

23
volcano

Les appels de shell sont spécifiques au système d'exploitation alors que Python Les fonctions du module OS ne le sont pas, dans la plupart des cas. Cela évite de générer un sous-processus.

16
JoshRomRock

C'est beaucoup plus efficace. Le "Shell" est juste un autre binaire du système d'exploitation qui contient beaucoup d'appels système. Pourquoi créer la totalité du processus Shell uniquement pour cet appel système?

La situation est encore pire lorsque vous utilisez os.system pour quelque chose qui n'est pas intégré à Shell. Vous démarrez un processus Shell qui, à son tour, lance un exécutable qui lance ensuite l'appel système (à deux processus de distance). Au moins subprocess aurait supprimé la nécessité d'un processus intermédiaire Shell.

Ce n'est pas spécifique à Python, ceci. systemd est une telle amélioration des temps de démarrage de Linux pour la même raison: il effectue lui-même les appels système nécessaires au lieu de générer un millier de shells.

11
MSalters