[Ubuntu] python 이용하여 의존성 패키지 설치

유콩·2021년 8월 11일
2

바로 전에 포스팅했던 인터넷이 되지 않는 환경에서 우분투 패키지 update, upgrade를 테스트하는 도중에 도저히 해결이 안되어 다른 방법을 찾아보았다. 그 중 하나가 파이썬을 이용하여 업그레이드 해야하는 모든 패키지 목록을 읽고 해당 패키지들의 의존성 패키지를 모두 다운 받도록 하는 것이다. 파이썬을 이용하여 만들면서 얻은 것들, 내가 작성한 코드에 대한 설명을 작성하였다.

Ubuntu 20.04 LTS 로 테스트하였음

📑 새로 알게된 것들

  • python 이용하여 cmd 창에 명령어를 실행하고 실행한 명령어의 결과값에 따라 처리
  • python 이용하여 cmd 출력결과 색상 변경

📑 python 이용하여 cmd 명령어 처리

python 표준 라이브러리인 os 를 이용하였다.

1. os.system()

os.system("cmd 명령어")

os.system("cmd 명령어") 로 실행할 수 있으며 실행이 성공하면 0을 반환하고 실패하면 그에 따른 에러코드를 반환한다.

os.system 으로는 명령어를 실행시킨 후 실행 결과에 따라서 cmd 출력을 다르게 하는데에 사용하였다.

2. os.popen().read()

os.popen("cmd 명령어").read()

os의 또 다른 함수 popen는 해당 명령어의 실행결과값을 저장할 수 있다. os.popen("cmd 명령어").read() 로 실행할 수 있다.

os.popen 로는 현재 내가 가지고 있는 패키지의 목록을 불러와 처리하기 위해 사용하였다.

참고 사이트

📑 python 이용하여 cmd 출력결과 색상 변경

print(f"\033[{색 변경 숫자 코드}m{출력할 문자열}\033[0m")

