← DoniKit 웹 앱으로

Shell 코드 스니펫 41종

Windows(PowerShell 5.1·cmd)와 Linux 서버를 오가는 실무 셸 명령 모음입니다. 포트·프로세스·서비스·로그 조회부터 파일 검색·압축·전송·해시 검증, sed·awk 로그 가공까지 — 파괴적 명령은 확인→드라이런→실행 순서로 정리했습니다.

웹 앱에서는 빈칸을 채워 완성된 코드를 바로 복사할 수 있습니다.

포트 점유 프로세스 찾기 → 종료 (netstat · taskkill)

시스템·네트워크 · cmd·PowerShell 공용 (EXE)

# 1) 포트를 물고 있는 PID 찾기 — 마지막 열이 PID
netstat -ano | findstr :8080

# 2) 그 PID가 무슨 프로세스인지 확인
tasklist /FI "PID eq 1234"

# 3) 강제 종료 (/F) — 무엇인지 확인한 뒤에!
taskkill /PID 1234 /F

💡 "이미 사용 중인 포트" 단골 해결 순서. taskkill /F는 강제 종료라 저장 안 된 작업이 날아간다 — 2)로 꼭 확인. PS 한 줄 대안: Get-Process -Id (Get-NetTCPConnection -LocalPort 8080).OwningProcess. 주석(#) 줄은 cmd에선 오류 — 명령 줄만 붙여넣는다.

프로세스 목록·검색·종료 (tasklist · taskkill)

시스템·네트워크 · cmd·PowerShell 공용 (EXE)

# 전체 프로세스 (PID·메모리 포함)
tasklist

# 이름으로 거르기 (/i 대소문자 무시)
tasklist | findstr /i java

# 이름으로 한 번에 종료 — 같은 이름 전부 죽는다!
taskkill /IM java.exe /F

💡 taskkill /IM은 같은 이름 프로세스를 전부 종료한다 — 여러 인스턴스 중 하나만 잡으려면 PID 방식(포트 카드 참조).

프로세스 조회 — 메모리 상위 · 경로 확인 (Get-Process)

시스템·네트워크 · PowerShell 5.1

# 메모리(WS) 많이 쓰는 순 상위 10개
Get-Process | Sort-Object WS -Descending | Select-Object -First 10

# 이름으로 찾기 (와일드카드)
Get-Process java*

# 어느 폴더의 exe인지 경로까지
Get-Process java* | Select-Object Id, ProcessName, Path

💡 WS(Working Set) = 실제 사용 메모리. Path는 권한이 없으면 일부 프로세스에서 비어 나온다(관리자 PS로). 종료는 Stop-Process -Id 1234 -Force.

서비스 상태·중지·시작 (sc · net)

시스템·네트워크 · cmd

rem 상태 확인 — 이름은 services.msc 속성의 "서비스 이름"(표시 이름 아님)
sc query "Tomcat9"

rem 중지 → 시작 (cmd엔 restart가 없어 순서대로)
net stop "Tomcat9"
net start "Tomcat9"

rem 시작 유형 변경 (auto/demand/disabled)
sc config "Tomcat9" start= auto

💡 관리자 콘솔 필요 — "액세스가 거부되었습니다" = 권한 부족. sc config의 "start= auto"는 = 뒤 한 칸 공백이 정식 문법이다(오타 아님).

서비스 조회·재시작 (Get-Service · Restart-Service)

시스템·네트워크 · PowerShell 5.1

# 상태 확인 (-Name은 서비스 이름 · 표시 이름은 -DisplayName)
Get-Service -Name "Tomcat9"

# 재시작 한 번에
Restart-Service -Name "Tomcat9"

# 실행 중인 서비스만 보기
Get-Service | Where-Object Status -eq 'Running'

💡 관리자 PowerShell 필요. 다른 서비스가 물려 있어 안 멈추면 -Force. 이름을 모르면 Get-Service *tomcat* 와일드카드로 찾는다.

환경변수 확인·설정 (set · setx)

시스템·네트워크 · cmd

rem 값 확인
echo %JAVA_HOME%

rem 전체 목록 (앞 글자로 거르기: set JAVA)
set

rem 현재 창에서만 설정 (창 닫으면 사라짐)
set JAVA_HOME=C:\dev\jdk-17

rem 영구 설정 — 레지스트리 저장, "새로 여는" 창부터 적용
setx JAVA_HOME "C:\dev\jdk-17"

💡 setx는 지금 떠 있는 창에는 적용 안 되고(새 콘솔부터) 값이 1024자에서 잘리는 함정이 있다 — 긴 PATH 편집은 GUI(sysdm.cpl)가 안전. set은 = 양옆에 공백을 넣으면 공백까지 값이 된다.

환경변수 확인·설정 ($env: · SetEnvironmentVariable)

시스템·네트워크 · PowerShell 5.1

# 값 확인 · 전체 목록
$env:JAVA_HOME
Get-ChildItem env: | Sort-Object Name

# 현재 세션에서만 설정
$env:JAVA_HOME = "C:\dev\jdk-17"

# 영구 설정 (User 범위 — 새 창부터 적용. 시스템 전체는 "Machine" + 관리자)
[Environment]::SetEnvironmentVariable("JAVA_HOME", "C:\dev\jdk-17", "User")

💡 $env:는 프로세스 범위라 창을 닫으면 사라진다. 영구 설정 후에도 이미 떠 있는 콘솔·IDE는 옛 값을 본다 — 재시작해야 반영.

네트워크 진단 — 내 IP · ping · 경로 · DNS

시스템·네트워크 · cmd·PowerShell 공용 (EXE)

# 내 IP·게이트웨이·DNS 확인
ipconfig /all

# 응답 확인
ping 192.168.0.10

# 어느 구간에서 끊기는지 추적
tracert 192.168.0.10

# 이 이름이 어떤 IP로 풀리는지 (DNS)
nslookup 192.168.0.10

💡 ping 실패 ≠ 서버 다운 — 방화벽이 ICMP만 막는 경우가 흔하다. 서비스 생사는 포트로 확인(Test-NetConnection 카드). DNS 캐시가 의심되면 ipconfig /flushdns.

포트 열림 확인 — telnet 대체 (Test-NetConnection)

시스템·네트워크 · PowerShell 5.1

# 특정 포트가 열려 있는지 — 결과의 TcpTestSucceeded : True = 열림
Test-NetConnection 192.168.0.10 -Port 8080

# True/False만 (스크립트용)
Test-NetConnection 192.168.0.10 -Port 8080 -InformationLevel Quiet

💡 telnet 클라이언트는 기본 미설치(폐쇄망에선 설치도 어렵다) — 포트 확인은 이걸로. ping은 되는데 포트만 닫혀 있다 = 방화벽 또는 서비스 문제로 분리 진단.

파일 내용 검색 (findstr) — grep 대체

파일·저장 · cmd·PowerShell 공용 (EXE)

# 하위 폴더까지(/s) 대소문자 무시(/i) 줄번호(/n) 검색
findstr /s /i /n "OutOfMemoryError" *.log

# 여러 확장자에서
findstr /s /i /n "OutOfMemoryError" *.java *.xml

# 들어 있는 파일명만 (/m)
findstr /s /i /m "OutOfMemoryError" *.*

💡 공백 포함 문구는 /c:"검색 문구"로 — 그냥 따옴표만 쓰면 단어별 OR로 갈라진다. UTF-8 한글이 안 잡히면 인코딩 문제이니 Select-String 카드(PS)로.

파일 내용 검색 (Select-String) — 정규식·앞뒤 줄

파일·저장 · PowerShell 5.1

# 하위 폴더 전체 .log에서 검색 (파일:줄번호:내용)
Get-ChildItem -Recurse -Filter *.log | Select-String -Pattern "OutOfMemoryError"

# 앞뒤 2줄 같이 보기 (grep -C 2)
Select-String -Path .\app.log -Pattern "OutOfMemoryError" -Context 2,2

# 매칭 줄 수만
(Select-String -Path .\app.log -Pattern "OutOfMemoryError").Count

💡 -Pattern은 기본이 정규식 — 괄호·대괄호 같은 특수문자를 글자 그대로 찾으려면 -SimpleMatch. UTF-8 한글이 깨지면 Get-Content -Encoding UTF8 파이프로 우회.

로그 실시간 보기 — tail -f (Get-Content -Wait)

파일·저장 · PowerShell 5.1

# 마지막 200줄 보여주고 이어서 실시간 출력 (중지: Ctrl+C)
Get-Content "C:\logs\app.log" -Tail 200 -Wait

# 한글 깨지면 인코딩 지정
Get-Content "C:\logs\app.log" -Tail 200 -Wait -Encoding UTF8

💡 cmd엔 tail이 없다(type은 전체 출력이라 큰 로그에 부적합) — PS 5.1 내장이라 폐쇄망 서버에서도 바로 된다. 특정 문구만 보려면 뒤에 | Select-String "ERROR" 를 잇는다.

N일 지난 파일 일괄 삭제 (forfiles) — 드라이런 먼저

파일·저장 · cmd

rem 1) 대상 확인 — echo는 지우지 않고 경로만 출력 (드라이런)
forfiles /p "C:\logs" /s /m *.log /d -30 /c "cmd /c echo @path"

