Comment implémenter des idiomes bash communs en Python? [fermé]

242

Je fais actuellement ma manipulation de fichiers texte à travers un tas d'AWK, sed, Bash et un tout petit peu de Perl.

J'ai vu mentionner à quelques endroits que le python est bon pour ce genre de chose. Comment puis-je utiliser Python pour remplacer les scripts shell, AWK, sed et amis?

Chris Jefferson
la source
3
pythonpy est un bon concurrent pour awk et sed utilisant la syntaxe python: github.com/Russell91/pythonpy
RussellStewart
4
vous pouvez utiliser shellpy qui a été conçu avec une idée en tête pour remplacer bash / sh par python github.com/lamerman/shellpy
Alexander Ponomarev
C'est ma question, je ne comprends pas pourquoi elle est basée sur l'opinion. La première réponse répertorie chacune des principales fonctions d'un shell et vous indique comment les effectuer en python. À mon avis, cela répond à la question sans avis.
Chris Jefferson
Cette question, et sa fermeture, sont en cours de discussion sur meta ici
Erik A

Réponses:

144

Tout shell a plusieurs ensembles de fonctionnalités.

  • Les commandes Essential Linux / Unix. Tous ces éléments sont disponibles via la bibliothèque de sous - processus . Ce n'est pas toujours le meilleur premier choix pour exécuter toutes les commandes externes. Regardez également shutil pour certaines commandes qui sont des commandes Linux distinctes, mais vous pourriez probablement implémenter directement dans vos scripts Python. Un autre lot énorme de commandes Linux se trouve dans la bibliothèque os ; vous pouvez le faire plus simplement en Python.

    Et - bonus! -- plus vite. Chaque commande Linux distincte dans le shell (à quelques exceptions près) crée un sous-processus. En utilisant Python shutilet des osmodules, vous ne créez pas de sous-processus.

  • Les fonctionnalités de l'environnement shell. Cela inclut des éléments qui définissent l'environnement d'une commande (répertoire actuel et variables d'environnement et autres). Vous pouvez facilement gérer cela directement depuis Python.

  • Les fonctionnalités de programmation shell. Il s'agit de la vérification du code d'état du processus, des diverses commandes logiques (si, pendant, pour, etc.) de la commande de test et de toutes ses apparentées. Le truc de définition de fonction. Tout cela est beaucoup, beaucoup plus facile en Python. C'est l'une des énormes victoires en se débarrassant de bash et en le faisant en Python.

  • Fonctions d'interaction. Cela inclut l'historique des commandes et ce qui ne l'est pas. Vous n'en avez pas besoin pour écrire des scripts shell. C'est uniquement pour l'interaction humaine, et non pour l'écriture de scripts.

  • Les fonctionnalités de gestion des fichiers shell. Cela inclut la redirection et les pipelines. C'est plus compliqué. Une grande partie de cela peut être effectuée avec un sous-processus. Mais certaines choses faciles à utiliser sont désagréables en Python. Plus précisément des trucs comme (a | b; c ) | something >result. Cela exécute deux processus en parallèle (avec sortie de acomme entrée pour b), suivi d'un troisième processus. La sortie de cette séquence est exécutée en parallèle avec somethinget la sortie est collectée dans un fichier nommé result. C'est juste complexe à exprimer dans une autre langue.

Des programmes spécifiques (awk, sed, grep, etc.) peuvent souvent être réécrits sous forme de modules Python. N'allez pas trop loin. Remplacez ce dont vous avez besoin et faites évoluer votre module "grep". Ne commencez pas à écrire un module Python qui remplace "grep".

La meilleure chose est que vous pouvez le faire par étapes.

  1. Remplacez AWK et PERL par Python. Laissez tout le reste tranquille.
  2. Essayez de remplacer GREP par Python. Cela peut être un peu plus complexe, mais votre version de GREP peut être adaptée à vos besoins de traitement.
  3. Essayez de remplacer FIND par des boucles Python qui utilisent os.walk. C'est une grande victoire car vous ne générez pas autant de processus.
  4. Essayez de remplacer la logique shell commune (boucles, décisions, etc.) par des scripts Python.
