× К оглавлению На главную Об авторе

Дата и время публикации:    

Проблема и решение

1. Суть проблемы

Большинство операций с файлами заключаются в анализе текстовых данных, содержащие некую важную и понятную человеческому глазу информацию, например, лог сборки и установки программного обеспечения, выполнения перечня важных системных операций или просто записи реляционной базы данных, на роль которой можете подойти любой текстовый файл содержащий данные соотносимые с другими такими же файлами, образующие таким образом файловую базу данных.

Поэтому давайте взглянем на пример, который будем крутить, в виде нескольких связанных между собой файлов /etc/passwd и /etc/group

2. Решение

2.1 Открытие текстового файла

Показано в листинге 2.1.1

Листинг 2.1.1

...
 10 import os
 11 import sys
...
 45 def outfile_read( fl_name ) :
 46    try:
 47        with open(fl_name,'r') as f:
 48             content = f.read()
 49             return len(content) 
 50    except FileNotFoundError:
 51             print("%s file is missing" % fl_name )
 52             return 0
 53    except PermissionError:
 54             print("You are not allowed to read %s" % fl_name )
 55             return 0 
...
200 if __name__ == '__main__':
201   if len (sys.argv) > 1 :
202      outfl_nm = sys.argv[1]
203      if outfl_nm :
204         n = outfile_read(outfl_nm)
205         print ( 'Outfile %s : ' % outfl_nm, ' read : %d' % int(n), ' bytes')
...

Как показано в строке 47, открытие текстового файла, передаваемого в первом аргументе (см. строки 201 и 202) в вызываемом коде cкрипте Python, производится путем обрашения к встроенной функции open() [3.1] с передачей ей аргументов — имени файла (fl_name) и режима открытия "r" — только чтение

При этом, нам нет надобности импортировать модуль errno, потому что в последних версиях Pytnon3 нет надобности собственноручной инспекции атрибутов исключений типа ENOENT, EACCES, EPERM. Вместо них теперь обещесистемные исключения FileNotFoundError и PermissionError [3.2]

2.2 Построчное чтение данных

В случае с текстовым файлом нам интересно в первую очередь построчное чтение содержимого, которое можно реализовать путем использования непосредственно вызова readline(), к примеру как показано в листинге 2.2.1

Листинг 2.2.1

...
 41             n = 0
 42             while True :
 43                line=f.readline()
 44                if line != '' :
 46                   print ( line )
 47                   n += len(line)
 48                   continue
 49                break
 50             return n
...

Текст которого на мой взгляд избыточен, чем тоже самое чтение, с использованием встроенной функции read(size), которая может принимать один единственный аргумент size – размер читаемой информации в байтах. Если же его не указывать, то по умолчанию, read() будет читать до тех пор, пока не дойдет до конца читаемого файла, т.е. встретит пустую строку (line=''), как показано в листинге 2.2.2

Листинг 2.2.2

...
 38 def passwd_read( csvfl ) :
...
 41             n = 0
 42             for line in f:
 43                 n += len(line) 
...

2.3 Извлечение данных из прочитанной строки

Cамое простое использовать split() из Python-модуля re [3.4], указывая сразу несколько возможных разделителей текста [3.5], как показано в листинге 2.3.1

Листинг 2.3.1

...
 38 def passwd_read( csvfl ) :
...
 41             n = 0
 42             for line in f:
 43                 n += len(line)
 44                 chunks = re.split(':|\\n', line)
 45                 if len(chunks) > 6 :
 46                    if re.match ( '.*/false$|.*/nologin$'  , chunks[6] ) :
 47                       continue;      
 48                    print(chunks) 
...

В результате выполнения кода, как в случае с файлом passwd, будет выдано на консоль нечто подобное, как показано в дампе 2.3.2

Дамп 2.3.2