rem 2) 확인했으면 echo → del 로 바꿔 실행 (파괴적!)
forfiles /p "C:\logs" /s /m *.log /d -30 /c "cmd /c del @path"

💡 /d -30 = 수정일이 30일 이전. 휴지통을 거치지 않고 지워진다 — 1) 드라이런을 생략하지 말 것. 대상이 0건이면 오류 코드로 끝나니 배치 errorlevel 판정 주의.

N일 지난 파일 일괄 삭제 (Get-ChildItem + Remove-Item)

파일·저장 · PowerShell 5.1

# 1) 대상 미리 보기 (드라이런)
Get-ChildItem "C:\logs" -Recurse -Filter *.log |
  Where-Object LastWriteTime -lt (Get-Date).AddDays(-30)

# 2) 확인 후 삭제 — -WhatIf를 붙이면 "지웠다 치고" 시뮬레이션만
Get-ChildItem "C:\logs" -Recurse -Filter *.log |
  Where-Object LastWriteTime -lt (Get-Date).AddDays(-30) |
  Remove-Item -Force

💡 휴지통 없이 지워진다 — 1) 미리 보기와 Remove-Item -WhatIf로 이중 확인. 줄 끝 파이프(|)가 다음 줄로 잇는 PS 표준 이어쓰기다.