S.Lott
la source
6
a écrit: "Fonctionnalités d'interaction. Cela inclut l'historique des commandes et tout ce qui n'est pas. Je crains que personne ne puisse dire ce dont une personne a vraiment besoin ou non. Peut-être qu'il le fait. En outre, ces fonctionnalités ont beaucoup de sens dans un shell interactif, en prenant comme exemple la différence entre Idle et IPython.
heltonbiker
47
Je souhaite sincèrement que les gens abandonnent complètement les scripts shell. Je comprends que le piratage est pratiquement une religion dans le monde * nix mais je suis vraiment fatigué d'essayer d'interpréter toutes les solutions de contournement hackers implantées dans le système d'exploitation. La nouveauté des microtools (awk, sed, top, base, etc.) s'estompe le jour où tout le monde a décidé de lancer sa propre version. Je grince des dents quand j'imagine la quantité d'heures-homme gaspillées sur de petits outils de merde qui pourraient facilement être remplacés par quelques modules Python bien conçus. :: soupir ::
Evan Plaice
40
Je ne suis pas d'accord avec @EvanPlaice car la version python de plusieurs findscripts que j'ai est moche et longue et impossible à maintenir en comparaison. Beaucoup de choses devraient être des scripts shell, beaucoup d'autres non . Tout ne doit pas nécessairement être un seul de Python ou BASH (ou autre chose).
mikebabcock
8
@mikebabcock Idéalement, il devrait y avoir une bibliothèque complète qui implémente tous les micro-outils mis à disposition par la pile de base * nix. Des fonctions comme find () et last () seraient incluses et au lieu des tuyaux, une combinaison de curry et de chargement paresseux gérerait le collage de tous ensemble. Ne serait-il pas agréable d'avoir un environnement de script POSIX qui fonctionne de manière standard dans toutes les distributions? Rien de tel n'existe encore ...
Evan Plaice
2
Le point sur les pipelines shell (tels que (a | b; c ) | something >result) est quelque peu atténué par le fait qu'il est trivialement facile de passer des pipelines shell à des subprocessméthodes utilisantshell=True
iruvar
103

Oui bien sûr :)

Jetez un œil à ces bibliothèques qui vous aident à ne plus jamais écrire de scripts shell (la devise de Plumbum).

De plus, si vous souhaitez remplacer awk, sed et grep par quelque chose basé sur Python, je recommande pyp -

"The Pyed Piper", ou pyp, est un outil de manipulation de texte en ligne de commande Linux similaire à awk ou sed, mais qui utilise des méthodes de chaîne et de liste en python standard ainsi que des fonctions personnalisées évoluées pour générer des résultats rapides dans un environnement de production intense.

Piotr Dobrogost
la source
Jetez également un œil à Envoy, qui est une alternative à sh github.com/kennethreitz/envoy
AllanLRH
57

Je viens de découvrir comment combiner les meilleures parties de bash et ipython. Jusqu'à présent, cela me semble plus confortable que d'utiliser un sous-processus, etc. Vous pouvez facilement copier de grandes parties de scripts bash existants et par exemple ajouter la gestion des erreurs de la manière python :) Et voici mon résultat:

#!/usr/bin/env ipython3

# *** How to have the most comfort scripting experience of your life ***
# ######################################################################
#
# … by using ipython for scripting combined with subcommands from bash!
#
# 1. echo "#!/usr/bin/env ipython3" > scriptname.ipy    # creates new ipy-file
#
# 2. chmod +x scriptname.ipy                            # make in executable
#
# 3. starting with line 2, write normal python or do some of
#    the ! magic of ipython, so that you can use unix commands
#    within python and even assign their output to a variable via
#    var = !cmd1 | cmd2 | cmd3                          # enjoy ;)
#
# 4. run via ./scriptname.ipy - if it fails with recognizing % and !
#    but parses raw python fine, please check again for the .ipy suffix

# ugly example, please go and find more in the wild
files = !ls *.* | grep "y"
for file in files:
  !echo $file | grep "p"
# sorry for this nonsense example ;)

Voir les documents IPython sur les commandes du shell système et son utilisation comme shell système .

