변수와 확장
| 문법 | 뜻 |
mol=water | 변수 만들기. = 양옆에 공백 없음 |
"$mol", "${mol}_opt.inp" | 값 꺼내기. 뒤에 글자가 붙으면 중괄호 |
'$mol' | 작은따옴표 안은 글자 그대로 |
n=$(grep -c "^H" water.xyz) | 명령 치환: 출력을 변수에 |
$(( nprocs * 2 )) | 정수 산술. 소수는 awk나 bc |
export OMP_NUM_THREADS=8 | 하위 프로그램에도 보이는 환경 변수 |
${f%.xyz} | 뒤에서 .xyz 떼기 |
${f##*/} / ${f%/*} | 파일 이름만 / 디렉터리만 |
${f#*_} / ${f%%_*} | 처음 _까지 떼기 / 처음 _부터 떼기 |
${f/.xyz/.inp} | 처음 맞는 부분 바꾸기. ${f//a/b}는 전부 |
${n:-4} | 비었거나 없으면 4 |
${#mol} | 문자열 길이 |
검사
| 검사 | 참인 경우 |
[[ -f $f ]], -d, -e | 파일 / 디렉터리 / 아무거나 있다 |
[[ -s $f ]] | 파일이 있고 비어 있지 않다 |
[[ -z $s ]] / [[ -n $s ]] | 문자열이 비었다 / 비어 있지 않다 |
[[ $a == $b ]], != | 문자열 비교 |
[[ $f == *.xyz ]] | 패턴 비교 (오른쪽을 따옴표로 감싸지 않는다) |
[[ $n -lt 8 ]] | 정수 비교: -eq -ne -lt -le -gt -ge |
(( n > 8 )) | 정수 비교, 산술 문법 |
!, &&, || | [[ ]] 안의 부정, 그리고, 또는 |
조건문과 반복문
| 형식 | 메모 |
if 명령; then ...; elif ...; else ...; fi | 명령의 종료 코드가 0이면 참 |
A && B / A || B | A가 성공하면 B / 실패하면 B |
for f in *.xyz; do ...; done | 파일 반복. $(ls)는 쓰지 않는다 |
shopt -s nullglob | 맞는 파일이 없으면 빈 목록 |
for i in {1..10}, {01..10} | 숫자 범위. 변수는 못 쓴다 |
for (( i=0; i<n; i++ )) | C 형식. 범위에 변수를 쓸 때 |
while read -r a b; do ...; done < list.txt | 파일을 한 줄씩. 파이프로 넘기면 변수가 밖에 안 남는다 |
done < <(find . -name '*.out') | 명령 출력을 한 줄씩 (프로세스 치환) |
case $f in *.xyz) ... ;; *) ... ;; esac | 패턴별로 나누기 |
continue / break | 이번 차례 건너뛰기 / 반복 끝내기 |
함수와 인자
| 문법 | 뜻 |
name() { ...; } | 함수 정의. 부를 때는 name arg1 arg2 |
local x=$1 | 함수 안에서만 보이는 변수 |
$0, $1 ... ${10} | 스크립트 이름, 인자 |
$# | 인자 개수 |
"$@" / "$*" | 인자를 각각 / 한 문자열로 |
shift, shift 2 | 앞 인자 버리기 |
return 1 | 함수의 종료 코드 (0–255) |
e=$(orca_energy water.out) | 출력으로 값 돌려받기 |
while getopts "m:n:h" opt | 옵션 파싱. 값은 $OPTARG, 끝나면 shift $((OPTIND - 1)) |
source lib.sh, . lib.sh | 다른 파일을 현재 셸에서 실행 |
리다이렉션과 파이프
| 문법 | 뜻 |
cmd > out / cmd >> out | 표준 출력을 파일에 덮어쓰기 / 이어 쓰기 |
cmd 2> err | 표준 오류만 파일로 |
cmd > log 2>&1, cmd &> log | 둘 다 한 파일로 |
cmd 2>/dev/null | 오류 메시지 버리기 |
echo "msg" >&2 | 표준 오류로 찍기 |
cmd < file | 파일을 표준 입력으로 |
a | b | a의 출력을 b의 입력으로 |
cmd | tee log | 화면에도 찍고 파일에도 쓰기 |
cat > f.inp <<EOF ... EOF | here-document. 변수와 $( )가 펼쳐진다 |
<<'EOF' | 펼치지 않고 글자 그대로 |
<<-EOF | 줄 앞의 탭을 지운다 (공백은 안 지운다) |
cmd <<< "$var" | here-string: 문자열 하나를 입력으로 |
안전장치
| 문법 | 뜻 |
set -euo pipefail | 실패하면 멈춤, 없는 변수는 오류, 파이프 중간 실패도 잡기 |
grep ... || true | -e 아래에서 "못 찾음"을 허용 |
i=$((i + 1)) | ((i++)) 대신. i가 0일 때 -e에 걸리지 않는다 |
${1:-} | -u 아래에서 없는 인자를 빈 값으로 |
cd "$d" || exit 1 | 이동 실패 시 멈추기 |
command -v orca | 프로그램이 있는지 확인하고 경로 얻기 |
tmp=$(mktemp -d) | 겹치지 않는 임시 디렉터리 |
trap 'rm -rf "$tmp"' EXIT | 끝날 때 뒷정리 |
date '+%F %T' | 2026-09-29 00:15:54 형식 시각 |
bash -n script.sh | 실행하지 않고 문법만 검사 |
bash -x script.sh | 실행하는 명령을 한 줄씩 찍기 |
shellcheck script.sh | 흔한 실수 찾기 |
sed -i 's/\r$//' script.sh | Windows 줄바꿈(CRLF) 지우기 |
grep · sed · awk
ORCA 출력에서 자주 쓰는 형태만 모았다. 자세한 설명은 05장과 06장에 있다.
| 명령 | 하는 일 |
grep -n "FINAL SINGLE POINT ENERGY" water.out | 맞는 줄을 줄 번호와 함께 |
grep -c "FINAL SINGLE POINT ENERGY" water.out | 맞는 줄 수 (최적화 단계 수를 셀 때) |
grep -A 10 "^VIBRATIONAL FREQUENCIES" water.out | 맞는 줄과 그 뒤 10줄 |
grep -B 2 "TERMINATED NORMALLY" water.out | 맞는 줄과 그 앞 2줄 |
grep -l "TERMINATED NORMALLY" */*.out | 맞는 줄이 있는 파일 이름만. -L은 없는 파일 |
grep -r --include='*.out' -l "TERMINATED NORMALLY" jobs | 디렉터리 아래를 재귀로 |
grep -q "..." f | 출력 없이 종료 코드만 (if 조건용) |
sed -n '10,20p' water.out | 10–20번째 줄만 |
sed -n '/CARTESIAN COORDINATES (ANGSTROEM)/,+4p' water.out | 패턴부터 4줄 더 |
sed -i 's/nprocs 4/nprocs 8/' */*.inp | 파일 안에서 바꾸기. -i.bak이면 원본을 남긴다 |
sed '/^#/d' f | #으로 시작하는 줄 지우기 |
awk '{print $5}' | 공백 기준 다섯째 열 |
awk '{print $NF}' | 마지막 열 (NF는 열 개수) |
awk 'NR > 2 {print $1}' water.xyz | 셋째 줄부터 첫 열 (NR은 줄 번호) |
awk -F, '{print $2}' data.csv | 쉼표로 나눈 둘째 열 |
awk '/FINAL SINGLE POINT ENERGY/ {e=$5} END {print e}' water.out | 마지막으로 맞은 줄의 값 |
awk '{s += $2} END {print s / NR}' f | 둘째 열 평균 |
awk -v x="$e" 'BEGIN {printf "%.2f\n", x * 627.509}' | 셸 변수로 소수 계산 (Hartree → kcal/mol) |
$ grep -n "FINAL SINGLE POINT ENERGY" water.out | tail -n 1
3124:FINAL SINGLE POINT ENERGY -76.426369457144
$ awk '/FINAL SINGLE POINT ENERGY/ {e=$5} END {print e}' water.out
-76.426369457144
$ grep -L "ORCA TERMINATED NORMALLY" jobs/*/*.out
jobs/methane_opt/methane_opt.out
PBS와 Slurm
PBS는 PBS Pro 기준이다. 클러스터마다 큐 이름, 메모리 단위, 기본값이 다르니 사이트 문서를 함께 본다.
작업 스크립트 전체 예는 07장, 배열은 08장에 있다.
| 할 일 | PBS Pro | Slurm |
| 제출 | qsub job.pbs | sbatch job.sh |
| 내 작업 보기 | qstat -u $USER | squeue -u $USER |
| 작업 상세 | qstat -f <jobid> | scontrol show job <jobid> |
| 끝난 작업 기록 | qstat -x <jobid> | sacct -j <jobid> |
| 취소 | qdel <jobid> | scancel <jobid> |
| 대화형 작업 | qsub -I -l select=1:ncpus=4 | srun --pty -c 4 bash |
| 작업 이름 | #PBS -N opt | #SBATCH --job-name=opt (-J opt) |
| 코어와 메모리 | #PBS -l select=1:ncpus=8:mem=16gb | #SBATCH --ntasks=1
#SBATCH --cpus-per-task=8
#SBATCH --mem=16G |
MPI 프로세스 8개 (ORCA %pal) | #PBS -l select=1:ncpus=8:mpiprocs=8 | #SBATCH --ntasks=8 |
| 시간 제한 | #PBS -l walltime=24:00:00 | #SBATCH --time=24:00:00 |
| 큐 / 파티션 | #PBS -q normal | #SBATCH --partition=normal |
| 출력 파일 | #PBS -j oe (표준 오류를 출력에 합침) | #SBATCH --output=%x-%j.out |
| 작업 배열 | #PBS -J 1-10 | #SBATCH --array=1-10 |
| 배열 번호 | $PBS_ARRAY_INDEX | $SLURM_ARRAY_TASK_ID |
| 제출한 디렉터리 | $PBS_O_WORKDIR | $SLURM_SUBMIT_DIR |
| 작업 ID | $PBS_JOBID | $SLURM_JOB_ID |
| 받은 코어 수 | $NCPUS | $SLURM_CPUS_PER_TASK, $SLURM_NTASKS |
같은 글자, 다른 뜻
PBS의 -N은 작업 이름이지만 Slurm의 -N은 노드 수다. PBS의 -J는 배열이지만 Slurm의 -J는 작업 이름이다.
스크립트를 옮길 때는 한 글자 옵션보다 Slurm의 긴 옵션(--job-name, --nodes, --array)을 쓰면 헷갈리지 않는다.
PBS 작업은 홈 디렉터리에서 시작하므로 스크립트 첫머리에 cd "$PBS_O_WORKDIR"가 필요하다.
Slurm은 제출한 디렉터리에서 시작한다. 작업 스크립트를 로컬에서 시험할 때는 cd "${PBS_O_WORKDIR:-$PWD}"처럼 기본값을 주면 편하다.