대량 복사·백업 (robocopy) — 재시도·로그 내장

파일·저장 · cmd·PowerShell 공용 (EXE)

# 추가·갱신만 (대상의 여분 파일은 그대로 둠)
robocopy "C:\deploy\app" "D:\backup\app" /E /R:1 /W:1

# 미러 백업 — 원본과 똑같이. 대상의 여분 파일이 삭제된다!
robocopy "C:\deploy\app" "D:\backup\app" /MIR /R:1 /W:1 /LOG:robocopy.log

# 드라이런 (실제 복사 없이 목록만)
robocopy "C:\deploy\app" "D:\backup\app" /MIR /L

💡 xcopy보다 안전·빠름(재시도 /R·대기 /W·로그·이어받기). /MIR은 대상 쪽 파일을 지우는 파괴적 옵션 — /L 드라이런 먼저. 종료코드 0~7이 성공 계열(1=복사함)이라 배치의 "if errorlevel 1" 판정은 오판.

압축·해제 (Compress-Archive · Expand-Archive)

파일·저장 · PowerShell 5.1

# 폴더 → zip (있으면 덮어쓰기 -Force)
Compress-Archive -Path "C:\deploy\app" -DestinationPath "C:\temp\backup.zip" -Force

# 여러 대상 묶기
Compress-Archive -Path .\logs, .\config.xml -DestinationPath "C:\temp\backup.zip" -Force

# 해제
Expand-Archive -Path "C:\temp\backup.zip" -DestinationPath "C:\temp\restore" -Force

💡 PS 5.1 내장이지만 한계가 있다 — 2GB 넘는 파일과 한글 파일명 인코딩에 약함(다른 툴에서 풀 때 이름이 깨질 수 있음). 대용량·호환이 중요하면 tar 카드.

압축·해제 (tar) — Windows 10+ 내장

파일·저장 · cmd·PowerShell 공용 (Windows 10 1803+)

# 폴더 → zip (-a: 확장자로 형식 자동 결정)
tar -acf backup.zip app

# 내용 목록 확인
tar -tf backup.zip

# 해제 (-C 대상 폴더는 미리 만들어 두어야 함)
tar -xf backup.zip -C restore

💡 Windows 10 1803부터 내장(bsdtar) — 구형 서버(2012 등)엔 없다. zip·tar.gz 둘 다 다룬다. 경로에 공백이 있으면 따옴표로 감싼다.

파일 해시 (certutil) — 반입 파일 무결성 검증

