Автор Тема: Удаление хвостов удаляемых пакетов по признаку ненужности другим пакетам  (Прочитано 2904 раз)

Оффлайн stranger573

  • Мастер
  • ***
  • Сообщений: 1 461
    • Email
Кстати, паттерн в sed (sed 's/-[^-]*-[^-]*$//') даёт ошибку в обоих местах применения. Отсекает часть имени некоторых пакетов, которые потом естественно не находятся. Некоторые пакеты причудливо называются, не вижу как таким способом убрать версию, ошибки всё равно возникают. Зато нашёл способ как скопом у всех пакетов из списка оттяпать версию, оставив только корректные имена. :-)
rpm -q --qf '%{NAME}\n' $z 2>/dev/null

А вот такой дикого вида скрипт даёт корректный список (вроде бы):
apt-cache depends "+ package +" | grep -v -E '(Д|К|В|<.*>)' | sort | uniq | sed 's/^.*: //;s/[0-9]*://;s/ <.*$//;s/ <=.*$//;s/ =.*$//;s/ >=.*$//;s/ >.*$//;s/^ *//' | tr "\n" " " | (read perem; rpm -q --qf '%{NAME}\n' $perem 2>/dev/null)
Одновременно:
Подавляет сообщения "Для установки требует", "Конфликтует", "Вытесняет" (это явно не надо обрабатывать).
Подавляет сообщение о том, что пакет не установлен.
Не выводит имена пакетов, которые не установлены (поэтому проверку на наличие в системе можно дальше не делать).
Выводит имена пакетов из строк вида: "Требует: имя_пакета >= версия" (такие встречаются и установлены).

Оффлайн stranger573

  • Мастер
  • ***
  • Сообщений: 1 461
    • Email
Допилил до рабочего состояния скрипт для p6 с apt-cache.
Получилось как-то так:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# this work for ru_RU.UTF-8 locale only

import subprocess
import sys
import copy


def eprint(*args, **kwargs):
    print(*args, file=sys.stderr, **kwargs)


def getDependency(package):
    if package == "":
        return None
    resp = subprocess.check_output(["/bin/sh", "-c", "apt-cache depends "+ package +" | grep -E -v '(Д|К|В|<.*>)' | sed 's/[0-9]\+://;s/^.*: //;s/^ \+//;s/ .*$//' | sort | uniq | tr '\n' ' ' | (read za; rpm -q --qf '%{NAME}\n' $za 2>/dev/null) | sed 's/^ //'"],universal_newlines=True);
    s = str(resp);
    # eprint(s)
    if s and len(s.strip()) > 0:
        return s.replace("'", "").split("\n")
    else:
        return None


def getAllpackages():
    resp = subprocess.check_output(["rpm", "-qa", "--qf", "'%{NAME}\n'"], universal_newlines=True);
    s = str(resp)
    return s.replace("'", "").split("\n")


def getWhatReq(package):
    args = ["/bin/sh", "-c", "rpm -e --test " + package + " 2>&1 >/dev/null | grep -E -o 'для.+' | sed 's/для //' | tr '\n' ' ' | (read zb; rpm -q --qf '%{NAME}\n' $zb 2>/dev/null) | sed 's/^ //'"]
    resp = subprocess.check_output(args, universal_newlines=True, stderr=None)
    s = str(resp).strip("\n");
    # eprint(s)
    if s and len(s.strip()) > 0:
        return s.replace("'", "").split("\n")
    else:
        return None


candidats = {}
all = getAllpackages()

blacklist = set()
whitelist = set()


def process_pckg_list(pckg_list):
    # eprint(blacklist)
    for package in pckg_list:
        try:
            dep = candidats[package]
            hasBefore = True
        except KeyError:
            dep = set()
            candidats[package] = dep
            hasBefore = False

        try:
            list = getDependency(package)
            if list == None:
                return True
        except subprocess.CalledProcessError:
            eprint("error while processing :", package)
            # xtree.pop(package)
            return False

        for s in list:
            s = s.strip()
            if len(s) == 0:
                continue
            if "(" in s:
                continue
            if "/" in s:
                continue

            try:
                pname = s.strip().split()[0].strip()
            except IndexError:
                eprint("index error:", s)
                continue;
            if pname in blacklist:
                continue
            # print(package, "::", s,"->",pname)
            dep.add(pname)
            if pname not in candidats:
                status = process_pckg_list([pname])
                if not status:
                    try:
                        if not hasBefore:
                            eprint("blacklist:", pname);
                            blacklist.add(pname)
                            del candidats[pname]
                    except KeyError:
                        pass

    return True


