Найти - Пользователи
Полная версия: Сортировка строк
Начало » Python для новичков » Сортировка строк
1 2
houey
Добрый день. Изучаю Python ровно 2 часа, гугл не спасает.

Хочу запилить небольшую прогу, которая упростит мне жизнь. Имеется следующий код:
 import subprocess
import re
import os
serverRegex = re.compile("server[0-9]+.sh")
clientRegex = re.compile("client[0-9]+.sh")
currentDir = os.getcwd()
remoteDir = "/home/andrey/test"
os.chdir(remoteDir)
process = subprocess.Popen(["ls"], stdout=subprocess.PIPE)
out, err = process.communicate()
servers = serverRegex.findall(out)
clients = clientRegex.findall(out)
os.chdir(currentDir)
print ("Current work dir: %s" % currentDir)
print ("Remote work dir: %s" % remoteDir)
print ("Out: %s" % out)
print ("Servers: %s" % servers)
print ("Clients: %s" % clients)
print ("Amount of servers is: %s" % len(servers))
print ("Amount of clients is: %s" % len(clients))

Вроде работает всё как и ожидается.
Вот вывод программы:

Servers:
Clients:
Amount of servers is: 10
Amount of clients is: 10


Далее я хочу запускать найденные скрипты в определённом порядке (в зависимости от числа в конце файла), т.е. server1, server2, ….

Подскажите как это можно сделать именно средствами Python, желательно без костылей.
Прекрасно знаю что можно написать более коротко и т.п., либо юзать ls с grep'ом, но сейчас стоит задача изучить Python на минимальном уровне, поэтому собственно такой вопрос.

В итоге хочу чтобы массив:
 ['server10.sh', 'server1.sh', 'server2.sh', 'server3.sh', 'server4.sh', 'server5.sh', 'server6.sh', 'server7.sh', 'server8.sh', 'server9.sh']
Стал таким:
 ['server1.sh', 'server2.sh', 'server3.sh', 'server4.sh', 'server5.sh', 'server6.sh', 'server7.sh', 'server8.sh', 'server9.sh', 'server10.sh']

Спасибо!
houey
Нашел ещё один наркоманский способ, но результат примерно такой же

 import os
import re
remoteDir = "/home/andrey/test"
files = os.listdir(remoteDir)
serverRegex = re.compile("server\d+\.sh")
clientRegex = re.compile("client\d+\.sh")
servers = serverRegex.findall(''.join(files))
clients = clientRegex.findall(''.join(files))
for server in servers:
    print ("Got server: %s" % server)
for client in clients:
    print ("Got client: %s" % client)

Не подскажите случайно как можно применить regex к списку? И получить результат?
Т.е. по сути хочу сделать:

 servers = serverRegex.findall(files)
py.user.next
  
>>> import re
>>> 
>>> def tr(s):
...     pat = r'(\d+)\.sh$'
...     return int(re.search(pat, s).group(1))
... 
>>> lst = ['server10.sh', 'server1.sh',
...        'server2.sh', 'server3.sh',
...        'server4.sh', 'server5.sh',
...        'server6.sh', 'server7.sh',
...        'server8.sh', 'server9.sh']
>>> 
>>> out = sorted(lst, key=tr)
>>> out
['server1.sh', 'server2.sh', 'server3.sh', 'server4.sh', 'server5.sh', 'server6.sh', 'server7.sh', 'server8.sh', 'server9.sh', 'server10.sh']
>>>
doza_and
houey
Далее я хочу запускать найденные скрипты в определённом порядке
Во многих случаях если вы разгребаете проблемы, потому что начали думать слишком поздно…

Если бы вы назвали файлы не
 'server10.sh', 'server1.sh', 'server2.sh'
а
 'server010.sh', 'server001.sh', 'server002.sh'
то данного вопроса не было, причем не только в питоне но и в bash grep и куче других программных средств, поскольку в этом случае нумерация совпадает с алфавитным порядком. Имена тогда можно генерировать а не получать из директории с последующей фильтрацией и парсингом имен, что конечно гораздо быстрее и гораздо проще программируется (важно для большого количества файлов).
 ["server{:03d}.sh".format(i) for i in range(11)]