파일·저장 · cmd·PowerShell 공용 (EXE)

# SHA-256 (기본 권장)
certutil -hashfile "C:\deploy\app.war" SHA256

# 구형 대조표가 MD5면
certutil -hashfile "C:\deploy\app.war" MD5

💡 폐쇄망 반입 파일이 원본과 같은지 확인하는 표준 절차 — 반출 전 해시를 적어 와서 대조한다. 두 값 비교는 DoffKit 해시 탭에 붙여넣으면 편하다.

파일 해시 (Get-FileHash) — 폴더 일괄·CSV

파일·저장 · PowerShell 5.1

# SHA-256 (기본값)
Get-FileHash "C:\deploy\app.war"

# 폴더 전체 해시 목록 (반입 검증 대조표 만들기)
Get-ChildItem "C:\deploy" -File -Recurse | Get-FileHash | Format-Table Hash, Path

# CSV로 저장
Get-ChildItem "C:\deploy" -File -Recurse | Get-FileHash | Export-Csv hashes.csv -NoTypeInformation

💡 MD5가 필요하면 -Algorithm MD5. PS 5.1의 Export-Csv는 UTF-16으로 저장된다 — 다른 도구가 읽을 파일이면 -Encoding UTF8을 붙인다.

HTTP 호출 (curl.exe) — PS에선 꼭 .exe까지

HTTP 클라 · cmd·PowerShell 공용 (Windows 10 1803+)

# GET (응답 헤더까지 보려면 -i)
curl.exe -s http://localhost:8080/api/users

# POST JSON — cmd는 \" 이스케이프 필요
curl.exe -s -X POST http://localhost:8080/api/users -H "Content-Type: application/json" -d "{\"name\":\"kim\"}"

# 파일 다운로드 (-L 리다이렉트 따라감)
curl.exe -L -o result.json http://localhost:8080/api/export

💡 PS에서 curl만 치면 Invoke-WebRequest 별칭이 잡혀 인자 체계가 완전히 다르다 — curl.exe로 명시하는 게 함정 회피. PS에선 JSON 본문을 작은따옴표로 감싸면 이스케이프가 필요 없다: -d '{"name":"kim"}'

HTTP 호출 (Invoke-RestMethod) — JSON을 객체로

HTTP 클라 · PowerShell 5.1

# GET — JSON 응답이 바로 객체가 된다
Invoke-RestMethod "http://localhost:8080/api/users"

# 필드 뽑기·필터도 파이프로
Invoke-RestMethod "http://localhost:8080/api/users" | Where-Object age -ge 20 | Select-Object name, age

# POST JSON
Invoke-RestMethod "http://localhost:8080/api/users" -Method Post -ContentType "application/json; charset=utf-8" -Body '{"name":"kim","age":30}'

💡 PS 5.1은 응답에 charset이 없으면 한글이 깨질 수 있다(기본 해석이 구식) — 서버에 charset=utf-8을 요청하거나 .http 탭·curl.exe로 우회. 인증 헤더는 -Headers @{ Authorization = "Bearer 토큰" }.

포트 점유 프로세스 찾기 → 종료 (ss · lsof · kill)

시스템·네트워크 · Linux 공용

# 1) 포트를 물고 있는 프로세스 찾기 (ss: 최신 · netstat: 구버전 서버)
ss -tulpn | grep :8080
netstat -tulpn | grep :8080

# 2) 열려 있는 소켓으로 확인 (root면 남의 프로세스까지 보인다)
sudo lsof -i :8080

# 3) 확인한 PID 종료 — 먼저 정상 종료, 안 죽으면 강제(-9)
kill 1234
kill -9 1234

💡 "이미 사용 중인 포트" 단골 해결 순서. ss 출력 마지막 열 users:(("java",pid=1234,...))가 PID다. kill -9는 종료 처리(파일 flush·셧다운 훅)를 건너뛰니 kill 먼저 시도. ss가 없는 구형 서버는 netstat(net-tools 패키지).

프로세스 검색·종료 (ps -ef · pkill)

시스템·네트워크 · Linux 공용

# 전체 프로세스 (부모 PID·실행 명령줄까지)
ps -ef

# 이름으로 찾기 — grep 자신이 걸리는 걸 제외
ps -ef | grep java | grep -v grep

# 메모리 많이 쓰는 순 상위 10개
ps -eo pid,ppid,rss,comm --sort=-rss | head -n 11

