Python3-subprocesuitvoer

Ik wil het Linux-hulpprogramma wc voor het tellen van woorden gebruiken om het aantal regels in de /var/log/syslog te bepalen, zodat ik kan detecteren dat het groeit. Ik heb verschillende tests geprobeerd en hoewel ik de resultaten terugkrijg van wc, bevat het zowel het aantal regels als het commando (bijv. var/log/syslog).

Dus het komt terug:
1338 /var/log/syslog
Maar ik wil alleen het aantal regels, dus ik wil het /var/log/syslog-gedeelte verwijderen en gewoon 1338 behouden.

Ik heb geprobeerd het van bytestring naar string te converteren en vervolgens het resultaat te strippen, maar geen plezier. Hetzelfde verhaal voor het converteren naar string en strippen, decoderen, enz. – ze produceren allemaal niet de uitvoer waarnaar ik op zoek ben.

Dit zijn enkele voorbeelden van wat ik krijg, met 1338 regels in syslog:

  • b’1338 /var/log/syslog\n’
  • 1338 /var/log/syslog

Hier is wat testcode die ik heb geschreven om deze noot te kraken, maar geen oplossing:

import subprocess
#check_output returns byte string
stdoutdata = subprocess.check_output("wc --lines /var/log/syslog", shell=True)
print("2A stdoutdata: " + str(stdoutdata))
stdoutdata = stdoutdata.decode("utf-8")
print("2B stdoutdata: " + str(stdoutdata))    
stdoutdata=stdoutdata.strip()
print("2C stdoutdata: " + str(stdoutdata))    

De output hiervan is:

  • 2A stdoutdata: b’1338 /var/log/syslog\n’

  • 2B stdoutdata: 1338 /var/log/syslog

  • 2C stdoutdata: 1338 /var/log/syslog

  • 2D stdoutdata: 1338 /var/log/syslog


Antwoord 1, autoriteit 100%

Ik stel voor dat je subprocess.getoutput()gebruikt omdat het precies doet wat je wilt: een commando in een shell uitvoeren en de tekenreeksuitvoer(in tegenstelling tot byte stringuitvoer). Dan kun je splitsen op witruimteen het eerste element uit de geretourneerde lijst met strings.

Probeer dit:

import subprocess
stdoutdata = subprocess.getoutput("wc --lines /var/log/syslog")
print("stdoutdata: " + stdoutdata.split()[0])

Antwoord 2, autoriteit 31%

Sinds Python 3.6 kun je check_output()een strlaten retourneren in plaats van bytesdoor het een coderingparameter:

check_output('wc --lines /var/log/syslog', encoding='UTF-8')

Maar aangezien u alleen de telling wilt, en zowel split()als int()kunnen worden gebruikt met bytes, hoeft u dat niet te doen. moet je je bezighouden met de codering:

linecount = int(check_output('wc -l /var/log/syslog').split()[0])

Hoewel sommige dingen gemakkelijker kunnen zijn met een extern programma (bijv. het tellen van logregelitems die zijn afgedrukt door journalctl), hoeft u in dit specifieke geval geen extern programma te gebruiken. De eenvoudigste oplossing voor alleen Python is:

with open('/var/log/syslog', 'rt') as f:
    linecount = len(f.readlines())

Dit heeft wel het nadeel dat het het hele bestand in het geheugen leest; als het een enorm bestand is, initialiseer dan linecount = 0voordat je het bestand opent en gebruik een for line in f: linecount += 1lus in plaats van readlines()om tijdens het tellen slechts een klein deel van het bestand in het geheugen te hebben.


Antwoord 3, autoriteit 13%

Om te voorkomen dat een shell wordt aangeroepen en bestandsnamen worden gedecodeerd die een willekeurige bytereeks kunnen zijn (behalve '\0') op *nix, kunt u het bestand doorgeven als stdin:

import subprocess
with open(b'/var/log/syslog', 'rb') as file:
    nlines = int(subprocess.check_output(['wc', '-l'], stdin=file))
print(nlines)

Of u kunt eventuele decoderingsfouten negeren:

import subprocess
stdoutdata = subprocess.check_output(['wc', '-l', '/var/log/syslog'])
nlines = int(stdoutdata.decode('ascii', 'ignore').partition(' ')[0])
print(nlines)

Antwoord 4, autoriteit 4%

Equivalent aan Curt J. Sampson’s antwoord is ook dit (het geeft een string terug):

subprocess.check_output('wc -l /path/to/your/file | cut -d " " -f1', universal_newlines=True, shell=True)

uit documenten:

Als codering of fouten zijn opgegeven, of tekst waar is, bestandsobjecten voor
stdin, stdout en stderr worden geopend in tekstmodus met behulp van de opgegeven
codering en fouten of de standaard io.TextIOWrapper. De
universal_newlines argument is gelijk aan tekst en is voorzien in
achterwaartse compatibiliteit. Standaard worden bestandsobjecten binair geopend
modus.

Iets soortgelijks, maar een beetje ingewikkelder met subprocess.run():

subprocess.run(command, shell=True, check=True, universal_newlines=True, stdout=subprocess.PIPE).stdout

aangezien subprocess.check_output() gelijk zou kunnen zijn aan subprocess.run().


Antwoord 5

getoutput(en de nauwere vervanging getstatusoutput) zijn geen directe vervanging van check_output– er zijn beveiligingswijzigingen in 3.x die voorkomen sommige eerdere commando’s werkten op die manier (mijn script probeerde met iptables te werken en faalde met de nieuwe commando’s). Het is beter om je aan te passen aan de nieuwe python3-uitvoer en het argument universal_newlines=True toe te voegen:

check_output(command, universal_newlines=True)

Dit commando gedraagt ​​zich zoals je verwacht check_output, maar retourneert string-output in plaats van bytes. Het is een directe vervanging.

Other episodes