['server000.sh', 'server001.sh', 'server002.sh', 'server003.sh', 'server004.sh', 'server005.sh', 'server006.sh', 'server007.sh', 'server008.sh', 'server009.sh', 'server010.sh']

Нумеровать кстати тоже лучше с нуля.
Rodegast
1) Для просмотра содержания каталога нужно использовать os.listdir.
2) Регулярки работают относительно медленно, по этому ИХМО лучше стараться обойтись без них.
 >>> s = "server1.sh"
>>> s[6:][:-3]
'1'
>>> s = "server100.sh"
>>> s[6:][:-3]
'100'
3) Использовать os.chdir не нужно.
py.user.next
Rodegast
  
>>> s = "server100.sh"
>>> s[6:][:-3]
'100'
Впервые такое вижу
  
>>> 'server1.sh'[6:-3]
'1'
>>> 'server12345.sh'[6:-3]
'12345'
>>>

Rodegast
Регулярки работают относительно медленно
Завтра там будет имя в виде serverabc123.sh или abc123.sh и регулярка сработает без каких-либо изменений, потому что поглощает широкое множество строк. Всё, что касается файловых имён, нужно разбирать регулярками, так как бухгалтерша (образно называя так класс пользователей) не будет тебе 001 вводить или даже английский в именах соблюдать, поэтому устройство имени должно классически быть ослаблено (как ослабляют предусловие у подпрограммы), чтобы при любых изменениях в поступающих данных вероятность изменения кода для распознавания этих данных стремилась к нулю.
Короче, ты фиксируешься на том, что там обязательно должно быть слово server, а зачем тебе на этом фиксироваться? Наоборот, надо убрать это условие, чтобы программа стала больше имён принимать для сортировки. Если там будет comp1.sh, comp2.sh или client1.sh, client2.sh или serv1.sh, serv2.sh, то она без проблем это отсортирует между собой.
Rodegast
> Впервые такое вижу

Тебя что-то удивляет?

> Завтра там будет имя в виде …

Если это имя будет, то наверно из-за того что пользователь допустил ошибку и ему надо дать по рукам.

> Если там будет comp1.sh, comp2.sh или client1.sh, client2.sh или serv1.sh, serv2.sh, то она без проблем это отсортирует между собой.

А если эти comp1.sh или serv2.sh там случайно оказались, то их тоже запустят? А если там файл PahcBarmina007.sh окажется и запускаться всё это будет под root-ом?
py.user.next
Rodegast
Тебя что-то удивляет?
Ну, ты говоришь о скорости, а сам применяешь два среза вместо одного. А при каждом срезе ведь создаётся новая строка. Поэтому и показалось странным, что ты такое применил, будто точно не знаешь, как срезы работают.

Rodegast
Если это имя будет, то наверно из-за того что пользователь допустил ошибку и ему надо дать по рукам.
Да бывало такое, сегодня надо скрипт на одной группе файлов применить, а завтра эти файлы приходят с другого компа под другими именами. Устроены одинаково, но префиксы различаются.

Rodegast
А если эти comp1.sh или serv2.sh там случайно оказались, то их тоже запустят?
Да всякое бывает, но обычно готовится группа файлов (копируется в директорию), да и селектор можно добавить ещё до запуска скрипта (выбрать нужное множество файлов). У тебя же там надо будет не только выбрать, но и переименовать их, потому что из-за фиксированных срезов всё сломается, скрипт будет не применим. А переименуешь файлы, обратно уже их не переименуешь после обработки, потому что старые имена станут неизвестны. Так что придётся тебе всё-таки скрипт менять сам, что является плохим стилем, так как разных ситуаций с именами файлов может быть много.
Rodegast
> Ну, ты говоришь о скорости, а сам применяешь два среза вместо одного. А при каждом срезе ведь создаётся новая строка.

Как раз из-за строки я их и применяю. Её создание на скорость почти не влияет, но зато это привносит гибкость. Например если я захочу проверять начало файла на “server”, то достаточно дописать:
 s[6:] if s[:6] == "server" else "" [:-3]
