Kjør Bash-skript i Python: En komplett guide

Om du er en Linux-bruker, er det stor sannsynlighet for at du setter pris på kraften i skallkommandoer.

Dersom du i tillegg jobber med Python, har du kanskje allerede utforsket muligheten for å automatisere oppgaver. Dette er en effektiv måte å spare tid på. Det er også mulig å bruke bash-skript for automatisering.

Python er et svært nyttig verktøy for å skrive skript, og mange anser det som et bedre alternativ enn bash. Python-skript er også enklere å administrere, spesielt når de blir mer komplekse. Bash-skript kan raskt bli vanskelige å vedlikeholde etter hvert som de vokser i størrelse og omfang.

Men hva om du allerede har bash-skript som du ønsker å bruke i Python?

Finnes det en måte å kjøre disse bash-kommandoene og skriptene direkte fra Python?

Svaret er ja. Python har en innebygd modul som heter `subprocess`. Denne modulen gir deg muligheten til å utføre både kommandoer og skript direkte fra dine Python-programmer. La oss se nærmere på hvordan du kan bruke dette til å kjøre bash-kommandoer og skript fra Python.

Utføre Bash-kommandoer

Som nevnt, er `subprocess`-modulen nøkkelen til å utføre bash-kommandoer og skript. Modulen tilbyr ulike metoder og klasser for å oppnå dette.

Spesielt to viktige elementer fra `subprocess`-modulen er verdt å kjenne: `run` og `Popen`. Disse to hjelper deg å utføre bash-kommandoer i dine Python-skript. La oss se nærmere på hver av dem.

subprocess.run()

Metoden `subprocess.run()` tar en liste av strenger som et posisjonsargument. Denne listen er obligatorisk og inneholder selve bash-kommandoen, i tillegg til eventuelle argumenter. Det første elementet i listen er kommandonavnet, mens de resterende elementene er argumenter til kommandoen.

La oss illustrere dette med et enkelt eksempel.

import subprocess
subprocess.run(["ls"])

Skriptet ovenfor vil vise alle elementene i gjeldende arbeidsmappe der skriptet befinner seg. I dette eksemplet har vi ingen argumenter for kommandoen, kun selve kommandoen `ls`. Vi kan imidlertid legge til flere argumenter til `ls`-kommandoen, som for eksempel `-l`, `-a` eller `-la`.

Her er et eksempel med kommandoargumenter:

import subprocess
subprocess.run(["ls", "-la"])

Denne kommandoen vil vise alle filer og mapper, inkludert skjulte filer, sammen med tilhørende rettigheter. Argumentet `-la` gir ekstra informasjon og viser skjulte filer og mapper.

Det kan fort oppstå feil når man skriver kommandoer. Disse feilene kan hoper seg opp. Hvis du ønsker å fange opp disse feilene og bruke dem senere, kan du bruke nøkkelordargumentet `stderr`.

Her er et eksempel:

import subprocess
result = subprocess.run(["cat", "sample.txt"], stderr=subprocess.PIPE, text=True)
print(result.stderr)

Sørg for at du ikke har en fil med navnet `sample.txt` i din arbeidsmappe. Verdien `PIPE` for nøkkelordargumentet `stderr` gjør at feilen returneres som et objekt. Dette objektet kan du senere få tilgang til via samme navn. Nøkkelordargumentet `text` sørger for at utdataene blir returnert som en streng.

Tilsvarende kan vi fange opp resultatet av kommandoen ved å bruke nøkkelordargumentet `stdout`.