# 대상 먼저 확인 → 이름으로 한 번에 종료 (같은 이름 전부 죽는다!)
pgrep -af java
pkill -f java

💡 rss 단위는 KB(1048576 ≈ 1GB). pkill -f는 실행 명령줄 전체를 매칭해 의도보다 많이 죽을 수 있다 — pgrep -af로 대상을 눈으로 확인한 뒤 실행한다. --sort의 하이픈 두 개를 빼먹으면 오류.

디스크 여유 · 용량 큰 폴더 찾기 (df · du)

시스템·네트워크 · Linux 공용

# 파티션별 여유 공간 (-h: 사람이 읽는 단위)
df -h

# 이 폴더 바로 아래에서 용량 큰 순 상위 10
du -sh /var/log/* | sort -h | tail -n 10

# 폴더 전체 합계만
du -sh /var/log

# 용량 큰 파일 찾기 (100MB 이상)
find /var/log -type f -size +100M -exec ls -lh {} +

💡 "지웠는데 여유가 안 늘어남"은 프로세스가 삭제된 파일을 아직 열고 있는 경우다(sudo lsof +L1로 확인 → 해당 서비스 재시작). sort -h는 K/M/G 단위를 이해하는 정렬. du는 폴더를 훑느라 느릴 수 있다.

메모리 · CPU 부하 확인 (free · uptime · top)

시스템·네트워크 · Linux 공용

# 메모리·스왑 여유 (-h: 사람이 읽는 단위)
free -h

# 부하 평균(1·5·15분)과 가동 시간
uptime

# 실시간 (종료: q · 메모리순 정렬: 대문자 M · CPU순: P)
top

# 한 번만 찍기 (배치·리포트용)
top -b -n 1 | head -n 20

💡 free의 available이 실제 여유다 — free 열만 보면 "메모리 없음"으로 오해한다(리눅스는 남는 메모리를 캐시로 쓰고 필요하면 즉시 회수). 부하 평균은 코어 수와 비교해 읽는다(4코어에서 4.00 = 포화). 코어 수는 nproc.

서비스 상태·재시작·부팅 등록 (systemctl)

시스템·네트워크 · Linux (systemd)

# 상태 확인 (실행 여부 + 최근 로그 몇 줄)
systemctl status tomcat

# 중지 · 시작 · 재시작
sudo systemctl stop tomcat
sudo systemctl start tomcat
sudo systemctl restart tomcat

# 부팅 시 자동 시작 등록 / 해제
sudo systemctl enable tomcat
sudo systemctl disable tomcat

# 유닛 파일을 고쳤으면 먼저 다시 읽기
sudo systemctl daemon-reload

# 서비스 목록
systemctl list-units --type=service

💡 enable(부팅 등록)과 start(지금 실행)는 별개다 — 재부팅 후 서비스가 안 뜨는 사고의 단골 원인. 이름은 tomcat.service의 .service를 생략할 수 있다. systemd가 없는 구형(CentOS 6 등)은 service tomcat restart · chkconfig.

서비스 로그 보기 (journalctl)

시스템·네트워크 · Linux (systemd)

# 이 서비스 로그 실시간 (중지: Ctrl+C)
journalctl -u tomcat -f

# 최근 200줄만
journalctl -u tomcat -n 200

# 기간으로 (오늘 · 특정 시각 이후)
journalctl -u tomcat --since today
journalctl -u tomcat --since "09:00"

# 에러만 (err 이상)
journalctl -u tomcat -p err

💡 systemd가 없는 서버엔 journalctl이 없다 — /var/log/messages(RHEL)·/var/log/syslog(Debian)·톰캣 catalina.out을 tail로 본다. 재부팅하면 로그가 사라지는 건 저장이 volatile이기 때문(Storage=persistent 필요).

네트워크 진단 — 내 IP · 응답 · 포트 · DNS

시스템·네트워크 · Linux 공용

# 내 IP·인터페이스 (ifconfig는 구버전)
ip a

# 라우팅·게이트웨이
ip r

# 응답 확인 (-c 4: 4번만 보내고 끝)
ping -c 4 192.168.0.10

# 포트 열림 확인 — telnet 대체 (-z: 접속만 · -v: 결과 출력)
nc -zv 192.168.0.10 8080

# 이 이름이 어떤 IP로 풀리는지 (DNS)
dig +short 192.168.0.10
nslookup 192.168.0.10

💡 ping 실패 ≠ 서버 다운 — 방화벽이 ICMP만 막는 경우가 흔하다(생사는 포트로 확인). nc·dig가 없는 폐쇄망 서버가 많다 — 이때는 bash 내장으로 대체: timeout 3 bash -c "echo > /dev/tcp/192.168.0.10/8080" 후 종료코드 0이면 열림.

환경변수 확인·설정 (printenv · export)

시스템·네트워크 · Linux (bash)

# 값 확인 (printenv는 이름만 · echo는 $를 붙여서)
printenv JAVA_HOME
echo $JAVA_HOME

# 전체 목록 · 이름으로 거르기
printenv | sort
printenv | grep -i java

# 현재 셸에서만 설정 (터미널 닫으면 사라짐)
export JAVA_HOME=/usr/lib/jvm/java-17

# 로그인할 때마다 적용 — 내 계정만
echo 'export JAVA_HOME=/usr/lib/jvm/java-17' >> ~/.bash_profile
source ~/.bash_profile

💡 export 없이 NAME=값만 하면 실행한 프로그램(자식 프로세스)에 전달되지 않는다. 이미 열려 있는 터미널·서비스는 옛 값을 보므로 서비스는 재시작해야 반영. 읽는 파일이 배포판·셸마다 다르다(~/.bash_profile vs ~/.bashrc, 전체 적용은 /etc/profile.d/에 .sh 파일).

크론 등록·확인 (crontab)

시스템·네트워크 · Linux 공용

# 내 크론 목록
crontab -l

# 편집 (저장하면 바로 적용 · 기본 편집기가 vi)
crontab -e

# 분 시 일 월 요일 — 매일 새벽 3시
0 3 * * * /app/batch.sh >> /var/log/batch.log 2>&1

# 5분마다
*/5 * * * * /app/batch.sh >> /var/log/batch.log 2>&1