def main():
    args = sys.argv  # [-1]
    args.pop(0)
    args1 = set(copy.copy(args))
    for p in args:
        if p[-1]== '+':
            whitelist.add(p[0:-1])
            try :
                args1.remove(p)
            except KeyError:
                pass
            continue
        deps = getWhatReq(p)
        if deps:
            args1  = args1.union(set(deps))

            # args1 = deps(set(args1))
    args1 -= whitelist
    eprint(args1)
    # eprint(blacklist)
    eprint(whitelist)

    process_pckg_list(args1)
    # main(["Ristetto"])

    #    print(len(candidats))
    #    print(candidats)

    #    print("------------------")
    needNextPass = False
    passCount = 1

    class NeedPassException(Exception):
        pass

    dep_cache = {}
    # eprint(candidats)

    while (True):
        # eprint ("------------pass ", passCount)
        passCount += 1
        needNextPass = False
        try:
            for p in candidats:
                try:
                    deps = dep_cache[p]
                except KeyError:
                    try:
                        deps = getWhatReq(p)
                        dep_cache[p] = deps
                    except subprocess.CalledProcessError:
                        # eprint("p=",p)
                        dep_cache[p] = []
                        continue
                if deps == None:
                    # eprint("getWhatReq(",p ,") return None" )
                    continue

                for r in deps:
                    r = r.strip()
                    if len(r) == 0:
                        continue
                    if r == p:
                        # eprint("skip [" + r + "]")
                        continue
                    if r in whitelist:
                        continue
                    if r not in all:
                        continue
                    if r not in candidats:
                        needNextPass = True
                        xlist = candidats[p]
                        for c1 in xlist:
                            try:
                                del candidats[c1]
                                # eprint("removed ",c1)
                            except KeyError:
                                pass
                        try:
                            del candidats[p]
                            # eprint("removed ", p)
                        except KeyError:
                            pass

                        raise NeedPassException
                    pass
                pass
        except NeedPassException:
            continue
        if not needNextPass:
            break
    pass
    result = ''
    eprint("Всего найдено пакетов для удаления:", len(candidats.keys()))
    for s in candidats.keys():
        result = result + s + " ";
    print(result)


if __name__ == "__main__":
    main()

Работает, конечно, не шибко быстро, но вполне терпимо. Время работы для medit 8мин, firefox 9 мин, gimp 16 мин.
Захотелось посмотреть для большого количества пакетов — для удаления glibc-core комп пыхтел 2 часа 10 минут, выдал на удаление 593 пакета из установленных 1800. Вспоминая, как у нас один из админов на прошлой неделе обновлял win10 (не на новую версию, а просто обновлял) где-то часа четыре с половиной, не сказал бы, что два часа это так уж плохо. Тем более, что для всех вменяемых задач которые пробовал, наподобие удаления какого-либо приложения, скрипт укладывается в 5...20 мин. Это на машине 1 ядро 2ГГц 2Гб памяти.

Оффлайн ASte

  • Мастер
  • ***
  • Сообщений: 1 553
Цитата: stranger573 link=topic=38414.msg30809 все2#msg308092 date=1490148617
Работает, конечно, не шибко быстро, но вполне терпимо. Время работы для medit 8мин, firefox 9 мин, gimp 16 мин.

У всех свои критерии терпимости... Для меня такое время работы скрипта уже относится к "не приемлемо долго".
А в p6 есть apt-scripts?
« Последнее редактирование: 22.03.2017 06:19:31 от ASte »

Оффлайн ruslandh

  • Поспешай не торопясь !
  • Модератор глобальный
  • *****
  • Сообщений: 32 292
  • Учиться .... Телепатами не рождаются, ими ....
    • Email

Оффлайн ASte

  • Мастер
  • ***
  • Сообщений: 1 553
stranger573,тогда можно радикально ускорить работу скрипта, если переписать  его на lua в виде расширения к apt, по аналогии со скриптами в apt-scripts.

Оффлайн stranger573

  • Мастер
  • ***
  • Сообщений: 1 461
    • Email
У всех свои критерии терпимости... Для меня такое время работы скрипта уже относится к "не приемлемо долго".
Ну, до этого я всё это добро выковыривал руками. Потому даже два часа в режиме: "не могу сидеть и смотреть как машина работает, пойду прилягу" — вполне очень даже хорошо. :-)
Кстати первой машиной которую я вживую увидел (и даже повезло пару-тройку программ накорябать и запустить) была Сетунь с троичной логикой. Там был диван. :-)