import subprocess
result = subprocess.run(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
print(result.stdout)

subprocess.run() – input

Du kan gi input til kommandoer ved hjelp av nøkkelordargumentet `input`. Inputen må gis i strengformat. I tillegg må du sette nøkkelordargumentet `text` til `True`, siden standard er at inputen forventes i form av bytes.

La oss se på et eksempel:

import subprocess
subprocess.run(["python3", "add.py"], text=True, input="2 3")

I eksemplet ovenfor forventer Python-skriptet `add.py` to tall som input. Vi har gitt denne inputen til Python-skriptet ved å bruke nøkkelordargumentet `input`.

subprocess.Popen()

Klassen `subprocess.Popen()` er mer avansert enn metoden `subprocess.run()`. Den tilbyr flere muligheter for å utføre kommandoer. For å bruke `Popen()` må vi først opprette en instans av klassen, og deretter kan vi bruke denne instansen til å utføre kommandoer, sjekke status, få utdata, gi input osv.

Det finnes flere metoder for klassen `subprocess.Popen()` som er nyttige å kjenne til. La oss se nærmere på disse med kodeeksempler.

wait()

Metoden `wait()` brukes til å vente til en kommando er fullført. Ingen kode etter `wait()` vil bli utført før kommandoen er ferdig. Her er et eksempel:

import subprocess
process = subprocess.Popen(["ls", "-la"])
print("Completed!")

Når du kjører koden over vil du se at meldingen «Completed!» blir skrevet ut før kommandoen er ferdig. Dette kan vi unngå ved å bruke metoden `wait()`. Vi kan vente til kommandoen er ferdig før den neste linjen utføres.

import subprocess
process = subprocess.Popen(["ls", "-la"])
process.wait()
print("Completed!")

Når du kjører denne koden, vil du se at `wait()` fungerer som forventet. Utskriftssetningen utføres først etter at kommandoen er ferdig.

communicate()

Metoden `communicate()` brukes til å motta utdata, feilmeldinger og gi input til kommandoen. Den returnerer en tuppel som inneholder henholdsvis utdata og feil. La oss se på et eksempel:

import subprocess
process = subprocess.Popen(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
result = process.communicate()
print(result)

subprocess.Popen() – input

Vi kan ikke sende input direkte til klassen `Popen()`. I stedet må vi bruke nøkkelordargumentet `stdin` for å gi input til kommandoen. Instansen av klassen `Popen` vil gi oss et `stdin`-objekt. Dette objektet har en metode som heter `write()` som vi kan bruke for å sende input til kommandoen.

Som tidligere nevnt, forventes input som standard i form av byte-lignende objekter. Derfor er det viktig å sette nøkkelordargumentet `text` til `True` når du oppretter instansen av `Popen`.

Her er et eksempel:

import subprocess
process = subprocess.Popen(["python3", "add.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
process.stdin.write("2 3")
process.stdin.close()
print(process.stdout.read())

poll()

Metoden `poll()` brukes for å sjekke om utførelsen av en kommando er ferdig eller ikke. Metoden returnerer `None` dersom kommandoen fortsatt kjører. La oss illustrere dette med et eksempel:

import subprocess
process = subprocess.Popen(['ping', '-c 5', 'geekflare.com'], stdout=subprocess.PIPE, text=True)
while True:
    output = process.stdout.readline()
    if output:
        print(output.strip())
    result = process.poll()
    if result is not None:
        break

I koden over bruker vi `ping`-kommandoen med 5 forespørsler. Vi har en evig løkke som fortsetter frem til utførelsen av kommandoen er ferdig. Vi bruker `poll()` for å sjekke status. Når `poll()` returnerer noe annet enn `None`, er kommandoen ferdig, og løkken avsluttes.

Utføre Bash-skript

Vi har nå sett på to måter å utføre kommandoer. La oss se på hvordan vi kan utføre bash-skript fra Python.

Modulen `subprocess` har en metode som heter `call`. Denne metoden brukes for å utføre bash-skript. Metoden returnerer utgangskoden fra bash-skriptet. Standard utgangskode for bash-skript er 0. Her er et eksempel:

Først, la oss opprette et bash-skript med navnet `practice.sh` med følgende innhold:

#!/bin/bash
echo "Hello, World!"
exit 1

Lag nå et Python-skript som utfører bash-skriptet:

import subprocess
exit_code = subprocess.call('./practice.sh')
print(exit_code)

Når du kjører Python-skriptet, vil du se følgende utdata:

Hello, World!
1

Konklusjon

Vi har nå sett hvordan man utfører både bash-kommandoer og bash-skript fra Python. Dette er et svært nyttig verktøy for automatisering.

Lykke til med kodingen! 👨‍💻