# 크론이 실제로 돌았는지 확인
grep CRON /var/log/syslog

💡 크론은 로그인 셸이 아니라 PATH·JAVA_HOME이 거의 없다 — 명령은 절대경로로, 환경변수는 스크립트 안에서 다시 export한다("수동은 되는데 크론만 안 되는" 1순위 원인). 2>&1은 에러도 같은 파일에 남기라는 뜻. RHEL 계열 로그는 /var/log/cron.

로그 실시간 보기 (tail -f) · 문구 필터

파일·저장 · Linux 공용

# 마지막 200줄 보여주고 이어서 실시간 (중지: Ctrl+C)
tail -f -n 200 /var/log/app.log

# 특정 문구만 골라 실시간
tail -f /var/log/app.log | grep --line-buffered ERROR

# 여러 파일 동시에 (파일명 헤더가 붙는다)
tail -f /var/log/app.log /var/log/messages

# 로그가 새로 만들어져도 계속 따라가기 (rotate 대응)
tail -F /var/log/app.log

💡 로그가 갑자기 멈춘 것처럼 보이면 로테이트된 것 — 소문자 -f는 옛 파일을 계속 보고, 대문자 -F는 새 파일을 따라간다. grep을 파이프로 물릴 때 --line-buffered를 빼면 버퍼가 찰 때까지 화면에 안 나온다.

파일 내용 검색 (grep) — 재귀·문맥·건수

파일·저장 · Linux 공용

# 하위 폴더까지 재귀(-r) 줄번호(-n) 대소문자 무시(-i)
grep -rni "OutOfMemoryError" /var/log

# 확장자만 골라서
grep -rni "OutOfMemoryError" --include="*.log" /var/log

# 앞뒤 2줄 문맥까지 (스택트레이스 볼 때)
grep -n -C 2 "OutOfMemoryError" /var/log

# 들어 있는 파일명만(-l) · 건수만(-c)
grep -rl "OutOfMemoryError" /var/log
grep -rc "OutOfMemoryError" /var/log

# 제외하고 보기(-v)
grep -v "DEBUG" /var/log

💡 검색어가 하이픈으로 시작하면 옵션으로 오해받는다 — grep -- "-Xmx" 처럼 --를 앞에 둔다. 괄호·점 같은 특수문자를 글자 그대로 찾으려면 -F(고정 문자열). Windows 쪽 대응은 findstr·Select-String 카드.

파일 찾기 (find) — 이름·수정일·크기

파일·저장 · Linux 공용

