Linux Mint 22.3 XFCE — 터미널 완전 정복
XFCE 데스크톱 기반 Linux Mint 22.3에서 파이썬 개발자가 필요한 모든 리눅스 기초를 다룹니다.
리눅스 & Linux Mint 22.3 XFCE란
Linux Mint 22.3은 2025년 출시된 Ubuntu 24.04 LTS 기반 배포판입니다. 코드명 "Wilma"이며, XFCE 4.18 데스크톱 환경을 사용합니다.
파이썬 개발자에게 Linux Mint XFCE가 좋은 이유:
- 서버 환경 일치 — Django/FastAPI 배포 서버(Ubuntu 기반)와 동일한 패키지 관리자, 동일한 명령어
- 가벼움 — XFCE는 RAM을 적게 써서 파이썬 서버, Docker, DB를 동시에 돌리기 편함
- apt 패키지 — pip + apt 조합으로 모든 개발 환경 세팅 가능
- LTS 기반 — Ubuntu 24.04 기반으로 2029년까지 보안 업데이트 보장
| 항목 | Linux Mint 22.3 XFCE | 비고 |
|---|---|---|
| 기반 | Ubuntu 24.04 LTS (Noble) | apt 완전 호환 |
| 커널 | Linux 6.8.x | 최신 하드웨어 지원 |
| Python | 3.12.x (기본 내장) | pip3, venv 포함 |
| GCC | 13.x (apt install 필요) | build-essential |
| RAM 사용 | ~400MB (유휴 시) | VM에도 쾌적 |
| 기본 터미널 | xfce4-terminal | Ctrl+Alt+T 설정 가능 |
| 기본 에디터 | mousepad | nano/vim 별도 설치 |
터미널 열기 & 첫 명령 (XFCE)
XFCE에서 터미널을 여는 방법:
- 바탕화면 우클릭 → 터미널 열기
- 애플리케이션 메뉴 → 시스템 → Xfce Terminal
- 단축키 설정: 설정 → 키보드 → 애플리케이션 단축키 →
xfce4-terminal→Ctrl+Alt+T - 파일관리자(Thunar) 주소창에서 Ctrl+Alt+T (Thunar 내 위치에서 터미널)
xfce4-terminal 입력 → Ctrl+Alt+T 누르기$ lsb_release -a No LSB modules are available. Distributor ID: Linuxmint Description: Linux Mint 22.3 Release: 22.3 Codename: wilma $ uname -r # 커널 버전 6.8.0-51-generic $ python3 --version Python 3.12.3 $ echo $XDG_CURRENT_DESKTOP # 데스크톱 환경 확인 XFCE $ free -h # 메모리 사용량 total used free Mem: 7.7Gi 412Mi 6.8Gi
Tab 자동완성 / ↑↓ 명령 기록 / Ctrl+R 기록 검색 / Ctrl+C 중단 / Ctrl+D 종료 / Ctrl+L 화면 지우기 / Ctrl+Shift+C xfce4-terminal 복사 / Ctrl+Shift+V xfce4-terminal 붙여넣기파일시스템 구조
리눅스는 모든 것이 파일입니다. 하나의 루트 /에서 모든 경로가 시작됩니다.
$ pwd # 현재 위치 (Print Working Directory) /home/user $ cd /etc && pwd /etc $ cd ~ # 홈으로 $ cd - # 이전 위치 (토글) $ cd ~/projects/myapp # ~ 확장 # 경로 규칙 # /home/user/projects 절대경로 (/ 로 시작) # ./src/main.py 상대경로 (현재 위치 기준) # ../config.py 상위 디렉터리의 파일 # ~ /home/현재사용자 # XFCE Thunar 파일관리자에서 경로 직접 입력: Ctrl+L
필수 명령어 총정리
| 명령어 | 설명 | 예시 |
|---|---|---|
ls -la | 파일 목록 (상세+숨김) | ls -lhS (크기순) |
cd | 디렉터리 이동 | cd ~/projects |
pwd | 현재 경로 출력 | pwd |
mkdir -p | 폴더 생성 (중간경로 포함) | mkdir -p src/utils |
rm -rf | 삭제 (⚠️ 주의) | rm -rf build/ |
cp -r | 복사 (디렉터리) | cp -r src/ backup/ |
mv | 이동/이름 변경 | mv old.py new.py |
cat | 파일 내용 출력 | cat -n main.py |
less | 페이지 단위 보기 (q 종료) | less /var/log/syslog |
head / tail | 앞/뒷부분 출력 | tail -f app.log |
grep -rn | 패턴 검색 (재귀+줄번호) | grep -rn "def " . |
find | 파일 찾기 | find . -name "*.py" |
touch | 빈 파일 생성 | touch main.py |
wc -l | 줄 수 세기 | wc -l main.py |
sort / uniq | 정렬 / 중복 제거 | sort file.txt | uniq -c |
diff | 파일 비교 | diff old.py new.py |
tree | 디렉터리 구조 트리 | tree -L 2 myproject/ |
history | 명령 기록 | history | grep git |
man | 매뉴얼 (q 종료) | man grep |
# | 파이프: 앞 출력 → 다음 입력 $ ls -la | grep ".py" | wc -l # .py 파일 수 $ cat access.log | grep "404" | sort | uniq -c # 리다이렉션 $ python3 main.py > out.txt # stdout 저장 $ python3 main.py >> log.txt # stdout 추가 $ python3 main.py 2> err.log # stderr만 $ python3 main.py &> all.log # stdout+stderr # tee: 화면 + 파일 동시 $ python3 main.py | tee log.txt
rm -rf *, rm -rf /는 절대 금지. 항상 경로를 먼저 echo로 확인하세요.파일 권한 (chmod)
모든 파일은 소유자(u), 그룹(g), 기타(o) 3자에 대해 읽기(r=4), 쓰기(w=2), 실행(x=1) 권한을 가집니다.
-rwxr-xr-- 1 user group 2048 May 6 script.sh ↑↑↑↑↑↑↑↑↑ │└──┤└──┤└──┘ │ u g o │ rwx r-x r-- │ 7 5 4 └─ 파일타입: - 파일, d 디렉터리, l 심볼릭링크
$ chmod 755 run.sh # rwxr-xr-x : 실행파일 표준 $ chmod 644 config.py # rw-r--r-- : 일반 파일 $ chmod 600 .env # rw------- : API키 등 비밀파일 $ chmod 700 scripts/ # rwx------ : 내 디렉터리만 $ chmod +x myscript.py # 실행권한 추가 $ chmod -R 755 myproject/ # 재귀 적용 # 파이썬 스크립트 직접 실행하기 $ nano hello.py # 첫 줄에 추가: #!/usr/bin/env python3 $ chmod +x hello.py $ ./hello.py # python3 없이 실행!
패키지 관리 (apt) — Linux Mint 22.3
# 패키지 목록 갱신 (항상 먼저) $ sudo apt update # 전체 업그레이드 $ sudo apt upgrade # 개발자 필수 패키지 한 번에 $ sudo apt install -y \ git curl wget tree htop vim \ python3-pip python3-venv python3-dev \ build-essential gcc g++ make gdb \ net-tools nmap # 패키지 검색/정보 $ apt search python3 $ apt show python3 $ apt list --installed | grep python # 제거 $ sudo apt remove 패키지명 $ sudo apt purge 패키지명 # 설정파일까지 $ sudo apt autoremove # 불필요한 의존성 # Linux Mint 전용: mintinstall (소프트웨어 매니저) $ mintinstall # GUI 소프트웨어 센터 # flatpak (snap 대신 Mint는 Flatpak 권장) $ flatpak install flathub org.gimp.GIMP
텍스트 편집 (nano / vim / mousepad)
XFCE의 기본 GUI 에디터는 Mousepad입니다. 터미널에서는 nano(쉬움) 또는 vim(강력)을 사용합니다.
# nano (입문자 추천) $ nano hello.py # ^O 저장 ^X 종료 ^W 검색 ^\ 치환 ^K 줄 삭제 ^U 붙여넣기 # vim (강력, 학습 필요) $ vim main.py # i → Insert모드 Esc → Normal :wq 저장종료 :q! 강제종료 # dd 줄삭제 yy 복사 p 붙여넣기 /검색어 검색 # Mousepad (XFCE GUI 에디터) $ mousepad hello.py & # & : 백그라운드 실행 (터미널 계속 사용) # VS Code (권장 개발 에디터) $ sudo apt install snapd # 또는 공식 .deb 다운로드: code.visualstudio.com $ wget -q https://packages.microsoft.com/keys/microsoft.asc \ | gpg --dearmor > /etc/apt/keyrings/microsoft.gpg $ sudo apt install code
프로세스 & 시스템 관리
# 프로세스 목록 $ ps aux | grep python # 파이썬 프로세스만 $ pgrep -la python # 이름으로 PID 검색 # 실시간 모니터 $ htop # 컬러 모니터 (추천) $ top # 기본 (q 종료) # XFCE 작업 관리자 $ xfce4-taskmanager & # GUI 작업 관리자 # 종료 $ kill -9 1234 # PID로 강제 종료 $ pkill python # 이름으로 종료 # Ctrl+C 현재 실행 중단 # Ctrl+Z 백그라운드로 (fg로 복귀) # 백그라운드 실행 $ python3 server.py & # & : 백그라운드 $ nohup python3 server.py > server.log 2>&1 & # 로그아웃 후에도 $ jobs # 백그라운드 작업 목록 # 시스템 자원 $ free -h # 메모리 $ df -h # 디스크 파티션 $ du -sh * | sort -h # 현재 폴더 크기 정렬 $ lsblk # 디스크 블록 장치
환경변수 & .bashrc 설정
$ nano ~/.bashrc # ── 개발자 alias 모음 ───────────────────────── alias ll='ls -la --color=auto' alias la='ls -A' alias py='python3' alias pip='pip3' alias gs='git status' alias gl='git log --oneline --graph --all' alias gp='git push' alias ..='cd ..' alias ...='cd ../..' alias myip='curl -s ifconfig.me' alias ports='ss -tulnp' alias update='sudo apt update && sudo apt upgrade -y' # ── 환경변수 ───────────────────────────────── export EDITOR="nano" export PYTHONDONTWRITEBYTECODE=1 # .pyc 생성 안 함 export PYTHONUNBUFFERED=1 # 출력 버퍼링 없음 export PATH="$HOME/.local/bin:$PATH" # ── 프롬프트 커스터마이즈 ──────────────────── # 현재 git 브랜치 표시 parse_git_branch() { git branch 2>/dev/null | grep '*' | sed 's/* //' } export PS1='\[\033[0;32m\]\u@\h\[\033[0m\]:\[\033[0;34m\]\w\[\033[0;33m\]$(b=$(parse_git_branch); [ -n "$b" ] && echo " ($b)")\[\033[0m\]\$ ' # ── 적용 ──────────────────────────────────── $ source ~/.bashrc
import os from dotenv import load_dotenv # pip install python-dotenv load_dotenv() # .env 파일 자동 로드 DEBUG = os.getenv('DEBUG', 'False') == 'True' SECRET_KEY = os.environ['SECRET_KEY'] # 없으면 KeyError DB_URL = os.getenv('DATABASE_URL', 'sqlite:///db.sqlite3')
셸 스크립트 기초
#!/bin/bash # Linux Mint 22.3 XFCE 개발환경 자동 세팅 set -e # 오류 시 즉시 종료 PROJECT="$1" # 첫 번째 인자 = 프로젝트명 PYTHON=python3 # 인자 확인 if [ -z "$PROJECT" ]; then echo "사용법: $0 <프로젝트명>" exit 1 fi log() { echo "[$(date '+%H:%M:%S')] $1"; } # 개발 패키지 설치 log "시스템 패키지 설치 중..." sudo apt update -q sudo apt install -y git python3-pip python3-venv python3-dev build-essential # 프로젝트 폴더 생성 if [ -d "$PROJECT" ]; then echo "이미 존재: $PROJECT" exit 1 fi mkdir "$PROJECT" cd "$PROJECT" # 가상환경 생성 & 활성화 log "가상환경 생성 중..." $PYTHON -m venv venv source venv/bin/activate # 기본 패키지 설치 pip install --upgrade pip -q pip install requests python-dotenv -q # .gitignore 생성 cat > .gitignore << 'EOF' venv/ __pycache__/ *.pyc .env *.log .DS_Store EOF # README 생성 echo "# $PROJECT" > README.md # git 초기화 git init git add . git commit -m "initial commit" log "✓ $PROJECT 세팅 완료! 'source $PROJECT/venv/bin/activate' 로 활성화"
$ chmod +x setup_dev.sh $ ./setup_dev.sh mydjango [10:23:01] 시스템 패키지 설치 중... [10:23:15] 가상환경 생성 중... [10:23:18] ✓ mydjango 세팅 완료!
grep · sed · awk — 텍스트 처리 3대장
$ grep -rn "import django" . # 재귀+줄번호 $ grep -i "error" app.log # 대소문자 무시 $ grep -v "DEBUG" app.log # 제외 $ grep -c "ERROR" app.log # 개수만 $ grep -A3 "Exception" app.log # 매칭 후 3줄 더 $ grep -E "error|warning|critical" log # OR $ grep -rn "TODO\|FIXME" --include="*.py" .
# sed — 스트림 에디터 $ sed 's/DEBUG=True/DEBUG=False/g' settings.py $ sed -i 's/localhost/0.0.0.0/g' config.py # 파일 직접 수정 $ sed '/^#/d' config.py # # 주석 줄 삭제 $ sed '/^$/d' file.py # 빈 줄 삭제 $ sed -n '10,20p' file.py # 10~20번 줄만 출력 # awk — 컬럼 처리 $ ps aux | awk '{print $2, $11}' # PID, 명령어 $ df -h | awk '/\/$/ {print $5}' # 루트 파티션 사용률 $ awk -F',' 'NR>1 {sum+=$3} END {print sum}' data.csv $ awk -F':' '{print $1}' /etc/passwd # 계정명 목록
cron — 작업 예약
$ crontab -e # 편집 (처음 실행 시 에디터 선택 → nano 선택) $ crontab -l # 현재 목록 확인 $ crontab -r # 전체 삭제 # 형식: 분 시 일 월 요일 명령어 # * * * * * command # │ │ │ │ └── 요일 (0=일, 1=월 ... 6=토) # │ │ │ └───── 월 (1-12) # │ │ └──────── 일 (1-31) # │ └─────────── 시 (0-23) # └────────────── 분 (0-59) # 예시 0 6 * * * /home/user/venv/bin/python3 /home/user/daily.py */5 * * * * /home/user/scripts/monitor.sh >> /home/user/cron.log 2>&1 0 9 * * 1 /home/user/venv/bin/python3 /home/user/weekly.py 0 0 1 * * /home/user/scripts/backup.sh @reboot /home/user/scripts/startup.sh # 재부팅 시 1회 # cron 서비스 상태 $ sudo systemctl status cron
/home/user/myproject/venv/bin/python3처럼 절대경로를 사용하거나, 스크립트 첫 줄에 source /home/user/myproject/venv/bin/activate를 포함하세요.SSH & 원격 접속
# 기본 접속 $ ssh user@192.168.1.100 $ ssh -i ~/.ssh/mykey.pem ubuntu@54.180.10.95 # SSH 키 생성 (비밀번호 없는 접속) $ ssh-keygen -t ed25519 -C "myemail@gmail.com" # 생성: ~/.ssh/id_ed25519 (비공개), ~/.ssh/id_ed25519.pub (공개) # 공개키 서버에 등록 $ ssh-copy-id user@server # 이제 비밀번호 없이 접속! # ~/.ssh/config 설정 (단축 접속) $ nano ~/.ssh/config Host myserver HostName 54.180.10.95 User ubuntu IdentityFile ~/.ssh/mykey.pem $ ssh myserver # 이제 단축키로 접속 # 파일 전송 $ scp local.py user@server:~/ $ scp -r myproject/ user@server:~/ $ rsync -avz --exclude=venv/ myproject/ user@server:~/myproject/
네트워크 명령어
# IP 확인 $ ip addr | grep "inet " # 로컬 IP $ curl ifconfig.me # 공인 IP $ hostname -I # IP 목록 # 연결 테스트 $ ping -c 4 8.8.8.8 $ ss -tulnp # 열린 포트 목록 $ ss -tulnp | grep ":8000" # 8000번 포트 사용중? # curl — Django API 테스트에 유용 $ curl http://localhost:8000/api/users/ # GET $ curl -X POST \ -H "Content-Type: application/json" \ -H "Authorization: Token abc123" \ -d '{"name":"홍길동","email":"hong@test.com"}' \ http://localhost:8000/api/users/ $ curl -s http://localhost:8000/api/ | python3 -m json.tool
Python — 리눅스에서 시작하는 파이썬
Linux Mint 22.3 XFCE에서 가상환경부터 OOP, subprocess, 데코레이터까지 실전 파이썬을 완성합니다.
Python 설치 & 환경 확인
Linux Mint 22.3에는 Python 3.12.x가 기본 내장되어 있습니다.
$ python3 --version Python 3.12.3 # 필수 패키지 설치 $ sudo apt install python3-pip python3-venv python3-dev # 인터랙티브 셸 (REPL) $ python3 >>> print("Hello, Linux Mint 22.3!") Hello, Linux Mint 22.3! >>> import sys; print(sys.version) 3.12.3 (main, Apr 10 2024, 05:33:47) >>> exit() # 스크립트 실행 방법들 $ python3 main.py $ python3 -c "print('한 줄 실행')" $ python3 -m http.server 8080 # 빠른 파일 서버 $ python3 -i main.py # 실행 후 REPL 유지
가상환경 (venv) 완전 정복
$ mkdir myproject && cd myproject $ python3 -m venv venv $ source venv/bin/activate (venv) user@linuxmint:~/myproject$ (venv) $ pip install --upgrade pip (venv) $ pip install django requests pillow (venv) $ pip freeze > requirements.txt (venv) $ pip install -r requirements.txt (venv) $ deactivate # .gitignore에 추가 $ cat >> .gitignore << 'EOF' venv/ __pycache__/ *.pyc .env EOF
변수 & 자료형
# 기본 자료형 name = "Linux Mint" # str version = 22.3 # float count = 42 # int active = True # bool nothing = None # NoneType # f-string (Python 3.6+, 권장 포맷) print(f"OS: {name} v{version:.1f}") print(f"카운트: {count:,}") # 천 단위 콤마 print(f"{'left':<10}|") # 왼쪽 정렬 10칸 # 문자열 메서드 s = " Hello, Linux! " print(s.strip()) # 공백 제거 print(s.lower().strip()) print(s.replace("Linux", "Mint")) print(s.split(",")) # 분리 print(",".join(["a", "b", "c"])) # 합치기 print("linux" in s.lower()) # True # 형변환 print(int("42"), float("3.14"), str(100)) print(bool(0), bool(""), bool([])) # 모두 False print(bool(1), bool("a"), bool([0])) # 모두 True
조건문 & 반복문
# match-case (Python 3.10+) status = 404 match status: case 200: print("OK") case 404: print("Not Found") case _: print("Other") # enumerate + zip names = ["Alice", "Bob", "Charlie"] scores = [85, 92, 78] for i, (name, score) in enumerate(zip(names, scores), 1): print(f"{i}. {name}: {score}점") # 컴프리헨션 squares = [x**2 for x in range(10) if x % 2 == 0] score_map = {n: s for n, s in zip(names, scores)} unique = {n.upper() for n in names} # set gen = (sum(x**2 for x in range(1000000))) # 제너레이터 # sorted / filter / map nums = [3, 1, 4, 1, 5, 9] print(sorted(nums, reverse=True)) print(list(filter(lambda x: x > 3, nums))) print(list(map(lambda x: x * 2, nums)))
함수 & 모듈 (os, pathlib, sys)
import os, sys from pathlib import Path from datetime import datetime def greet(name: str, greeting: str = "안녕하세요") -> str: """간단한 인사 함수.""" return f"{greeting}, {name}님!" def stats(*args: float) -> dict: return {"합계": sum(args), "평균": sum(args)/len(args)} def create_user(**kwargs) -> dict: return {"created_at": datetime.now().isoformat(), **kwargs} # os 모듈 — Linux에서 핵심 print(os.getcwd()) # 현재 디렉터리 print(os.listdir(".")) # 파일 목록 os.makedirs("output/logs", exist_ok=True) print(os.environ.get("HOME")) # 환경변수 print(os.cpu_count()) # CPU 코어 수 # pathlib — 현대적 경로 처리 p = Path("~/projects").expanduser() for f in p.rglob("*.py"): print(f.stem, f.stat().st_size) path = Path("/home/user/main.py") print(path.parent, path.stem, path.suffix) new = path.with_suffix(".txt") # sys print(sys.argv) # 명령줄 인자 print(sys.platform) # 'linux' sys.exit(0) # 종료
파일 입출력 & pip 패키지
import json, csv from pathlib import Path # 텍스트 Path("out.txt").write_text("Hello\n두 번째 줄\n", encoding="utf-8") content = Path("out.txt").read_text(encoding="utf-8") lines = content.splitlines() # JSON data = {"name": "홍길동", "scores": [85, 92], "active": True} Path("data.json").write_text( json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8" ) loaded = json.loads(Path("data.json").read_text()) # CSV with open("students.csv", "w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=["name", "score"]) writer.writeheader() writer.writerows([{"name": "Alice", "score": 95}]) # pip 명령 # pip install 패키지 설치 # pip install -r requirements 일괄 설치 # pip freeze > requirements.txt 목록 저장 # pip list 설치 목록 # pip show 패키지 상세 정보 # pip uninstall 패키지 제거
리스트 · 딕셔너리 심화
from collections import defaultdict, Counter, deque # Counter — 빈도 분석 words = ["apple", "banana", "apple", "cherry", "apple"] cnt = Counter(words) print(cnt.most_common(2)) # [('apple', 3), ('banana', 1)] # defaultdict — KeyError 없는 딕셔너리 groups: defaultdict = defaultdict(list) for team, name in [("A","Alice"),("B","Bob"),("A","Carol")]: groups[team].append(name) # 딕셔너리 심화 config = {"debug": True, "port": 8000} override = {"port": 9000, "workers": 4} merged = config | override # Python 3.9+ 병합 # 구조분해 할당 first, *rest = [1, 2, 3, 4] *head, last = [1, 2, 3, 4] # dict 언패킹으로 함수 호출 def connect(host, port): print(f"{host}:{port}") connect(**config) # 리스트 슬라이싱 nums = [0, 1, 2, 3, 4, 5] print(nums[1:4]) # [1,2,3] print(nums[-3:]) # [3,4,5] 마지막 3개 print(nums[::-1]) # [5,4,3,2,1,0] 역순 print(nums[::2]) # [0,2,4] 2칸 간격
클래스 & 객체지향
from dataclasses import dataclass, field from datetime import datetime class Animal: kingdom = "Animalia" # 클래스 변수 def __init__(self, name: str, age: int): self.name = name self.age = age def speak(self) -> str: return "..." def __repr__(self): return f"Animal({self.name!r})" def __str__(self): return f"{self.name} ({self.age}세)" class Dog(Animal): # 상속 def __init__(self, name, age, breed): super().__init__(name, age) self.breed = breed def speak(self): return f"{self.name}: 멍멍!" # dataclass — 보일러플레이트 자동 생성 @dataclass class User: name: str email: str age: int = 0 tags: list = field(default_factory=list) created_at: str = field( default_factory=lambda: datetime.now().isoformat() ) def is_adult(self) -> bool: return self.age >= 18 dog = Dog("바둑이", 3, "진도") print(dog.speak()) print(isinstance(dog, Animal)) # True user = User("홍길동", "hong@test.com", age=25) print(user, user.is_adult())
예외처리 (try / except)
import logging logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s', handlers=[logging.FileHandler('app.log'), logging.StreamHandler()]) logger = logging.getLogger(__name__) # 기본 구조 try: x = int(input("숫자: ")) result = 100 / x except ValueError: logger.error("숫자가 아닙니다") except ZeroDivisionError: logger.error("0으로 나눌 수 없음") except Exception as e: logger.exception(f"예상치 못한 오류: {e}") else: print(f"결과: {result}") # 예외 없을 때만 finally: print("항상 실행") # DB 연결 종료 등에 사용 # 사용자 정의 예외 class AppError(Exception): def __init__(self, msg: str, code: int = 500): super().__init__(msg) self.code = code class ValidationError(AppError): pass def validate_age(age: int): if not isinstance(age, int): raise TypeError("나이는 정수") if age < 0 or age > 150: raise ValidationError(f"유효하지 않은 나이: {age}", code=400)
subprocess — 파이썬에서 리눅스 명령 실행
import subprocess # 기본 실행 (권장) result = subprocess.run( ["ls", "-la"], capture_output=True, # stdout/stderr 캡처 text=True, # 문자열로 반환 cwd="/home/user" # 실행 디렉터리 ) print(result.stdout) print(result.returncode) # 0=성공 # 실패 시 예외 자동 발생 try: subprocess.run(["git", "pull"], check=True, text=True) except subprocess.CalledProcessError as e: print(f"실패: {e.stderr}") # 셸 명령 (파이프 포함) result = subprocess.run( "ps aux | grep python | awk '{print $2}'", shell=True, capture_output=True, text=True ) # 실용 예시: 배포 자동화 def deploy(branch="main"): cmds = [ ["git", "pull", "origin", branch], ["pip", "install", "-r", "requirements.txt", "-q"], ["python3", "manage.py", "migrate"], ["systemctl", "restart", "myapp"], ] for cmd in cmds: r = subprocess.run(cmd, capture_output=True, text=True) if r.returncode != 0: print(f"실패: {' '.join(cmd)}\n{r.stderr}") return False return True
argparse — CLI 도구 만들기
#!/usr/bin/env python3 """Linux Mint 시스템 정보 출력 도구.""" import argparse, subprocess, os, sys from pathlib import Path def get_os_info() -> dict: result = subprocess.run(["lsb_release", "-a"], capture_output=True, text=True) info = {} for line in result.stdout.splitlines(): if ":" in line: k, v = line.split(":", 1) info[k.strip()] = v.strip() return info def get_memory() -> str: r = subprocess.run(["free", "-h"], capture_output=True, text=True) return r.stdout def count_py_files(path: str) -> int: return len(list(Path(path).rglob("*.py"))) def main(): parser = argparse.ArgumentParser(description="Linux Mint 시스템 정보") parser.add_argument("--os", action="store_true", help="OS 정보") parser.add_argument("--mem", action="store_true", help="메모리 정보") parser.add_argument("--count-py", metavar="PATH", help=".py 파일 수") parser.add_argument("--version", action="version", version="1.0.0") args = parser.parse_args() if args.os: info = get_os_info() print(f"OS: {info.get('Description','?')}") if args.mem: print(get_memory()) if args.count_py: n = count_py_files(args.count_py) print(f".py 파일: {n}개") if not any([args.os, args.mem, args.count_py]): parser.print_help() if __name__ == "__main__": main()
$ chmod +x linuxinfo.py $ ./linuxinfo.py --os OS: Linux Mint 22.3 $ ./linuxinfo.py --mem $ ./linuxinfo.py --count-py ~/projects .py 파일: 142개 $ ./linuxinfo.py --help
데코레이터 & 제너레이터
import time, functools from typing import Callable, Any # ── 데코레이터 ─────────────────────────────── def timer(func: Callable) -> Callable: @functools.wraps(func) def wrapper(*args, **kwargs): t = time.perf_counter() result = func(*args, **kwargs) print(f"[timer] {func.__name__}: {time.perf_counter()-t:.4f}s") return result return wrapper def retry(max_attempts=3, delay=1.0): def decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): for i in range(max_attempts): try: return func(*args, **kwargs) except Exception as e: print(f"시도 {i+1}/{max_attempts}: {e}") if i < max_attempts-1: time.sleep(delay) raise RuntimeError("최대 재시도 초과") return wrapper return decorator @timer def slow_task(n): time.sleep(0.1); return sum(range(n)) @retry(max_attempts=3, delay=0.5) def fetch_data(url): pass # 실제론 requests.get(url) # ── 제너레이터 ─────────────────────────────── def fibonacci(): a, b = 0, 1 while True: yield a a, b = b, a + b def read_large_log(path: str): """대용량 로그를 한 줄씩 읽는 제너레이터.""" with open(path, encoding="utf-8") as f: for line in f: yield line.strip() # 사용 fib = fibonacci() first10 = [next(fib) for _ in range(10)] print(first10) # [0,1,1,2,3,5,8,13,21,34] # 에러 라인만 추출 (메모리 효율) error_lines = [ l for l in read_large_log("app.log") if "ERROR" in l ]
정규표현식 (re 모듈)
import re text = "이메일: hong@example.com, 전화: 010-1234-5678, 날짜: 2026-05-06" # 기본 함수 re.search(r'\d+', text) # 첫 매칭 print(re.findall(r'\d+', text)) # 모든 숫자 print(re.sub(r'\d{4}', '****', text)) # 4자리 숫자 마스킹 # 이메일 추출 email_pat = r'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}' print(re.findall(email_pat, text)) # ['hong@example.com'] # 한국 전화번호 phone_pat = r'0\d{1,2}[-.]?\d{3,4}[-.]?\d{4}' print(re.findall(phone_pat, text)) # 날짜 추출 + 그룹 m = re.search(r'(\d{4})-(\d{2})-(\d{2})', text) if m: year, month, day = m.groups() print(f"{year}년 {month}월 {day}일") # 컴파일 (반복 사용 시 성능 향상) pat = re.compile(r'\d{4}-\d{2}-\d{2}') print(pat.findall("2026-01-01, 2026-12-31")) # 핵심 패턴 # . 아무문자 \d 숫자 \w 문자/숫자/_ \s 공백 # * 0회이상 + 1회이상 ? 0또는1 # ^ 시작 $ 끝 [] 문자클래스 () 그룹 # {n,m} 반복횟수 | OR (?:) 비캡처그룹
C언어 — GCC로 배우는 C 기초
Linux Mint 22.3 XFCE에서 GCC로 컴파일하고 실행하는 전체 흐름부터 동적 메모리, 파일 I/O까지 다룹니다.
GCC 설치 & Hello World
C는 컴파일 언어입니다. 소스코드를 기계어로 번역하는 GCC가 필요합니다.
# hello.py print("Hello!") # $ python3 hello.py # 바로 실행됨
/* hello.c */ #include <stdio.h> int main(void) { printf("Hello!\n"); return 0; }
$ sudo apt install build-essential $ gcc --version gcc (Ubuntu 13.2.0-23ubuntu4) 13.2.0 # 컴파일 & 실행 $ gcc hello.c -o hello # -o : 출력 파일명 $ ./hello Hello! # 권장 옵션으로 컴파일 $ gcc -Wall -Wextra -std=c11 -g hello.c -o hello # -Wall 모든 경고 표시 # -Wextra 추가 경고 # -std=c11 C11 표준 # -g 디버그 정보 (gdb 사용 시) # -O2 최적화 레벨 2 (배포용) # 컴파일 에러 메시지 읽기 hello.c:4:5: error: implicit declaration of function 'prntf' 파일:줄:열: 에러/경고 내용
컴파일 4단계 이해
$ gcc -E hello.c -o hello.i # 전처리만 $ wc -l hello.i 849 hello.i # stdio.h 포함 후 849줄! $ gcc -S hello.c -o hello.s # 어셈블리 생성 $ gcc -c hello.c -o hello.o # 오브젝트 파일 $ gcc hello.o -o hello # 링크 # 여러 파일 컴파일 $ gcc main.c utils.c math.c -o myapp -lm # -lm : math 라이브러리 링크 (sin, cos, sqrt 사용 시)
변수 & 자료형 & 연산자
#include <stdio.h> #include <stdbool.h> // bool (C99+) #include <stdint.h> // int32_t 등 고정 크기 int main(void) { // 정수형 char c = 'A'; // 1바이트 int i = 2147483647; // 4바이트 long l = 9999999999L; // 8바이트 unsigned int u = 4294967295U; // 고정 크기 정수 (권장) int32_t a32 = 2147483647; uint64_t u64 = 18446744073709551615ULL; // 실수형 float f = 3.14f; // 4바이트, 7자리 double d = 3.14159265358979; // 8바이트, 15자리 (권장) // bool (stdbool.h) bool active = true, done = false; // 문자열 = char 배열 + \0 char name[] = "Linux Mint"; // 11바이트 (10글자 + \0) char buf[64]; // 64바이트 버퍼 // printf 포맷 printf("int: %d\n", i); printf("double: %.4f\n", d); printf("char: %c\n", c); printf("string: %s\n", name); printf("hex: %x\n", i); printf("bool: %s\n", active ? "true" : "false"); printf("sizeof(int)=%zu sizeof(double)=%zu\n", sizeof(int), sizeof(double)); // 연산자 int x=10, y=3; printf("%d/%d=%d (정수)\n", x, y, x/y); // 3 printf("%.2f (실수)\n", (double)x/y); // 3.33 printf("%d%%3=%d\n", x, x%y); // 1 // 비트 연산 (시스템 프로그래밍 핵심) printf("AND:%d OR:%d XOR:%d SHL:%d\n", 0b1010&0b1100, 0b1010|0b1100, 0b1010^0b1100, 1<<3); // 8 14 6 8 return 0; }
조건문 & 반복문
#include <stdio.h> int main(void) { // if / else if / else int score = 85; if (score >= 90) printf("A\n"); else if (score >= 80) printf("B\n"); // 출력됨 else if (score >= 70) printf("C\n"); else printf("F\n"); // 삼항 연산자 const char *grade = (score >= 60) ? "합격" : "불합격"; // switch (정수/char만 가능, break 필수!) char op = '+'; switch (op) { case '+': printf("더하기\n"); break; case '-': printf("빼기\n"); break; default: printf("기타\n"); } // for (초기화; 조건; 증감) for (int i = 0; i < 5; i++) printf("%d ", i); // 0 1 2 3 4 printf("\n"); // 역순 for for (int i = 10; i >= 0; i -= 2) printf("%d ", i); // 10 8 6 4 2 0 // while int n = 1; while (n <= 100) n *= 2; // 128 // do-while (최소 1회 실행) int input; do { printf("1-10 입력: "); scanf("%d", &input); } while (input < 1 || input > 10); // break / continue for (int i = 0; i < 20; i++) { if (i % 2 == 0) continue; // 짝수 건너뜀 if (i > 10) break; // 10 초과 중단 printf("%d ", i); // 1 3 5 7 9 } return 0; }
함수 & 배열
#include <stdio.h> // 함수 선언 (프로토타입) — main 위에! int add(int a, int b); double average(int arr[], int size); void print_array(int arr[], int size); void bubble_sort(int arr[], int size); int main(void) { printf("3+5=%d\n", add(3, 5)); int scores[] = {85, 92, 78, 96, 88}; int size = sizeof(scores) / sizeof(scores[0]); print_array(scores, size); printf("평균: %.1f\n", average(scores, size)); bubble_sort(scores, size); print_array(scores, size); // 정렬 후 // 2차원 배열 int matrix[3][3] = { {1,2,3},{4,5,6},{7,8,9} }; for (int i=0; i<3; i++) { for (int j=0; j<3; j++) printf("%3d", matrix[i][j]); printf("\n"); } return 0; } int add(int a, int b) { return a + b; } double average(int arr[], int size) { int sum = 0; for (int i=0; ireturn (double)sum / size; } void print_array(int arr[], int size) { printf("["); for (int i=0; i printf("%d%s", arr[i], i 1?",":""); printf("]\n"); } void bubble_sort(int arr[], int size) { for (int i=0; i 1; i++) for (int j=0; j 1; j++) if (arr[j] > arr[j+1]) { int t=arr[j]; arr[j]=arr[j+1]; arr[j+1]=t; } }
포인터 기초
포인터는 C의 가장 강력한 기능입니다. 변수의 메모리 주소를 저장하는 변수입니다.
#include <stdio.h> void swap(int *a, int *b); int sum_array(const int *arr, int size); int main(void) { int x = 10; int *p = &x; // p는 x의 주소를 저장 printf("x의 값: %d\n", x); printf("x의 주소: %p\n", (void*)&x); printf("p의 값: %p\n", (void*)p); // 같은 주소 printf("*p (역참조): %d\n", *p); // 10 *p = 99; // 포인터로 값 변경 printf("변경 후 x: %d\n", x); // 99 // 배열과 포인터의 관계 int arr[] = {10, 20, 30, 40}; int *q = arr; // 배열명 = 첫 원소 주소 printf("%d %d %d %d\n", *q, *(q+1), *(q+2), *(q+3)); printf("%d %d %d %d\n", q[0], q[1], q[2], q[3]); // 동일 // swap: 포인터 없이는 불가능! int a=5, b=10; printf("전: a=%d b=%d\n", a, b); swap(&a, &b); printf("후: a=%d b=%d\n", a, b); // a=10 b=5 // const 포인터: 내용 변경 불가 const int arr2[] = {1,2,3}; printf("sum=%d\n", sum_array(arr2, 3)); return 0; } void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } int sum_array(const int *arr, int size) { int sum = 0; for (int i=0; ireturn sum; }
&변수 → 주소 얻기 / *포인터 → 주소의 값(역참조) / int *p → p는 int를 가리키는 포인터 / 배열명은 곧 첫 원소의 주소구조체 (struct)
여러 자료형을 묶어 하나의 새로운 타입을 만듭니다. 파이썬의 dataclass와 유사합니다.
#include <stdio.h> #include <string.h> // 구조체 정의 + typedef typedef struct { char name[50]; int age; float gpa; } Student; typedef struct { double x, y; } Point; double distance(Point a, Point b); void print_student(const Student *s); int main(void) { // 초기화 방법 1: 선언과 동시에 Student s1 = {"홍길동", 20, 3.8f}; // 초기화 방법 2: 지정 초기화 (C99+) Student s2 = {.name="김철수", .age=22, .gpa=4.0f}; // 멤버 접근 (. 연산자) printf("%s: %d살, GPA %.1f\n", s1.name, s1.age, s1.gpa); // 포인터로 접근 (-> 연산자) Student *ps = &s2; printf("%s: %d살\n", ps->name, ps->age); // 구조체 배열 Student class_a[] = { {"Alice", 20, 3.9f}, {"Bob", 21, 3.5f}, {"Carol", 19, 4.0f}, }; int n = sizeof(class_a) / sizeof(class_a[0]); for (int i=0; iprint_student(&class_a[i]); // 중첩 구조체 Point p1 = {0.0, 0.0}, p2 = {3.0, 4.0}; printf("거리: %.2f\n", distance(p1, p2)); // 5.00 return 0; } void print_student(const Student *s) { printf("%-10s %2d살 GPA:%.1f\n", s->name, s->age, s->gpa); } #include <math.h> double distance(Point a, Point b) { return sqrt((b.x-a.x)*(b.x-a.x) + (b.y-a.y)*(b.y-a.y)); } // 컴파일: gcc struct.c -o struct -lm
동적 메모리 (malloc / free)
런타임에 필요한 만큼 메모리를 할당하고 해제합니다. 힙(Heap) 메모리를 직접 관리합니다.
#include <stdio.h> #include <stdlib.h> // malloc, calloc, realloc, free #include <string.h> // memset, memcpy int main(void) { int n; printf("배열 크기: "); scanf("%d", &n); // malloc: 초기화 없이 n*4바이트 할당 int *arr = (int *)malloc(n * sizeof(int)); if (!arr) { // 실패 시 NULL 반환 fprintf(stderr, "메모리 할당 실패\n"); return 1; } // calloc: 0으로 초기화하며 할당 int *zeros = (int *)calloc(n, sizeof(int)); for (int i=0; i1) * 10; printf("원본: "); for (int i=0; i printf("%d ", arr[i]); printf("\n"); // realloc: 크기 변경 int new_n = n * 2; int *tmp = (int *)realloc(arr, new_n * sizeof(int)); if (!tmp) { free(arr); return 1; } arr = tmp; for (int i=n; i 0; // 동적 구조체 typedef struct { int id; char name[50]; } Node; Node *node = (Node *)malloc(sizeof(Node)); node->id = 1; snprintf(node->name, 50, "홍길동"); printf("Node: %d %s\n", node->id, node->name); // 반드시 해제! (메모리 누수 방지) free(arr); free(zeros); free(node); arr = zeros = node = NULL; // 댕글링 포인터 방지 return 0; }
valgrind --leak-check=full ./myapp으로 누수를 확인할 수 있습니다 (sudo apt install valgrind).파일 입출력 (FILE*)
#include <stdio.h> #include <stdlib.h> int main(void) { // ── 텍스트 파일 쓰기 ───────────────── FILE *fp = fopen("data.txt", "w"); // w: 쓰기, r: 읽기, a: 추가 if (!fp) { perror("fopen"); return 1; } fprintf(fp, "이름,나이,점수\n"); fprintf(fp, "홍길동,25,92.5\n"); fprintf(fp, "김철수,22,88.0\n"); fclose(fp); // ── 텍스트 파일 읽기 (줄 단위) ─────── fp = fopen("data.txt", "r"); if (!fp) { perror("fopen"); return 1; } char line[256]; while (fgets(line, sizeof(line), fp)) { line[strcspn(line, "\n")] = '\0'; // \n 제거 printf("줄: %s\n", line); } fclose(fp); // ── 파일 크기 확인 ─────────────────── fp = fopen("data.txt", "r"); fseek(fp, 0, SEEK_END); long size = ftell(fp); rewind(fp); printf("파일 크기: %ld바이트\n", size); // ── 파일 전체 읽기 (동적 할당) ─────── char *content = (char*)malloc(size + 1); fread(content, 1, size, fp); content[size] = '\0'; printf("내용:\n%s\n", content); free(content); fclose(fp); // 파일 모드 // "r" 읽기 (파일 없으면 NULL) // "w" 쓰기 (없으면 생성, 있으면 초기화) // "a" 추가 (없으면 생성) // "r+" 읽기+쓰기 // "rb" 바이너리 읽기 return 0; } #include <string.h>
표준 라이브러리 활용
#include <stdio.h> #include <stdlib.h> // atoi, atof, rand, qsort, abs #include <string.h> // strlen, strcpy, strcat, strcmp, strstr #include <math.h> // sqrt, pow, sin, cos, floor, ceil #include <time.h> // time, clock #include <ctype.h> // isdigit, isalpha, toupper, tolower int cmp_int(const void *a, const void *b); // qsort용 int main(void) { // ── string.h ──────────────────────── char s1[50] = "Hello"; char s2[] = " Linux"; printf("길이: %zu\n", strlen(s1)); // 5 strcat(s1, s2); printf("합치기: %s\n", s1); // Hello Linux printf("비교: %d\n", strcmp("a", "b")); // 음수 (a printf("검색: %s\n", strstr(s1, "Linux")); // Linux // ── math.h ────────────────────────── printf("sqrt(16)=%.1f\n", sqrt(16.0)); // 4.0 printf("pow(2,8)=%.0f\n", pow(2.0, 8.0)); // 256 printf("ceil(3.2)=%.0f\n", ceil(3.2)); // 4 printf("floor(3.9)=%.0f\n", floor(3.9)); // 3 // ── stdlib.h ──────────────────────── printf("atoi: %d\n", atoi("42")); // 42 printf("atof: %.2f\n", atof("3.14")); // 3.14 printf("abs: %d\n", abs(-15)); // 15 // rand & srand (난수) srand(time(NULL)); for (int i=0; i<5; i++) printf("%d ", rand() % 100); // 0~99 난수 // qsort (범용 정렬) int arr[] = {5,2,8,1,9,3}; qsort(arr, 6, sizeof(int), cmp_int); for (int i=0; i<6; i++) printf("%d ", arr[i]); // ── ctype.h ───────────────────────── char ch = 'a'; printf("\n%c→%c\n", ch, toupper(ch)); // a→A printf("isdigit('5')=%d\n", isdigit('5')); // 非0 return 0; } int cmp_int(const void *a, const void *b) { return (*(int*)a) - (*(int*)b); } // 컴파일: gcc stdlib_demo.c -o stdlib_demo -lm
전처리기 (#define / #ifdef)
#include <stdio.h> // ── 상수 정의 ──────────────────────────────── #define MAX_SIZE 100 #define PI 3.14159265 #define APP_NAME "LinuxLab" #define VERSION "1.0.0" // ── 함수형 매크로 ──────────────────────────── #define SQUARE(x) ((x) * (x)) // 괄호 중요! #define MAX(a,b) ((a)>(b) ? (a) : (b)) #define MIN(a,b) ((a)<(b) ? (a) : (b)) #define ABS(x) ((x)>=0 ? (x) : -(x)) #define SWAP(t,a,b) do{t _t=a; a=b; b=_t;}while(0) // ── 헤더 가드 (헤더파일에서 이중 포함 방지) ── #ifndef MYLIB_H #define MYLIB_H /* 헤더 내용 */ #endif // ── 조건부 컴파일 ──────────────────────────── #define DEBUG // 이 줄을 주석처리하면 디버그 비활성화 #ifdef DEBUG #define LOG(msg) printf("[DEBUG] %s:%d: %s\n", __FILE__, __LINE__, msg) #else #define LOG(msg) // 비어있음 → 릴리즈 빌드에서 사라짐 #endif #ifdef __linux__ #define PLATFORM "Linux" #elif defined(_WIN32) #define PLATFORM "Windows" #else #define PLATFORM "Unknown" #endif int main(void) { printf("%s v%s\n", APP_NAME, VERSION); printf("플랫폼: %s\n", PLATFORM); printf("SQUARE(5)=%d\n", SQUARE(5)); // 25 printf("MAX(3,7)=%d\n", MAX(3, 7)); // 7 LOG("프로그램 시작"); int arr[MAX_SIZE]; for (int i=0; i// gcc로 외부에서 #define 하기 // gcc -DDEBUG -DMAX_SIZE=200 main.c -o main return 0; }
Makefile 기초
여러 파일 프로젝트의 빌드를 자동화합니다. 변경된 파일만 재컴파일해 효율적입니다.
# 변수 정의 CC = gcc CFLAGS = -Wall -Wextra -std=c11 -g LDFLAGS = -lm TARGET = myapp SRCS = main.c utils.c math_utils.c OBJS = $(SRCS:.c=.o) # .c → .o 치환 HEADERS = utils.h math_utils.h # 기본 타겟 all: $(TARGET) # 실행파일 빌드 $(TARGET): $(OBJS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) @echo "✓ 빌드 완료: $(TARGET)" # 오브젝트 파일 빌드 (패턴 규칙) %.o: %.c $(HEADERS) $(CC) $(CFLAGS) -c $< -o $@ # 정리 clean: rm -f $(OBJS) $(TARGET) @echo "정리 완료" # 빌드 후 실행 run: $(TARGET) ./$(TARGET) # 디버그 빌드 debug: CFLAGS += -DDEBUG -O0 debug: $(TARGET) # 릴리즈 빌드 release: CFLAGS := -Wall -O2 -std=c11 release: $(TARGET) # 가짜 타겟 선언 .PHONY: all clean run debug release
$ make # 빌드 gcc -Wall -Wextra -std=c11 -g -c main.c -o main.o gcc -Wall -Wextra -std=c11 -g -c utils.c -o utils.o gcc -Wall -Wextra -std=c11 -g -o myapp main.o utils.o -lm ✓ 빌드 완료: myapp $ make # 변경 없으면 make: 'myapp' is up to date. $ make run # 실행 $ make clean # 정리 $ make debug # 디버그 빌드 $ make -j4 # 4코어 병렬 빌드
$@ = 타겟명, $^ = 모든 의존성, $< = 첫 번째 의존성 (3) @로 시작하는 명령은 출력 안 됨