Le K
la source
11
A voté parce que pour une raison bizarre, personne d'autre n'a mentionné! -Les commandes dans IPython, qui sont absolument essentielles; d'autant plus que vous pouvez également affecter leur sortie à une variable (liste de lignes) comme dansfilelines = ! cat myfile
kampu
Et vous pouvez utiliser des variables python comme $vardans une commande shell? Sensationnel. Ce devrait être la réponse acceptée.
Chiel ten Brinke
Et vous pouvez également l'utiliser à partir des carnets jupyter
Yuval Atzmon
44

Depuis 2015 et la sortie de Python 3.4, il existe désormais un shell interactif raisonnablement complet disponible sur: http://xon.sh/ ou https://github.com/scopatz/xonsh

La vidéo de démonstration ne montre pas les canaux utilisés, mais ils SONT pris en charge en mode shell par défaut.

Xonsh ('conch') essaie très fort d'émuler bash, donc des choses pour lesquelles vous avez déjà gagné de la mémoire musculaire, comme

env | uniq | sort -r | grep PATH

ou

my-web-server 2>&1 | my-log-sorter

fonctionnera toujours bien.

Le didacticiel est assez long et semble couvrir une quantité importante des fonctionnalités que quelqu'un attendrait généralement à une invite ash ou bash:

  • Compile, évalue et exécute!
  • Historique des commandes et complétion des onglets
  • Aide & Superhelp avec ?&??
  • Alias ​​et invites personnalisées
  • Exécute des commandes et / ou des *.xshscripts qui peuvent également être importés
  • Variables d'environnement, y compris la recherche avec ${}
  • Redirection et combinaison des entrées / sorties
  • Emplois d'arrière-plan et contrôle des travaux
  • Imbrication de sous-processus, tuyaux et coprocessus
  • Mode sous-processus lorsqu'une commande existe, mode Python sinon
  • Sous-processus capturé avec $(), Sous-processus non capturé avec $[], Évaluation Python avec@()
  • Globbing de nom de fichier avec *ou Expression régulière Globbing de nom de fichier avec backticks
Kamilion
la source
Mais pourquoi semble-t-il que toutes ces réponses ne font que réinventer la roue pour les personnes qui ne connaissent pas bash ? Je suis devenu assez à l'aise avec bash et chacune de ces réponses semble que cela finira par être plus de travail pour peu d'avantages. Ces réponses s'adressent toutes aux personnes python qui ont peur (ou ne veulent pas passer du temps à apprendre) bash, ai-je raison?
Buttle Butkus
Semble avoir des inconvénients comme l'obligation d'utiliser l' .xshextension pour les fichiers avec le code xonsh : github.com/xonsh/xonsh/issues/2478 . Sinon, vous devez utiliser evalxpour l'appeler directement à partir des .pyfichiers.
Andry
31
  • Si vous souhaitez utiliser Python comme shell, pourquoi ne pas jeter un œil à IPython ? Il est également bon d'apprendre la langue de manière interactive.
  • Si vous faites beaucoup de manipulation de texte et si vous utilisez Vim comme éditeur de texte, vous pouvez également écrire directement des plugins pour Vim en python. tapez simplement ": help python" dans Vim et suivez les instructions ou consultez cette présentation . Il est si facile et puissant d'écrire des fonctions que vous utiliserez directement dans votre éditeur!
Mapad
la source
8
il y a un profil ipython appelé « sh » qui fait l'interprète très bien comme une coquille.
Autoplectic le
3
Le profil ip shon 'sh' a été supprimé depuis un certain temps maintenant.
gdw2
>>> result =! dmesg | grep -i 'usb' #the! opérateur fait tout
Permafacture
16

Au début, il y avait sh, sed et awk (et find, grep et ...). C'était bon. Mais awk peut être une petite bête étrange et difficile à retenir si vous ne l'utilisez pas souvent. Puis le grand chameau a créé Perl. Perl était le rêve d'un administrateur système. C'était comme un script shell sur les stéroïdes. Le traitement de texte, y compris les expressions régulières, n'était qu'une partie de la langue. Puis c'est devenu moche ... Les gens ont essayé de faire de grosses applications avec Perl. Maintenant, ne vous méprenez pas, Perl peut être une application, mais cela peut (peut!) Ressembler à un gâchis si vous n'êtes pas vraiment prudent. Ensuite, il y a toutes ces affaires de données plates. Il suffit de conduire un programmeur fou.

