Benutzer-Werkzeuge

Webseiten-Werkzeuge


de:sysadmin:tools:python

python Spickzettel

python möchte ich nicht mehr missen. Gerade wenn man Scripts zur Systemadministration und Systemdokumentation schreibt, bei denen das mit einem Shell-Script kompliziert wird, ist das eine sehr praktische und dennoch erfreulich übersichtliche (und somit sehr gut wartbare) Sprache. Auch die Möglichkeit, jedes Statement einzeln schon mal vorher in der Python-Konsole ausprobieren zu können, macht mir das Leben doch viel einfacher.

Welche Python-Version läuft bei mir?

von der Konsole aus:
$ python -V

interaktiv / Script:

import sys
print sys.version

Mehrwertsteuer

die Python-Konsole lässt sich prima als Taschenrechner missbrauchen …

def mw(netto):
     mw=netto*0.19+netto
     return mw

Und so sieht das auf der Konsole aus:

heb@moorbirke:~/bin> python
Python 2.5 (r25:51908, Jan  9 2007, 16:59:32)
[GCC 4.1.2 20061115 (prerelease) (some Linux)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> def mw(netto):
...     mw=netto*0.19+netto
...     return mw
...
>>>
>>> mw(100)
119.0
>>>

Würfel

und für schnödes Glücksspiel kann man Python auch einsetzen:

#!/usr/bin/env python

import random

seiten=(1,2,3,4,5,6)

print random.choice (seiten)

Formatierter Text (python printf)

IP="192.168.5.55"
PORT="80"
DNSname="www.example.com"
HWname="lnx-kluftinger"

Dreimal die selbe Ausgabe auf 3 verschiedenen Arten

print "* " + IP + ":" + PORT + " -- " + DNSname + " -- " + HWname
print "* %(a)s:%(b)s -- %(c)s -- %(d)s" % {'a':IP,'b': PORT, 'c': DNSname, 'd':HWname}
print "* %s:%s -- %s -- %s" % ( IP, PORT, DNSname, HWname )

Ausgabe:

* 192.168.5.55:80 -- www.example.com -- lnx-kluftinger

MySQL - Datenbankabfrage

#!/usr/bin/env python

# test-mysql-db.py

import MySQLdb

dbhost="testdb.example.com"
dbuser="testuser"
dbpasswd="strenggeheim"
dbname="testdb"

def mysqldbquery(dbq):
     # verbindung zum datenbankserver aufnehmen
        try:
            db = MySQLdb.connect (host=dbserver,user=dbuser, passwd=dbpasswd, db=dbname)
        except MySQLdb.OperationalError:
            # wenn das mit der DB-Verbindung nicht klappt, Fehlermeldung
            print "Fehler beim Verbinden mit Datenbank %s: %s " % (dbserver,sys.exc_info()[1])
    cursor = db.cursor()
    cursor.execute(dbq)
    result = cursor.fetchone()   # hole nur eine zeile, wenn man alle will: fetchall()
    if (not (result)):
       result=('')    # diese fehlerbehandlung koennte noch leicht optimiert werden ...
    db.close()
    return result

if __name__ == '__main__':

    ergebnis=mysqldbquery("select Servername from ServerTable where ServerIP='192.168.192.168'")

    print ergebnis[0]

PostgreSQL - Datenabfrage

vorsicht, ungetestetes Fragment FIXME :

import pg

dbserver="testpg.example.com"
dbuser="testuser"
dbpasswd="strenggeheim"
datenbankname="testdb"

# verbindung zum datenbankserver aufnehmen
def pgdbquery(dbq)
    try:
        pgcon = pg.connect(
            dbname=datenbankname, host=dbserver,
            user=dbuser, passwd=dbpasswd)
    except pg.InternalError:
        print "Fehler beim Verbinden mit %s: %s " % (dbserver,sys.exc_info()[1])

    resulttuplelist=pgcon.query(dbq).getresult()   # hole alles
    pg.close

    return resulttuplelist

SSH

für Python 2.3 und niedriger (ohne Warnungen auch mit 2.4 und 2.5)

#!/usr/bin/env python

# test-ssh.py

import os                                                    # python <= 2.5

def sshcmd(server,cmd):
    """sshcmd(server,cmd) -> string

    connect via ssh to server,
    do command cmd on that server,
    return max 900 characters of output to stdout
    """
    cmdsum= "ssh -o ConnectTimeout=5 %s %s" % (server,cmd)
    myin,myout,myerr=os.popen3(cmdsum)                       # python <= 2.5
    txt=myout.read(900)
    myerror=myerr.read(800) # FIXME
    if(myerror):
        txt="ERROR: (ssh to %s): %s " % (server,myerror)
    return txt


if __name__ == '__main__':
    
    print sshcmd("192.168.192.168","uname")

python 2.6 "os.popen3 is deprecated. Use the subprocess module"

ab Python 2.6 erhält man bei obigen Script die Warnung "os.popen3 is deprecated. Use the subprocess module".

Deswegen sollte man die popen3-Zeile jetzt ein wenig anders (leider etwas umständlicher) ausdrücken:

#!/usr/bin/env python

# test-ssh.py

import subprocess                                            # python => 2.4

def sshcmd(server,cmd):
    """sshcmd(server,cmd) -> string

    connect via ssh to server,
    do command cmd on that server,
    return max 900 characters of output to stdout
    """
    cmdsum= "ssh -o ConnectTimeout=5 %s %s" % (server,cmd)
    #                                                      v--  python => 2.4
    p = subprocess.Popen(cmdsum, shell=True,bufsize=0,
        stdin=subprocess.PIPE,stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,close_fds=True)
    myin,myout,myerr=(p.stdin,p.stdout,p.stderr)
    #                                                      ^--  python => 2.4
    txt=myout.read(900)
    myerror=myerr.read(800) # FIXME
    if(myerror):
        txt="ERROR: (ssh to %s): %s " % (server,myerror)
    return txt

if __name__ == '__main__':
    
    print sshcmd("192.168.192.168","uname")

Das Modul subprocess gibt es seit Python 2.4

http://docs.python.org/library/subprocess.html

SSH mit Fehlerbehandlung

#!/usr/bin/env python

# test-ssh.2.py

import os
def sshcmderr(server,cmd):
    """sshcmd(server,cmd) -> string
    connect via ssh to server,
    do command cmd on that server,
    return output and error in tupple
    """
    cmdsum= "ssh -o ConnectTimeout=5 %s %s" % (server,cmd)
    myin,myout,myerr=os.popen3(cmdsum)                      # python <= 2.5
    txt=myout.read(9999)
    myerror=myerr.read(9999)
    return(txt,myerror)


if __name__ == '__main__':

    ausgabe,fehler=sshcmd("192.168.192.168","uname")
    print ausgabe
    print fehler

egrep via ssh

Krankes Konstrukt, um auf einem entfernten Rechner einzelne Dateien mit egrep zu durchsuchen … (die Suchstrings werden als Aufrufargument übergeben)

...

if __name__ == '__main__':

    suchstringliste=sys.argv[1:]
    dateiliste = "/tmp/testdatei1.txt /tmp/testdatei2.txt"
    egrepstring="%s" % (string.join(suchstringliste,'\|'))  # baut (suchstring1|zwei|drei) zusammen
    ausgabe,fehler=sshcmderr(server,"""egrep \\'\(%s\)\\' %s """ % (egrepstring,dateiliste))
    print ausgabe
    print fehler

iso2epoch

#!/usr/bin/env python
# epoch2iso  iso2epoch

# hella breitkopf (www.unixwitch.de) 2002-12-19

"""
Usage:
epoch2iso epochtime
    input: epoch time integer
    returns a date string formated  YYYY-MM-DD--hhmmss  
    a "Z" is added to mark UTC

iso2epoch isotime
    input: "isotime" 
    returns an epoch time integer

EPOCH: Number of seconds since the Epoch, in UTC (a.k.a. GMT).  
It may be an integer or a floating point number (to represent fractions of seconds).
The Epoch is system-defined; on Unix, it is generally January 1st, 1970.
(1970-01-01--000000Z  (UTC))
Generate current time in epoch-format with the command:  date +%s

isotime: The ISO timeformat enhanced with hour, minute and seconds: YYYY-MM-DD--hhmmss
Generate the current time in this format with the command: date -u +%Y-%m-%d--%H%M%S%z
This program uses UTC (Universal Time Coordinated) aka. GMT (Greenwich Mean Time)
aka. Zulu Time

Attached timezone information other than Z or +0000 can't be used in this version,
if there is no timezone information, we assume UTC, anyway.
"""
# Q: so, Hella, why UTC, and not the much nicer localtime?

# A: It would need big effort to program around the problems of daylight-saving time
#    - if you use localtime without specification of timezone (with or without DST), 
#    you have these switch-hours, where you can't make 1 to 1 assignments from localtime to epoch 

#    I might think about a special command line option for switching this in future versions

# Q: ISO 8601 recommends to put a capital "T" between date and time, why did you use "--" instead?
# A: Easier to read


import sys
import time
import os.path

mytimeformatSTR="%Y-%m-%d--%H%M%S"


def usage(exitvalue):
    print __doc__
    #sys.exit(exitvalue)


def epoch2iso(epochSTR):
    """
Usage:
epoch2iso (epochtime)
    input: epoch time integer
    returns a date string formated  YYYY-MM-DD--hhmmss    (with attached Z for "Zulu Time")
    """
    try:
	epochint=int(epochSTR)
	#isotime=time.strftime(mytimeformatSTR , time.localtime( epochint ))
	isotime=time.strftime(mytimeformatSTR , time.gmtime( epochint ))+"Z"
    except ValueError:
	print >> sys.stderr, "Error: wrong input parameter"
	usage(4)
    return(isotime)

def iso2epoch(isoSTR):
    """
Usage:
iso2epoch (isostring)
    isostring: date+time in the format YYYY-MM-DD--hhmmss[Z|+0000] 
    returns epochtime 
    
    """

    try:
	#epochtime=int(time.mktime (time.strptime(isoSTR,mytimeformatSTR)))
	
	# shorten to excact 18 letters (you might have additional "Z"
	# (marks UTC)

	isoSTR,timezone=isoSTR[0:18],isoSTR[18:]

	# timezone might be left out, might be Z or might be "+0000"
	# which means we don't use anything else than UTC

	if (timezone !="" and timezone != "Z" and timezone != "+0000") :
	    usage(6)
	
	#epochtime=int(time.mktime (time.strptime(isoSTR,mytimeformatSTR)))
	epochtime=int(time.strftime ('%s', time.strptime(isoSTR,mytimeformatSTR)))
    except ValueError:
	print >> sys.stderr, "Error: wrong input parameter"
        usage(4)
	return(4)
    return (epochtime)


if __name__ == '__main__':
# if called as single programm, do these steps:


    # check the input parameters
    # we need one parameter (more are ignored)
    try:
	inputstring=sys.argv[1]		#either epochtime or isotime
    except IndexError:    
	print "Error: Parameter missing" 
	usage(4)

    # if help is needed help we give ..
    if inputstring == "--help" or inputstring == "-h" :
	usage(0)


    #with what program name were I called?
    programname=os.path.basename( sys.argv[0] )

    #depending on program name either we convert epoch time to isotime
    # or isotime to epochtime ...
    if programname == "epoch2iso" :
	print epoch2iso(inputstring)

    elif programname == "iso2epoch" :
	print iso2epoch(inputstring)

    else:
	print >> sys.stderr, 'Program name was expected to be "epoch2iso" or "iso2epoch", not ',programname
	sys.exit(5)

rename snortfiles + gzip (Datumskonvertierung)

import os
import iso2epoch # siehe oben ...

for file in os.listdir('.'):
     extension=file.split(basename+'.').pop()
     if extension.isdigit():
             newname=basename +'.'+ iso2epoch.epoch2iso(extension)
             print newname
             os.rename(file,newname)
             os.system('gzip ' + newname)
     else:
             print file

UTF-8 zu Unicode zu HTML-Entities

(getestet mit Python 2.7.6)

Meine Datenbank gibt mir UTF-8:

>>> originalfromdb
'Mein sch\xc3\xb6ner Test'

Bei der Ausgabe sieht das ja eigentlich ganz gut aus …

>>> print originalfromdb
Mein schöner Test
>>> type(originalfromdb)
>>> <type 'str'>

Wenn man diesen Strin in Python weiterverarbeiten will, bekommt man aber je nach Situation die schöne Fehlermeldung: "UnicodeDecodeError: 'ascii' codec can't decode byte 0xc3 in position 8: ordinal not in range(128)". Damit will Python uns sagen, dass man den String jetzt mal in richtigen Unicode umwandeln soll. Das geht so:

>>> text=unicode(originalfromdb, 'utf-8')
>>> text
u'Mein sch\xf6ner Test'
>>> print text
Mein schöner Test
>>> type(text)
<type 'unicode'>

Wenn ich das ganze dann zusätzlich in HTML-Entities umwandeln will (weil das dann in manchen Fällen doch noch sicherer ist als sich auf die UTF-8-Festigkeit des Empfängers zu verlassen), dann geht das so:

>>> htmltext=unicode(originalfromdb, 'utf-8').encode('ascii','xmlcharrefreplace')
>>> htmltext
'Mein sch&#246;ner Test'

siehe auch

de/sysadmin/tools/python.txt · Zuletzt geändert: 2016-01-17 14:37 von hella

Seiten-Werkzeuge