일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- kotlin spring
- MySQL
- APM
- CloudWatch
- Spring
- mysql 튜닝
- 정보처리기사실기 기출문제
- AWS EKS
- aws
- kotlin
- CKA 기출문제
- 정보처리기사 실기
- IntelliJ
- PETERICA
- kotlin querydsl
- AI
- 기록으로 실력을 쌓자
- Java
- kotlin coroutine
- Linux
- Pinpoint
- 코틀린 코루틴의 정석
- 티스토리챌린지
- minikube
- 정보처리기사 실기 기출문제
- Elasticsearch
- 오블완
- CKA
- 공부
- Kubernetes
- Today
- Total
피터의 개발이야기
[Kotlin] kotlin에서 shell 실행하기 본문
ㅁ 들어가며
ㅇ 대량 압축작업 시 OOME(Out Of Memory Exception)의 발생을 방지하기 위한 방법을 찾는 중이다.
ㅇ 지난 글, [Kotlin] ManagementFactory을 이용한 JVM 모니터링 방법에서는 JVM의 메모리 사용량을 검토하여 서킷브레이크 패턴을 적용하려고 검토하였다.
ㅇ 하지만 JVM의 힙메모리를 넘어서는 경우 커널에서 압축을 진행하는 방법도 검토하게 되면서 Kotlin에서 shell을 실행하는 방법을 공부하였다.
ㅁ shell 실행코드
fun runCommand(command: String) {
try {
val process = Runtime.getRuntime().exec(command)
// 명령어 실행 결과 읽기
val reader = BufferedReader(InputStreamReader(process.inputStream))
var line: String?
while (reader.readLine().also { line = it } != null) {
println(line)
}
// 에러 스트림 읽기
val errorReader = BufferedReader(InputStreamReader(process.errorStream))
while (errorReader.readLine().also { line = it } != null) {
System.err.println(line)
}
// 프로세스 종료 대기 및 종료 코드 확인
val exitCode = process.waitFor()
println("명령어 실행 완료. 종료 코드: $exitCode")
} catch (e: Exception) {
e.printStackTrace()
}
}
fun main() {
println("\n######################")
println("# 정상 shell 명령어\n")
runCommand("ls -l") // 실행할 shell 명령어
}
출력:
######################
# 정상 shell 명령어
total 8
-rw-r--r--@ 1 peterseo staff 498 May 6 14:24 hello-kotlin.iml
drwxr-xr-x@ 3 peterseo staff 96 May 6 14:17 out
drwxr-xr-x@ 3 peterseo staff 96 May 6 14:51 src
drwxr-xr-x@ 3 peterseo staff 96 Jul 11 07:20 zipDirRoot202407
명령어 실행 완료. 종료 코드: 0
ㅇ shell의 기본적인 명령어인 ls로 폴더의 파일 리스트를 확인하였다.
ㅇ Runtime.getRuntime().exec(command)를 사용하여 shell 명령어를 실행한다.
ㅇ process.inputStream을 통해 명령어의 표준 출력을 읽는다.
ㅇ process.errorStream을 통해 명령어의 에러 출력을 읽는다.
ㅇ process.waitFor()를 사용하여 프로세스가 종료될 때까지 대기하고, 종료 코드를 얻는다.
ㅇ 예외 처리를 통해 명령어 실행 중 발생할 수 있는 오류를 처리할 수 있다.
ㅁ 잘못된 shell 명령어
fun main() {
println("\n######################")
println("# error shell 명령어\n")
runCommand("error") // 잘못된 shell 명령어)
}
출력:
######################
# error shell 명령어
java.io.IOException: Cannot run program "error": error=2, No such file or directory
at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1170)
at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1089)
at java.base/java.lang.Runtime.exec(Runtime.java:681)
at java.base/java.lang.Runtime.exec(Runtime.java:491)
at java.base/java.lang.Runtime.exec(Runtime.java:366)
at ShellTestKt.runErrorCmd(ShellTest.kt:36)
at ShellTestKt.main(ShellTest.kt:67)
at ShellTestKt.main(ShellTest.kt)
Caused by: java.io.IOException: error=2, No such file or directory
at java.base/java.lang.ProcessImpl.forkAndExec(Native Method)
at java.base/java.lang.ProcessImpl.<init>(ProcessImpl.java:295)
at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:225)
at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1126)
... 7 more
ㅇ 에러 스트림을 확인하기 위해 "error"라는 잘못된 명령어를 실행하였다.
ㅁ 파일 폴더를 압축하고 에러 처리가 가능한 shell(tar 버젼)
#!/bin/bash
# 압축할 폴더와 결과 파일 이름을 인자로 받음
SOURCE_DIR="$1"
OUTPUT_FILE="$2"
# 함수: 에러 메시지 출력 및 종료
error_exit() {
echo "에러: $1" >&2
exit 1
}
# 인자 개수 확인
if [ $# -ne 2 ]; then
error_exit "사용법: $0 <압축할_폴더> <결과_파일.tar.gz>"
fi
# 소스 디렉토리 존재 확인
if [ ! -d "$SOURCE_DIR" ]; then
error_exit "지정한 폴더가 존재하지 않습니다: $SOURCE_DIR"
fi
# 출력 파일 이름이 .tar.gz로 끝나는지 확인
if [[ "$OUTPUT_FILE" != *.tar.gz ]]; then
OUTPUT_FILE="${OUTPUT_FILE}.tar.gz"
echo "출력 파일 이름을 $OUTPUT_FILE로 변경했습니다."
fi
# 압축 진행: -C
echo "압축 시작: $SOURCE_DIR -> $OUTPUT_FILE"
if tar -czf "$OUTPUT_FILE" -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")" 2>/dev/null; then
echo "압축 완료: $OUTPUT_FILE"
else
error_exit "압축 과정에서 오류가 발생했습니다."
fi
# 압축 파일 크기 확인
FILE_SIZE=$(du -h "$OUTPUT_FILE" | cut -f1)
echo "압축 파일 크기: $FILE_SIZE"
# 압축 파일 존재 확인
if [ -f "$OUTPUT_FILE" ]; then
echo "압축 파일이 성공적으로 생성되었습니다: $OUTPUT_FILE"
else
error_exit "압축 파일 생성에 실패했습니다."
fi
옵션 | 설명 |
-c | 파일을 tar로 묶음 |
-p | 파일 권한을 저장 |
-v | 묶거나 파일을 풀 때 과정을 화면으로 출력 |
-f | 파일 이름을 지정 |
-C | 경로를 지정 |
-x | tar 압축을 풂 |
-z | gzip으로 압축하거나 해제함 |
출처: https://nota.tistory.com/53 [nota's story:티스토리]
ㅁ zip 버젼
#!/bin/bash
# 사용법 함수
usage() {
echo "사용법: $0 <압축할_폴더_경로> [출력_zip_파일명]"
echo "예: $0 /path/to/folder [output.zip]"
exit 1
}
# 인자 개수 확인
if [ $# -lt 1 ]; then
usage
fi
# 입력 폴더와 출력 파일명 설정
INPUT_FOLDER="$1"
OUTPUT_ZIP="${2:-${INPUT_FOLDER##*/}.zip}"
# 폴더 존재 여부 확인
if [ ! -d "$INPUT_FOLDER" ]; then
echo "에러: '$INPUT_FOLDER' 폴더가 존재하지 않습니다."
exit 1
fi
# ZIP 명령어 존재 여부 확인
if ! command -v zip &> /dev/null; then
echo "에러: 'zip' 명령어를 찾을 수 없습니다. 설치가 필요합니다."
exit 1
fi
# 압축 실행
echo "압축 중: $INPUT_FOLDER -> $OUTPUT_ZIP"
if zip -r "$OUTPUT_ZIP" "$INPUT_FOLDER"; then
echo "압축 완료: $OUTPUT_ZIP"
else
echo "에러: 압축 과정에서 문제가 발생했습니다."
exit 1
fi
# 압축 파일 크기 확인
ZIP_SIZE=$(du -h "$OUTPUT_ZIP" | cut -f1)
echo "압축 파일 크기: $ZIP_SIZE"
# 압축 파일 무결성 검사
echo "압축 파일 무결성 검사 중..."
if unzip -t "$OUTPUT_ZIP" > /dev/null; then
echo "무결성 검사 통과"
else
echo "에러: 압축 파일 무결성 검사 실패"
exit 1
fi
ㅁ 테스트 temp 폴더
ㅇ [Kotlin] File들을 코드별 폴더로 압축하기에서 생성한 폴더이다.
ㅁ fieZipTest.sh 실행
fun main(){
println("\n######################")
println("# fileZipTest\n")
runCommand("sh fileZipTest.sh temp test.zip") // 압축할 폴더와 압축명을 인자로 전달
println("\n######################")
println("# fileZipTest error\n")
runCommand("sh fileZipTest.sh wrong_temp test.zip") // 압축할 폴더와 압축명을 인자로 전달
}
출력:
######################
# fileZipTest
압축 중: temp -> test.zip
adding: temp/ (stored 0%)
adding: temp/CODE2/ (stored 0%)
adding: temp/CODE2/file3.txt (stored 0%)
adding: temp/CODE2/file4.txt (stored 0%)
adding: temp/CODE3/ (stored 0%)
adding: temp/CODE3/file5.txt (stored 0%)
adding: temp/CODE3/file6.txt (stored 0%)
adding: temp/CODE1/ (stored 0%)
adding: temp/CODE1/file2.txt (stored 0%)
adding: temp/CODE1/file1.txt (stored 0%)
압축 완료: test.zip
압축 파일 크기: 4.0K
압축 파일 무결성 검사 중...
무결성 검사 통과
명령어 실행 완료. 종료 코드: 0
######################
# fileZipTest error
에러: 'wrong_temp' 폴더가 존재하지 않습니다.
명령어 실행 완료. 종료 코드: 1
ㅁ 마무리
ㅇ 위 코드는 기본적으로 시스템의 기본 shell을 실행할 수 있다.
ㅇ 복잡한 shell 스크립트를 실행하려면 별도의 실행 스크립트 파일을 만들고 그 파일을 실행하는 방식을 이용한다면, JVM에서 실행하기 힘든 압축작업을 충분히 수행할 수 있을 것이다.
'Programming > Kotlin' 카테고리의 다른 글
[Kotlin] Spring에 MapStruct와 Lombok을 함께 사용할 때 isCompleted 필드가 null로 넘어오는 문제 (0) | 2024.07.19 |
---|---|
[Kotlin] Kotlin으로 Shell 유틸리티 만들기: 파일 복사와 압축 (1) | 2024.07.18 |
[Kotlin] QueryDSL에서 YYYY-MM로 검색하기 (1) | 2024.07.16 |
[Kotlin] ManagementFactory을 이용한 JVM 모니터링 방법 (1) | 2024.07.14 |
[Kotlin] kotlin 폴더 전체 삭제 방법 (0) | 2024.07.13 |