Entrez Python, Ruby et al. Ce sont vraiment de très bons langages à usage général. Ils prennent en charge le traitement de texte et le font bien (mais peut-être pas aussi étroitement liés au noyau de base de la langue). Mais ils évoluent également très bien et ont toujours un code agréable à la fin de la journée. Ils ont également développé des communautés assez lourdes avec beaucoup de bibliothèques pour presque tout.

Maintenant, une grande partie de la négativité envers Perl est une question d'opinion, et certaines personnes peuvent certainement écrire du Perl très propre, mais avec autant de gens se plaignant qu'il soit trop facile de créer du code obscurci, vous savez qu'un certain grain de vérité est là. La question devient alors vraiment, allez-vous jamais utiliser ce langage pour plus que de simples remplacements de scripts bash. Sinon, apprenez un peu plus de Perl .. c'est absolument fantastique pour ça. Si, en revanche, vous voulez un langage qui évoluera avec vous au fur et à mesure que vous en ferez plus, je vous conseille Python ou Ruby.

Quoi qu'il en soit, bonne chance!

MattG
la source
9

Je suggère le livre en ligne génial Dive Into Python . C'est comme ça que j'ai appris la langue à l'origine.

En plus de vous enseigner la structure de base du langage et de nombreuses structures de données utiles, il contient un bon chapitre sur la gestion des fichiers et des chapitres suivants sur les expressions régulières et plus encore.

Dan Lenski
la source
1
... problème principal des réponses en lien uniquement.
Jean-François Fabre
7

Ajout aux réponses précédentes: vérifiez le module pexpect pour gérer les commandes interactives (adduser, passwd etc.)

Federico A. Ramponi
la source
7

Une des raisons pour lesquelles j'aime Python est qu'il est bien mieux standardisé que les outils POSIX. Je dois vérifier deux fois et trois fois que chaque bit est compatible avec d'autres systèmes d'exploitation. Un programme écrit sur un système Linux peut ne pas fonctionner de la même manière sur un système BSD d'OSX. Avec Python, je dois juste vérifier que le système cible a une version suffisamment moderne de Python.

Encore mieux, un programme écrit en Python standard fonctionnera même sur Windows!

Hal Canary
la source
1
"un programme écrit en Python standard fonctionnera même sur Windows": pas de blague?
Jean-François Fabre
6

Je vais donner ici mon avis basé sur l'expérience:

Pour la coque:

  • shell peut très facilement générer du code en lecture seule. Écrivez-le et quand vous y reviendrez, vous ne comprendrez plus jamais ce que vous avez fait. C'est très facile à réaliser.
  • shell peut faire BEAUCOUP de traitement de texte, de fractionnement, etc. en une seule ligne avec des tuyaux.
  • c'est le meilleur langage de collage lorsqu'il s'agit d'intégrer l'appel de programmes dans différents langages de programmation.

Pour python:

  • si vous voulez que la portabilité vers les fenêtres soit incluse, utilisez python.
  • python peut être meilleur lorsque vous devez manipuler un peu plus que du texte, comme des collections de nombres. Pour cela, je recommande python.

Je choisis généralement bash pour la plupart des choses, mais lorsque j'ai quelque chose qui doit traverser les limites de Windows, j'utilise simplement python.

Germán Diago
la source
4

pythonpy est un outil qui permet d'accéder facilement à de nombreuses fonctionnalités de awk et sed, mais en utilisant la syntaxe python:

$ echo me2 | py -x 're.sub("me", "you", x)'
you2
RussellStewart
la source
3

J'ai construit des scripts shell semi-longs (300-500 lignes) et du code Python qui fait des fonctionnalités similaires. Lorsque de nombreuses commandes externes sont exécutées, je trouve que le shell est plus facile à utiliser. Perl est également une bonne option lorsqu'il y a beaucoup de manipulation de texte.

Mike Davis
la source
3

En recherchant ce sujet, j'ai trouvé ce code de preuve de concept (via un commentaire sur http://jlebar.com/2010/2/1/Replacing_Bash.html ) qui vous permet «d'écrire des pipelines de type shell en Python en utilisant un syntaxe laconique et tirer parti des outils système existants là où ils ont du sens ":

for line in sh("cat /tmp/junk2") | cut(d=',',f=1) | 'sort' | uniq:
    sys.stdout.write(line)