# 이름으로 (와일드카드는 반드시 따옴표로)
find /var/log -type f -name "*.log"

# 수정일 — N일보다 오래된 것(+) · 최근 하루(-1)
find /var/log -type f -mtime +30
find /var/log -type f -mtime -1

# 크기 (100MB 이상)
find /var/log -type f -size +100M

# 찾은 파일마다 명령 실행 (+ 는 여러 개를 한 번에 넘김)
find /var/log -type f -name "*.log" -exec ls -lh {} +

# 바로 아래만 (깊이 제한)
find /var/log -maxdepth 1 -type f

💡 따옴표를 빼면 셸이 *.log를 현재 폴더 파일명으로 먼저 바꿔버려 엉뚱한 결과가 난다. -mtime +30 = "30일보다 오래된"(24시간 단위 내림). 권한 오류가 지저분하면 끝에 2>/dev/null.

권한·소유자 변경 (chmod · chown)

파일·저장 · Linux 공용

# 현재 권한·소유자 확인
ls -l /app/run.sh

# 소유자에게 실행 권한 추가 (기호식 — 최소 변경)
chmod u+x /app/run.sh

# 숫자식 — 소유자 읽기·쓰기, 나머지 읽기만 (644)
chmod 644 /app/run.sh

# 폴더는 755 · 파일은 644로 나눠 적용
find /app -type d -exec chmod 755 {} +
find /app -type f -exec chmod 644 {} +

# 소유자·그룹을 앱 실행 계정으로
sudo chown tomcat:tomcat /app/run.sh

💡 chmod 777은 보안 점검 지적 대상 — 실행이 필요하면 u+x, 앱이 파일을 못 읽으면 권한보다 소유자(chown)를 먼저 의심한다. 폴더에 -R 644를 걸면 폴더에 들어갈 수 없게 되니(x 권한 필요) 위처럼 나눠 적용한다.

압축·해제 (tar · zip · gzip)

파일·저장 · Linux 공용

# 압축 (c: 만들기 · z: gzip · v: 목록 · f: 파일명 — f는 마지막)
tar -czvf backup-20260730.tar.gz /app/logs

# 풀기 전에 내용 확인
tar -tzvf backup-20260730.tar.gz

# 해제 · 특정 폴더에 해제
tar -xzvf backup-20260730.tar.gz
tar -xzvf backup-20260730.tar.gz -C /tmp/restore

# zip (있는 경우)
zip -r backup-20260730.zip /app/logs
unzip backup-20260730.zip -d /tmp/restore

# 단일 파일 gzip — 원본이 사라진다!
gzip /app/logs
gunzip /app/logs.gz

💡 f는 옵션 묶음의 마지막에 — -cfz처럼 쓰면 파일명이 z로 잡혀 엉뚱한 파일이 생긴다. gzip은 원본을 지우고 .gz로 바꾼다(남기려면 -k). 절대경로로 압축하면 맨 앞 /가 제거된 상대경로로 풀린다(풀 위치 주의).

원격 복사·동기화 (scp · rsync)

파일·저장 · Linux 공용

# 로컬 → 원격
scp /app/logs deploy@192.168.0.10:/tmp/

# 원격 → 로컬 (현재 폴더로)
scp deploy@192.168.0.10:/tmp/app.log ./

# 폴더 통째로
scp -r /app/logs deploy@192.168.0.10:/tmp/

# 동기화 — 먼저 드라이런으로 확인
rsync -avz --dry-run /app/logs/ deploy@192.168.0.10:/backup/

# 확인했으면 --dry-run 빼고 실행
rsync -avz /app/logs/ deploy@192.168.0.10:/backup/

💡 rsync 원본 경로 끝의 /가 의미를 바꾼다 — logs/ 는 "안의 내용"을, logs 는 "폴더 자체"를 보낸다(빼먹으면 /backup/logs/logs가 된다). --delete는 받는 쪽의 없는 파일을 지우니 드라이런 없이 쓰지 않는다. 포트가 다르면 scp -P 2222 · rsync -e "ssh -p 2222".

파일 해시 (sha256sum) — 반입 파일 무결성 검증

파일·저장 · Linux 공용

# 해시 만들기 (보내는 쪽 원본에서)
sha256sum /tmp/app.jar

# 폴더 전체 → 목록 파일로
find /tmp/release -type f -exec sha256sum {} + > checksums.txt