\033[ 은 escape 코드로 이후에 나오는 숫자 색상으로 출력해야한다고 알려주는 역할을 한다. 출력할 문자열 이후에 나오는 0은 해당 범위까지만 색상을 변경할 것이라고 지정한다. \033[0m 으로 종료를 하지 않으면 이후 출력에까지 영향을 끼친다.

cmd 출력결과 색상 변경은 우분투에서 실행되는 결과와 내가 진행상태를 알기 위해 출력하는 값을 구분하기 위해 사용하였다.

참고 사이트

📑 전체 코드

# module that installs packages in now folder

import os

# print color text function
def print_color(text, color_number="36"):
	print(f"\033[{color_number}m{text}\033[0m")

# start
# move to package folder
os.system(f"cd /var/cache/apt/archives")

# package update
print_color(f"package update!!")
os.system(f"sudo apt-get update")
print_color(f"package update finish")

# get all packages
all_packages_list = os.popen("ls").read().split("\n")

all_packages_name_list = []
download_success_list = []
download_fail_list = []
for pkg in all_packages_list:
     if pkg == "":
             continue
     if pkg.split(".")[-1] != "deb":
             continue
     
     # 패키지 이름 규칙 : 패키지 이름_버전-릴리즈-아키텍처.deb
     pkg_name = pkg.split("_")[0]
     all_packages_name_list.append(pkg_name)
     
     pkg_download_result = os.system(f"sudo apt-get install -d -y {pkg_name}")
     
     print_color(f"{pkg_name} package download start!!", 35)
     if pkg_download_result == 0:
             download_success_list.append(pkg_name)
             print_color(f"{pkg_name} download success!!")
     else:
             download_fail_list.append(pkg_name)
             print_color(f"{pkg_name} download fail...", 31)

print_color(f"{len(download_success_list)} packages success and {len(download_fail_list)} packages fail")

if download_fail_list:
     print_color("=====================================", 31)
     print_color("download fail list: ", 31)
	 
     for fail_pkg in download_fail_list:
          print_color(f"{fail_pkg}", 31)

패키지 이름 규칙은 여기를 참고하였다.

📑 코드 설명

def print_color(text, color_number="36"):
	print(f"\033[{color_number}m{text}\033[0m")

매번 치기 번거로워 함수로 만들었다. 일반 print 함수와 동일하게 사용할 수 있으며 색상 기본값은 cyan 이다. 때에 따라서 변경할 수 있다.

os.system(f"cd /var/cache/apt/archives")

print_color(f"package update!!")
os.system(f"sudo apt-get update")
print_color(f"package update finish")

all_packages_list = os.popen("ls").read().split("\n")

패키지들이 저장되는 폴더 위치로 이동 후 update를 하여 현재 내가 가지고 있는 패키지들의 최신 버전 패키지를 다운받는다. 이후 패키지 목록을 이용하여 의존성 패키지를 받기 위해 내가 가지고 있는 패키지들의 목록을 조회한다.

all_packages_name_list = []
download_success_list = []
download_fail_list = []
for pkg in all_packages_list:
     if pkg == "":
             continue
     if pkg.split(".")[-1] != "deb":
             continue
     
     # 패키지 이름 규칙 : 패키지 이름_버전-릴리즈-아키텍처.deb
     pkg_name = pkg.split("_")[0]
     all_packages_name_list.append(pkg_name)
     
     pkg_download_result = os.system(f"sudo apt-get install -d -y {pkg_name}")
     
     print_color(f"{pkg_name} package download start!!", 35)
     if pkg_download_result == 0:
             download_success_list.append(pkg_name)
             print_color(f"{pkg_name} download success!!")
     else:
             download_fail_list.append(pkg_name)
             print_color(f"{pkg_name} download fail...", 31)

조회한 패키지 목록 중에서 패키지 형식이 아닌 파일(.deb가 아닌 파일)을 제외하고 패키지 자체 이름만 파싱하여 저장한다. apt-get install 명령어를 사용할 때 패키지 파일 이름이 아닌 패키지 자체 이름을 이용하여 다운받기 때문이다.

읽어온 패키지 이름을 이용하여 install 명령을 날린다. apt-get install 명령어는 설치하려는 패키지의 의존성 패키지까지 모두 다운로드 받는다.

-d 옵션은 인터넷을 사용할 수 없는 환경에서 이용하기 위해 설치하지 않고 패키지 다운로드만 받기 위해 추가한 것이다. 설치를 바로 진행하려면 해당 옵션은 없어도 된다.

-y 옵션은 매번 패키지를 설치할 때마다 'Y' 값을 입력해야 하기때문에 미리 옵션을 주어 매번 Y가 입력되도록 하였다. 패키지 설치 시 동의를 받는 때는 다운로드(또는 설치) 하려는 패키지를 다운로드 할 경우 일부 저장공간을 사용하는데 괜찮냐는 메시지이기 때문에 굳이 확인하지 않아도 괜찮아서 추가하였다.

몇 개의 패키지가 성공하고 실패했는지 알기 위해 성공한 패키지와 실패한 패키지 목록을 리스트에 담았다.

print_color(f"{len(download_success_list)} packages success and {len(download_fail_list)} packages fail")

if download_fail_list:
     print_color("=====================================", 31)
     print_color("download fail list: ", 31)
	 
     for fail_pkg in download_fail_list:
          print_color(f"{fail_pkg}", 31)

패키지 다운로드를 끝내면 총 몇 개의 패키지가 성공했고 실패했는지 결과로 보여준다. 실패한 패키지가 있을 경우에만 실패한 패키지 목록을 보여준다.


📑 번외) 의존성 패키지 목록 조회 코드

다른 뻘짓을 하다가 작성한 코드이다. 원래는 포스팅을 안하려다 필요한 사람이 호오옥시라도 있을 수가 있어 포스팅한다..

현재 내가 가지고 있는 패키지의 목록을 조회하여 패키지들의 의존성 패키지들을 확인하고 의존성 패키지들을 직접 다운로드 하는 코드이다. 다른 코드들은 비슷하기 때문에 특정 패키지의 의존성 패키지를 조회하여 다운로드하는 코드 만 가져왔다.

for pkg in all_packages_name_list:
     now_pkg_list = os.popen(f"apt-cache depends {pkg}").read().split("\n")
     
     print_color(f"{pkg}'s dependency packages download start!!", 35)
     for now_pkg in [x.strip() for x in now_pkg_list]:
          if now_pkg == pkg or now_pkg == "":
          	continue
          if now_pkg[0] == "|":
          	now_pkg = now_pkg[1:]
          if now_pkg.split(": ")[0] in ['Breaks', 'Recommends', 'Suggests', 'Conflicts', 'Replaces']:
          	break
          
          now_pkg = now_pkg.split(": ")
          download_pkg = now_pkg[0]
          if len(now_pkg) != 1:
          	if "<" in now_pkg[1]:
          		continue
          	if ">" in now_pkg[1]:
          		continue
          	download_pkg = now_pkg[1]
          
          # download dependency packages
          download_result = os.system(f"sudo apt-get install -d -y {download_pkg}")
          if download_result == 0:
          	download_success_list.append(download_pkg)
          	print_color(f"{download_pkg} download success!!")
          else:
          	download_fail_list.append(download_pkg)
          	print_color(f"{download_pkg} download fail...", 31)

apt-cache depends 의 결과 뜻은 여기를 참고하였다. 명령어 결과에서 DependsPredepends 의 패키지만 받도록 하였다.

📑 마무리

'sudo apt-get update' 는 의존성 패키지를 다운로드 받는 것과 연관이 없다는 것을 늦게 알아차려 문제를 해결하기까지 시간이 오래 걸렸다. 처음부터 해당 정의를 제대로 이해했다면 이번에 들인 시간을 더 단축할 수도 있었을 것이다. 단순한 정의라도 이게 어떤 의미를 가지고 있는지 이해하려는 습관을 가지도록 노력해야겠다.

0개의 댓글