А в случае одного среза эта возможность становится не очевидной.

> потому что из-за фиксированных срезов всё сломается, скрипт будет не применим.

И это хорошо. Ведь никто не знает что в некорректных файлах находится, а если там всё тот же пресловутый патч Бармина.
houey
Ребят, в общем у меня в продолжении темы появился вопрос. Если кто знает С++ - я хочу написать полный аналог данной проги на С++, но уже на Python.
Суть такова:
1. Завожу вектор строк
2. Удаляю ненужные элементы из вектора (при этом передаю вектор по ссылке)

 #include <iostream>
#include <vector>
#include <string>
void RemoveSuperfluousItems(const std::string& sPattern, std::vector<std::string>& items)
{
	for (auto it = items.begin(); it != items.end();)
		if (it->find(sPattern) == std::string::npos)
			it = items.erase(it);
		else
			++it;
}
void Print(const std::vector<std::string>& items)
{
	for (const auto& item : items)
		std::cout << item << std::endl;
	std::cout << std::endl;
}
int main(int argc, char* argv[])
{
	std::vector<std::string> files = { "server1.sh", "server3.sh", "client1.sh", "server2.sh", "1.sh", "2.sh" };
	Print(files);
	RemoveSuperfluousItems("server", files);
	Print(files);
	return 0;
}

Вывод программы:
server1.sh
server3.sh
client1.sh
server2.sh
1.sh
2.sh

server1.sh
server3.sh
server2.sh

Работает вродё всё как ожидается.

Теперь пытаюсь провернуть подобное на Python'e

 def removeSuperfluousItemsFromList(items, pattern):
    _items = items[:]
    for item in _items:
        if pattern not in item:
            _items.remove(item)
    return _items
lst = ['1', 'server', '3']
print (lst)
lst = removeSuperfluousItemsFromList(lst, 'server')
print (lst)

Вывод программы:

 ['1', 'server', '3']
['server']

Но вот когда уже хочу скопировать список всех файлов в 2 массива (по сути хочу произвести копирование по значению и всё)

А вот пытаюсь заюзать подобную функцию в проге, которая фигурировала выше:
 import re
def removeSuperfluousItemsFromList(items, pattern):
    _items = items[:]
    print ("Items length is %s" % len(_items))
    for item in _items:
        if pattern in item:
            continue
        else:
            _items.remove(item)
    print ("Items length is %s" % len(_items))
    print (_items)
    return _items
def tr(s):
    digit_pattern = r'(\d+)\.sh$'
    result = re.search(digit_pattern, s)
    if result is None:
        return 0
    return int(result.group(1))
#remoteDir = "/home/andrey/test"
#files = os.listdir(remoteDir)
files = ['server1.sh', 'client1.sh', 'server2.sh', 'client4.sh', 'client.sh']
# тут хочу сделать копирование по значению (т.е. получить 2 независимых массива)
servers = files[:]
clients = files[:]
servers = removeSuperfluousItemsFromList(servers, 'server')
clients = removeSuperfluousItemsFromList(clients, 'client')
a = sorted(servers, key=tr)
b = sorted(clients, key=tr)
test = ['1.sh', '0.sh', 'test5.sh', 'test3.sh']
c = removeSuperfluousItemsFromList(test, 'test')
c = sorted(test, key=tr)
print (a)
print (b)
print (c)

на что получаю вывод:
 Items length is 5
Items length is 3
['server1.sh', 'server2.sh', 'client.sh']
Items length is 5
Items length is 3
['client1.sh', 'client4.sh', 'client.sh']
Items length is 4
Items length is 3
['0.sh', 'test5.sh', 'test3.sh']
['client.sh', 'server1.sh', 'server2.sh']
['client.sh', 'client1.sh', 'client4.sh']
['0.sh', '1.sh', 'test3.sh', 'test5.sh']

как я понял проблема в присваивании, подскажите пожалуйста как пофиксить?
Т.е. по сути мне нужно реализовать функцию, которая будет производить манипуляции именно с тем объектом, для которого был вызван тот или иной метод

Спасибо!
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB