J'essaie de comprendre quelle est la motivation derrière l'utilisation des fonctions de bibliothèque de Python pour exécuter des tâches spécifiques au système d'exploitation telles que la création de fichiers / répertoires, la modification des attributs de fichier, 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 faire 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 de simplement 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 ici que d'exécuter de simples commandes shell sur 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 subprocess
module a plus de sens, par exemple.
la source
print
-vous puos.system("echo Hello world!")
?os.path
pour gérer les chemins au lieu de les gérer manuellement: cela fonctionne sur tous les systèmes d'exploitation sur lesquels il s'exécute.os.chmod
ne va pas appeler lechmod
programme que le shell ferait. L' utilisationos.system('chmod ...')
lance un shell pour interpréter une chaîne pour appeler un autre exécutable pour faire un appel à la Cchmod
fonction, toutos.chmod(...)
va beaucoup plus directement au Cchmod
.Réponses:
Il est plus rapide ,
os.system
et desubprocess.call
créer de nouveaux processus qui est inutile pour quelque chose aussi simple. En fait,os.system
etsubprocess.call
avec l'shell
argument, créez généralement au moins deux nouveaux processus: le premier étant le shell, et le second étant la commande que vous exécutez (si ce n'est pas un shell intégré commetest
).Certaines commandes sont inutiles dans un processus séparé . Par exemple, si vous exécutez
os.spawn("cd dir/")
, cela changera le répertoire de travail actuel du processus enfant, mais pas du processus Python. Vous devez utiliseros.chdir
pour cela.Vous n'avez pas à vous soucier des caractères spéciaux interprétés par le shell.
os.chmod(path, mode)
fonctionnera quel que soit le nom de fichier, alors queos.spawn("chmod 777 " + path)
cela échouera horriblement si le nom de fichier est quelque chose comme; rm -rf ~
. (Notez que vous pouvez contourner ce problème si vous utilisezsubprocess.call
sans l'shell
argument.)Vous n'avez pas à vous soucier des noms de fichiers commençant par un tiret .
os.chmod("--quiet", mode)
changera les permissions du fichier nommé--quiet
, maisos.spawn("chmod 777 --quiet")
échouera, comme cela--quiet
est interprété comme un argument. Cela est vrai même poursubprocess.call(["chmod", "777", "--quiet"])
.Vous avez moins de problèmes multi-plateformes et inter-shell, car la bibliothèque standard de Python est censée gérer cela pour vous. Votre système a-t-il la
chmod
commande? Est-il installé? Prend-il en charge les paramètres que vous attendez de lui? Leos
module essaiera d'être aussi multiplateforme que possible et documentera lorsque ce n'est pas possible.Si la commande que vous exécutez a une sortie qui vous tient à cœur, vous devez l'analyser, ce qui est plus délicat qu'il n'y paraît, car vous risquez d'oublier les cas d'angle (noms de fichiers contenant des espaces, des tabulations et des retours à la ligne), même lorsque vous ne vous souciez pas de la portabilité.
la source
ls
oudir
est assez élevé pour certains types de développeurs, tout commebash
oucmd
ouksh
ou quel que soit le shell que vous préférez.ls
c'est un niveau plus élevé que cela, car ce n'est pas un appel direct à l'API du noyau de votre système d'exploitation. C'est une (petite) application.ls
oudir
maisopendir()/readdir()
(api linux) ouFindFirstFile()/FindNextFile()
(api windows) ouFile.listFiles
(API java) ouDirectory.GetFiles()
(C #). Tous ces éléments sont étroitement liés à un appel direct au système d'exploitation. Certains peuvent être aussi simples que de pousser un nombre dans un registre et d'appelerint 13h
pour déclencher le mode noyau.C'est plus sûr. Pour vous donner une idée voici un exemple de script
Si l'entrée de l'utilisateur était,
test; rm -rf ~
cela supprimerait le répertoire de base.C'est pourquoi il est plus sûr d'utiliser la fonction intégrée.
Par conséquent, vous devriez également utiliser le sous-processus au lieu du système.
la source
Il existe quatre cas forts pour préférer les méthodes plus spécifiques de Python dans le
os
module à l'utilisation deos.system
ou dusubprocess
module lors de l'exécution d'une commande:os
module sont disponibles sur plusieurs plates-formes tandis que de nombreuses commandes shell sont spécifiques à l'OS.os
module.Redondance (voir code redondant ):
Vous exécutez en fait 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
:Et
subprocess
c'est juste un module pour engendrer de nouveaux processus.Vous pouvez faire ce dont vous avez besoin sans engendrer ces processus.
Portabilité (voir portabilité du code source ):
L'
os
objectif du module est de fournir des services génériques de système d'exploitation et sa description commence par:Vous pouvez utiliser
os.listdir
à la fois Windows et Unix. Essayer d'utiliseros.system
/subprocess
pour cette fonctionnalité vous obligera à maintenir deux appels (pourls
/dir
) et à vérifier sur quel système d'exploitation vous êtes. Ce n'est pas aussi portable et va causer encore plus de frustration plus tard (voir Gestion de sortie ).Comprendre les résultats de la commande:
Supposons que vous souhaitiez lister 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 essentiellement une grande chaîne avec les noms de fichiers.Comment pouvez-vous distinguer un fichier avec un espace dans son nom de deux fichiers?
Que faire si vous n'êtes pas autorisé à répertorier les fichiers?
Comment devez-vous mapper les données sur des objets python?
Ce ne sont que de ma tête, 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 suivi du principe Ne vous répétez pas (souvent appelé «DRY») en ne répétant pas une implémentation qui existe déjà et qui est librement disponible pour vous.
Sécurité:
os.system
etsubprocess
sont puissants. C'est bien quand vous avez besoin de ce pouvoir, mais c'est dangereux quand vous n'en avez pas. Lorsque vous utilisezos.listdir
, vous savez qu'il ne peut rien faire d'autre que répertorier les fichiers ou générer une erreur. Lorsque vous utilisezos.system
ousubprocess
pour obtenir le même comportement, vous pouvez éventuellement finir par faire quelque chose que vous n'aviez pas l'intention de faire.Sécurité d'injection (voir exemples d'injection de coque ) :
Si vous utilisez l'entrée de l'utilisateur comme nouvelle commande, vous lui avez essentiellement donné un shell. C'est un peu comme l'injection SQL fournissant un shell dans la base de données pour l'utilisateur.
Un exemple serait une commande de la forme:
Cela peut être facilement exploité pour exécuter n'importe quel code arbitraire en utilisant l'entrée:
NASTY COMMAND;#
pour créer l'éventuel:Il existe de nombreuses commandes de ce type qui peuvent mettre votre système en danger.
la source
Pour une raison simple - lorsque vous appelez une fonction shell, cela crée un sous-shell qui est détruit après que votre commande existe, donc si vous changez de répertoire dans un shell - cela n'affecte pas votre environnement en Python.
De plus, la création d'un sous-shell prend du temps, donc l'utilisation directe des commandes du système d'exploitation aura un impact sur vos performances
ÉDITER
J'ai eu quelques tests de chronométrage en cours:
La fonction interne s'exécute plus de 10 fois plus vite
EDIT2
Il peut y avoir des cas où l'invocation d'un exécutable externe peut donner de meilleurs résultats que les packages Python - je viens de me souvenir d'un courrier envoyé par un de mes collègues selon lequel les performances de gzip appelées via un sous-processus étaient bien supérieures à celles d'un package Python qu'il utilisait. Mais certainement pas quand nous parlons de packages OS standard émulant des commandes OS standard
la source
%
utiliser l'interpréteur normal.time <path to script>
terminal et il vous indiquera le temps réel, utilisateur et processus pris. C'est si vous n'avez pas iPython et que vous avez accès à la ligne de commande Unix.Les appels du shell sont spécifiques au système d'exploitation alors que les fonctions du module Python os ne le sont pas, dans la plupart des cas. Et cela évite de générer un sous-processus.
la source
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 engager la surcharge de création de l'ensemble du processus shell uniquement pour cet appel système unique?
La situation est encore pire lorsque vous utilisez
os.system
pour quelque chose qui n'est pas un shell intégré. Vous démarrez un processus shell qui à son tour démarre un exécutable qui ensuite (à deux processus) effectue l'appel système. Au moinssubprocess
aurait éliminé le besoin d'un processus d'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 fait que le système nécessaire s'appelle lui-même au lieu de générer un millier de shells.la source