Nickolay
la source
2

Votre meilleur pari est un outil spécifiquement adapté à votre problème. S'il traite des fichiers texte, Sed, Awk et Perl sont les principaux concurrents. Python est un langage dynamique à usage général . Comme pour tout langage à usage général, la manipulation de fichiers est prise en charge, mais ce n'est pas son objectif principal. Je considérerais Python ou Ruby si j'avais besoin d'un langage dynamique en particulier.

En bref, apprenez très bien Sed et Awk, ainsi que tous les autres goodies qui viennent avec votre saveur de * nix (Tous les Bash intégrés, grep, tr et ainsi de suite). Si c'est le traitement de fichiers texte qui vous intéresse, vous utilisez déjà les bonnes choses.

Eric Smith
la source
2

Vous pouvez utiliser python au lieu de bash avec la bibliothèque ShellPy .

Voici un exemple qui télécharge l'avatar d'un utilisateur Python depuis Github:

import json
import os
import tempfile

# get the api answer with curl
answer = `curl https://api.github.com/users/python
# syntactic sugar for checking returncode of executed process for zero
if answer:
    answer_json = json.loads(answer.stdout)
    avatar_url = answer_json['avatar_url']

    destination = os.path.join(tempfile.gettempdir(), 'python.png')

    # execute curl once again, this time to get the image
    result = `curl {avatar_url} > {destination}
    if result:
        # if there were no problems show the file
        p`ls -l {destination}
    else:
        print('Failed to download avatar')

    print('Avatar downloaded')
else:
    print('Failed to access github api')

Comme vous pouvez le voir, toutes les expressions à l'intérieur du symbole d'accent grave (`) sont exécutées en shell. Et dans le code Python, vous pouvez capturer les résultats de cette exécution et effectuer des actions dessus. Par exemple:

log = `git log --pretty=oneline --grep='Create'

Cette ligne s'exécutera d'abord git log --pretty=oneline --grep='Create'dans le shell, puis affectera le résultat à la variable de journal. Le résultat a les propriétés suivantes:

stdout tout le texte de stdout du processus exécuté

stderr tout le texte de stderr du processus exécuté

code retour code retour de l'exécution

Ceci est un aperçu général de la bibliothèque, une description plus détaillée avec des exemples peut être trouvée ici .

Alexander Ponomarev
la source
1

Si votre manipulation de fichier texte est généralement ponctuelle, éventuellement effectuée à l'invite du shell, vous n'obtiendrez rien de mieux avec python.

D'un autre côté, si vous devez généralement faire la même tâche (ou similaire) encore et encore, et que vous devez écrire vos scripts pour le faire, alors python est génial - et vous pouvez facilement créer vos propres bibliothèques (vous pouvez le faire cela avec les scripts shell aussi, mais c'est plus lourd).

Un exemple très simple pour ressentir.

import popen2
stdout_text, stdin_text=popen2.popen2("your-shell-command-here")
for line in stdout_text:
  if line.startswith("#"):
    pass
  else
    jobID=int(line.split(",")[0].split()[1].lstrip("<").rstrip(">"))
    # do something with jobID

Vérifiez également le module sys et getopt, ce sont les premiers dont vous aurez besoin.

Davide
la source
1

J'ai publié un package sur PyPI: ez .
Utilisez pip install ezpour l'installer.

Il a emballé des commandes communes dans shell et joliment ma lib utilise fondamentalement la même syntaxe que shell. par exemple, cp (source, destination) peut gérer à la fois le fichier et le dossier! (wrapper de shutil.copy shutil.copytree et il décide quand utiliser lequel). Encore plus joliment, il peut supporter la vectorisation comme R!

Un autre exemple: pas d'os.walk, utilisez fls (path, regex) pour trouver récursivement des fichiers et filtrer avec une expression régulière et il retourne une liste de fichiers avec ou sans fullpath

Dernier exemple: vous pouvez les combiner pour écrire des scripts très simplement:
files = fls('.','py$'); cp(files, myDir)

Vérifiez-le! Cela m'a coûté des centaines d'heures pour l'écrire / l'améliorer!

Jerry T
la source
1
Semble intéressant, mais je ne peux pas percer les documents non formatés sur pypi.python.org/pypi/ez , désolé ...
Greg Dubicki