Ajouter un espace entre des chiffres et des lettres en python

Je bute sur un problème sans doute simple pour un dev …

Je cherche à remettre à jour un script python pour tirer parti du retour de la commande ‘docker stats’

Depuis plusieurs versions le formatage du retour de cette commande à changé et je me retrouve avec les unité collé à la valeur, or pour le traitement je cherche à insérer un espace entre la valeur et l’unité.

En bref j’obtiens quelque chose comme ça actuellement :

78.21MiB

Et je cherche à obtenir ça :

78.21 MiB

Pour l’instant j’ai tenté à l’aide de rstrip / lstrip mais sans succès pour insérer cet espace salvateur dans cette foutue chaine caractère :confused:

fdata=pdata[3].rstrip(string.digits+" ")

Sachant que pdata[3] est mon résultat déjà parser dans le retour de ma commande et que j’ai besoin d’appliquer après coup encore une fonction (calcul) sur mon résultat fdata selon l’unité relevée.

N’étant pas des plus doué en Python si une bonne âme pythoniste pouvais me filer un coup de main pour régler ce souci ça m’arrangerai bien.

Un début :

fdata = pdata[3][:-3] 

Te supprimera les trois derniers caractères…
Si tu veux un entier, tu es obligé de passer par un float (décimal) :

fdata =  float(pdata[3][:-3])
print(f'{fdata}')
78.21
; pour l'int : 
fdata = int(float(pdata[3][:-3]))
print(f'{fdata}')
78

C’est un début, parce que je ne sais pas, ni si tu as besoin de garder la valeur lettrée d’octets, ni si elle peut avoir plus de trois lettres, ou moins…

Si tu veux absolument garder aussi la valeur lettrée d’octets :

NomBytes = pdata[3][-3:]

Sinon, pour avoir exactement ce que tu cherches :

fdata = pdata[3][:3] + " " + pdata[3][-3:]

C’est très basique ; on peut certainement faire mieux :wink:

Bon pas de secret pour le script en lui même :

import subprocess
import os
import time
import string

def B(b):
    return int(float(b))

def KB(b):
    return int(float(b) * 1024)

def MB(b):
    return int(float(b) * 1024 * 1024)

def GB(b):
    return int(float(b) * 1024 * 1024 * 1024)

def TB(b):
    return int(float(b) * 1024 * 1024 * 1024 * 1024)

size_options = {
    'k':KB,
    'kB':KB,
    'KB':KB,
    'KiB':KB,
    'K':KB,
    'm':MB,
    'mB':MB,
    'MB':MB,
    'MiB':MB,
    'M':MB,
    'g':GB,
    'GB':GB,
    'GiB':GB,
    'G':GB,
    't':TB,
    'T':TB,
    'b':B,
    'B':B
}

def pcpu(data):
    pdata=data.split()
    pcpu_data=pdata[2].split('%')[0]
    return pcpu_data

def umem(data):
    pdata=data.split('/')[0].split()
    fdata=pdata[3][:-3] + " " + pdata[3][-3:]
    value = size_options[fdata.split()[2][0]](fdata.split()[0])
    return value
    #return fdata

def lmem(data):
    pdata=data.split('/')[1].split()
    value = size_options[pdata[1][0]](pdata[0])
    return value

def pmem(data):
    pdata=data.split()
    pmem_data=pdata[6].split('%')[0]
    return pmem_data

def inet(data):
    pdata=data.split('/')[1].split()
    value = size_options[pdata[0][0]](pdata[2])
    return value

def onet(data):
    pdata=data.split('/')[2].split()
    value = size_options[pdata[0][0]](pdata[0])
    return value

options = {
    'pcpu':pcpu,
    'umem':umem,
    'lmem':lmem,
    'pmem':pmem,
    'inet':inet,
    'onet':onet
}

def local_run_command(cmd,file):
    cmd = cmd + " | tee > " + file
    if os.path.isfile(file) == False:
        os.system(cmd)
    else:
        (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime) = os.stat(file)
        ticks=int(time.time())
        delta=ticks-mtime
        if (delta > 60):
            os.system(cmd)

    strings = open(file,"r").readlines()
    return strings


container=sys.argv[1]
key=sys.argv[2]

cmd="docker stats --no-stream=true " + container
strings = local_run_command(cmd,"/tmp/zabbix-docker-stats-"+container+".out")

print options[key](strings[1])

Bon nickel j’arrive à reformater ma sortie correctement me reste maintenant à finir de traiter la donnée correctement et la c’est pas gagné :confused:

PS je commente la suite du recalcul afin de m’assurer de la bonne valeur de fdata :

# python /usr/local/bin/zabbix-docker-stats.py 692e07792052 umem
78.24 MiB

Par contre si je tente avec le script de retrouver la valeur renvoyé en B je me tape encore une erreur, je suppose que j’ai pas pigé comment fonctionne l’appel à la fonction size_options

# python /usr/local/bin/zabbix-docker-stats.py 692e07792052 umem
Traceback (most recent call last):
 File "/usr/local/bin/zabbix-docker-stats.py", line 120, in <module>
   print options[key](strings[1])
 File "/usr/local/bin/zabbix-docker-stats.py", line 66, in umem
   value = size_options[fdata.split()[2][0]](fdata.split()[0])
IndexError: list index out of range

Il te dit que tu cherches une clé qui n’existe pas - hors rang !

Bon finalement ça marche mais j’ai besoin de supprimer les lettres dans la string en passant par une regex car ma sortie n’est pas toujours composé de 3 lettres :confused:

Je vais donc regarder du côté des regex en python.

Bonjour

>>> import re
>>> 
>>> fdata = "78.24MiB"
>>> re.sub(r"(\d+\.\d+|\d+)(\w+)", r"\1 \2", fdata)
'78.24 MiB'
>>> re.sub(r"(\d+\.\d+|\d+)(\w+)", r"\1", fdata)
'78.24'
>>> 
>>> fdata = "7824MiB"
>>> re.sub(r"(\d+\.\d+|\d+)(\w+)", r"\1", fdata)
'7824'
>>> 

Bon enfin de compte durant la journée je suis parvenu à faire fonctionner ces foutu scripts :smiley:

Pour l’explication je devais en fait récupérer une valeur sous forme de flottant et pouvoir effectuer un calcul selon l’unité.

Je penser au départ séparer ma valeur et l’unité par un espace afin de le trier à l’aide d’un .split() mais il m’aie apparu plus simple de travailler avec deux string.

Donc finalement ça donne quelque chose comme ça :

def umem(data):
    pdata=data.split('/')[0].split()
    vdata=re.sub(r'[^\d\.]', '',pdata[3])
    udata=re.sub(r'[\d\.]', '',pdata[3])
    value = size_options[udata[0]](vdata)
    return value 

J’ai simplement fait un import re
Ce qui me permet du coup de pouvoir en plus exporter dynamiquement l’unité directement sur la plateforme de monitoring afin de pouvoir gérer les changement d’unité à la volée directement depuis le serveur Zabbix.

J’en ai profité pour ajouter une recalcule et une remontée d’informations de la data In et Out de chaque container … la grande classe en bref.

Au cas où ce type de script fonctionne sur du python2.7, je verrai pour le migrer vers du python3 lorsque j’en aurai le courage, j’en profiterai pour porter tous le monitoring via agent en mode actif par la même occasion.

Un grand merci à vous deux :wink:

1 J'aime