# 검증 — 받는 쪽에서 (OK / FAILED로 나온다)
sha256sum -c checksums.txt

# md5가 필요하면
md5sum /tmp/app.jar

💡 폐쇄망 반입(USB·망연계) 파일이 온전한지 확인하는 표준 절차 — 보낸 쪽 해시와 받은 쪽 해시를 비교한다. 목록 파일은 상대경로 기준이라 만든 위치와 같은 곳에서 -c를 실행해야 맞는다. Windows 쪽 대응은 certutil·Get-FileHash 카드.

N일 지난 로그 정리 (find -delete) — 드라이런 먼저

파일·저장 · Linux 공용

# 1) 대상 확인 — 지우지 않고 목록만 (드라이런)
find /var/log/app -type f -name "*.log" -mtime +30 -print

# 2) 확인했으면 삭제 (파괴적! 휴지통 없음)
find /var/log/app -type f -name "*.log" -mtime +30 -delete

# 빈 폴더까지 정리
find /var/log/app -type d -empty -delete

# 프로세스가 열고 있는 로그는 내용만 비운다
truncate -s 0 /var/log/app/app.log

💡 리눅스엔 휴지통이 없다 — 1) 드라이런을 생략하지 말 것. 실행 중인 프로세스가 열고 있는 로그를 rm하면 이름만 사라지고 디스크는 그대로다(truncate 또는 서비스 재시작·logrotate가 정답). -delete는 -name 뒤에 와야 한다.

문자열 치환·줄 삭제 (sed) — 백업본 먼저

문자열 · Linux (GNU sed)

# 화면으로 먼저 확인 (파일은 안 바뀐다)
sed 's/localhost/192.168.0.10/g' /app/config.properties

# 확인했으면 파일에 적용 — .bak 백업을 남긴다
sed -i.bak 's/localhost/192.168.0.10/g' /app/config.properties

# 특정 문구가 든 줄 삭제
sed -i.bak '/localhost/d' /app/config.properties

# 특정 줄 범위만 출력 (1~10줄)
sed -n '1,10p' /app/config.properties

💡 치환값에 /가 들어가면(경로 치환) 구분자를 바꾼다: s#/old/path#/new/path#g. -i는 되돌릴 수 없으니 .bak을 붙이는 습관을 권한다(GNU sed 기준 — 일부 유닉스는 -i 뒤 확장자 문법이 다르다). 점·괄호·별표는 정규식 특수문자다.

컬럼 추출·합계 (awk)

문자열 · Linux 공용

# 공백 기준 컬럼 뽑기 (첫 컬럼 $1 · 마지막 $NF)
awk '{print $1}' /var/log/app.log
awk '{print $1, $NF}' /var/log/app.log

# 구분자 지정 (콤마 CSV)
awk -F, '{print $2}' /var/log/app.log

# 조건 걸기 (특정 문구가 든 줄만)
awk '/ERROR/ {print $1, $2}' /var/log/app.log

# 숫자 컬럼 합계·평균
awk '{sum += $3} END {print sum}' /var/log/app.log
awk '{sum += $3} END {print sum / NR}' /var/log/app.log

💡 작은따옴표로 감싸는 게 중요하다 — 큰따옴표를 쓰면 셸이 $1을 먼저 자기 변수로 해석해 빈 값이 된다. NR=줄 번호(END에서는 총 줄 수) · NF=컬럼 개수. 탭 구분 파일은 -F'\t'.

건수 집계 — 줄 수 · 빈도 상위 (wc · sort · uniq)

문자열 · Linux 공용

# 줄 수 (건수 확인)
wc -l /var/log/app.log

# 특정 문구가 몇 번 나오는지
grep -c "ERROR" /var/log/app.log

# 빈도 상위 — 같은 값끼리 묶어 세고 많은 순 (에러 유형 집계)
awk '{print $5}' /var/log/app.log | sort | uniq -c | sort -rn | head -n 10

# 중복 제거 목록
sort -u /var/log/app.log

# 시간대별 건수 (앞 13글자 = 날짜+시)
cut -c 1-13 /var/log/app.log | sort | uniq -c

💡 uniq는 정렬된 입력만 제대로 센다 — sort를 반드시 앞에 둔다(uniq -c 앞 sort 누락이 단골 실수). sort -rn은 숫자 역순(많은 순). 컬럼 번호·자릿수는 로그 포맷에 맞게 바꿔 쓴다.