stranger573,тогда можно радикально ускорить работу скрипта, если переписать  его на lua в виде расширения к apt, по аналогии со скриптами в apt-scripts.
Да, я это понял, но получится позже. Там много надо выкурить манов, а меня уже начинают дёргать на другие проблемы.
Тут уж, если зацепился решил довести до чего-то рабочего. Кстати, последний скрипт должен работать и в p8. По крайней мере в python 3.5m работает. Единственное, что плохо — это приколоченность к русской локали.

« Последнее редактирование: 22.03.2017 10:33:08 от stranger573 »

Оффлайн ASte

  • Мастер
  • ***
  • Сообщений: 1 553

Оффлайн stranger573

  • Мастер
  • ***
  • Сообщений: 1 461
    • Email
ASte спасибо за ссылку. Материал очень интересный.

По скрипту.
Провёл один эксперимент и пришёл к выводу, что отказываться от apt-cache и питона пока рано.
Забросил в скрипт, показанный ниже список из 1160 установленных пакетов (зависимых от glibc-core). На выходе получил список из 1694 пакетов.
apt-cache depends "+ package +" | grep -E -v '(Д|К|В|<.*>)' | sed 's/[0-9]\+://;s/^.*: //;s/^ \+//;s/ .*$//' | sort | uniq | tr '\n' ' ' | (read za; rpm -q --qf '%{NAME}\n' $za 2>/dev/null)
   В результате получил весьма неожиданный результат. Время работы скрипта 4,06 сек!
Похоже apt-cache, действительно, не столько долго работает, сколько подготавливается. Для одного пакета medit с выводом списка из 21 пакета время работы 3,67 сек.
   Теперь, прежде чем обращаться к какому-либо коду хочу обдумать диаграмму алгоритма со всеми проверками. В примере с двухчасовой работой для glibc-core, который описывал ранее, apt-cache вызывается не меньше тысячи раз, поскольку вызов происходит практически для каждого пакета. Если изменить алгоритм обработки и получать depends для списка каждого слоя за один раз, теоретически можно уменьшить время работы для большого количества пакетов на два порядка. Глубина вложенности слоёв зависимостей, думаю, не очень большая. К примеру, если слоёв не больше десяти, время работы будет раз в сто меньше.

Оффлайн Тарен

  • Завсегдатай
  • *
  • Сообщений: 78
  • "Жизнь как сон...сплошной обман..."
   Читал долго все сообщения....показалось мне это очень сказочно сложным процесом. Может к месту будет обратить внимание на создание временной шкалы-линейки типа....где к примеру отмечены 3 точки времени(дат). Точка 1 - установлен пакет "a" с зависимостями 1,2,3...Точка 2 - установлен пакет "b"....Точка 3 - удалён пакет "d"....
   Для реализации решения проблемы крутим прпоцес от Точки 3 до Точки 1  --- устанавливается пакет "d",удаляется пакет "b", удаляется пакет "a" с зависимостями 1,2,3.   Но этот вариант слишком громоздский...так как точек может быть много. (можно некоторые пропускать)

Оффлайн ruslandh

  • Поспешай не торопясь !
  • Модератор глобальный
  • *****
  • Сообщений: 32 292
  • Учиться .... Телепатами не рождаются, ими ....
    • Email
Можно конечно привязаться к логам установки и удаления пакетов, но ...
Тут есть одно большое НО - за прошедшее время удалённый пакет может совсем пропасть из репозитория, сменить версию, поменять имя. зависимости и т.п. И его обратно не удасться установить.

Оффлайн Тарен

  • Завсегдатай
  • *
  • Сообщений: 78
  • "Жизнь как сон...сплошной обман..."
Можно конечно привязаться к логам установки и удаления пакетов, но ...
Тут есть одно большое НО - за прошедшее время удалённый пакет может совсем пропасть из репозитория, сменить версию, поменять имя. зависимости и т.п. И его обратно не удасться установить.
удастся ... если этот пакет будет хранится в локальном карантине на диске в ПК

Оффлайн ruslandh

  • Поспешай не торопясь !
  • Модератор глобальный
  • *****
  • Сообщений: 32 292
  • Учиться .... Телепатами не рождаются, ими ....
    • Email
Это надо хранить не только этот пакет, но и все, которые от него зависят.