Truncate: /home/user/Projects/Pythons/passwd_report.csv
['root', 'x', '0', '0', 'root', '/root', '/bin/bash', '']
\=====> ['root', 'x', '0', '', '']
['sync', 'x', '4', '65534', 'sync', '/bin', '/bin/sync', '']
\=====> ['nogroup', 'x', '65534', '', '']
['user', 'x', '1000', '1000', '', '/home/user', '/bin/bash', '']
\=====> ['user', 'x', '1000', '', '']
['sen-sira', 'x', '1001', '1001', '', '/home/sen-sira', '/bin/sh', '']
\=====> ['sen-sira', 'x', '1001', '', '']
['admin', 'x', '1002', '1002', '', '/home/admin', '/usr/bin/bash', '']
\=====> ['admin', 'x', '1002', '', '']
Passwd /etc/passwd :   read : 2680  bytes
Outfile /home/user/Projects/Pythons/passwd_report.csv :   was created.

т.е. объект chunks будет представлять собой не просто строку с текстовыми данными, а уже некую реляцию, которую можно идентифицировать по первому столбцу (символьному имени пользователя), либо по 3 и 4-му столбцам, содержащие соответственно идентификатору пользователя UID и группы (GID).

Тоже самое получим с файлом group, как показано в листинге 2.3.3

Листинг 2.3.2

...
 22 def group_read( gid ) :
 23    try:
 24        with open(group_nm,'r') as f:
 25             for line in f :
 26                 chunks = re.split(':|\\n', line)
 27                 if len(chunks) > 3 and chunks[2] == str(gid) : 
 28                    print ( '\=====>', chunks ) 
 29                    return chunks
 30             return None
 31    except PermissionError:
 32             print("You are not allowed to read %s" % group_nm )
 33             return None               
...
 36 def passwd_read( csvfl ) :
...
 47                 print ( chunks )
 48                 gchunks=group_read( chunks[3] )
...

Для составление отчета, в котором сопоставляется несколько текстовых значений и добавляются одной строкой в текстовый файл, как показано в листинге 2.4.1

Листинг 2.4.1

...
 17 pwdir=os.path.dirname(os.path.realpath(__file__))
...
 38 def passwd_read( csvfl ) :
...
 48                    gchunks=group_read( chunks[3] )   
 49                    if gchunks :
 50                       print ( '\"%s\"' % chunks[0], ';%s' % chunks[2], ';%s' % gchunks[0], ';%s' % chunks[5], ';%s' % chunks[6], file=csvfl )
...
 61 def  csvreport_truncate(fl_path) : 
 62     try:
 63       with open(fl_path,'w') as f:
 64            f.write('LOGIN;UID;GROUP;HOME;SHELL\n')
 65            f.close() 
 66            return True
 67     except PermissionError:
 68            print("You are not allowed to read %s" % fl_path )
 69            return False 
 70
 71 if __name__ == '__main__':
 72  csvreport_nm = pwdir+'/'+'passwd_report.csv'
 73  if csvreport_truncate(csvreport_nm) :
 74     csvrepo_fl=open(csvreport_nm,'a+')
 75     n = passwd_read(csvrepo_fl)
 76     csvrepo_fl.close()
...

Как показано в листинге 2.4.1 в строке 50 через передачу аргументу файлового объекта csvrepo_fl в который будет производится перенаправление вывода print() [3.6]. При этом,

Как показано на рисунке 2.4.2, нами согласно определенных критериев были получены, отобраны и затем сопоставлены данные, что указывает на то, что файлы /etc/passwd и /etc/group относятся к реаляционной базы данных, которые позволяют осуществлять локально процедуру аутентификации и идентифкации, т. е. авторизировать действия и самого пользователя в системе.

Рисунок 2.4.2

При этом, дамп 2.3.2 и рисунк 2.4.2 подсказывают, что каждая строка содержит реляцию, которая имеет постоянную структуру и как минимум содержит одно идетифицирующее свойство с другим файлом.

В данном случае 3-й столбец (при отсчете от нуля) файла /etc/passwd содержит GID, по которому была найдена имя группы в соответствующей реляции в файле /etc/group . В тоже время, сам файла /etc/passwd тоже содержит реляции, т.к. по первый столбец LOGIN содержит всегда уникальное символьное имя.

3. Библиография

3.1 Python's Built-in Functions. Open()

3.2 Python's Built-in exceptions

3.3 Python's Input and Output

3.4 Python Split String by Space

3.5 Stackoverflow. Split string with multiple delimiters in python

3.6 Stackoverflow. How to redirect the output of print to a txt file