변수와 확장

문법뜻
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 || BA가 성공하면 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 | ba의 출력을 b의 입력으로
cmd | tee log화면에도 찍고 파일에도 쓰기
cat > f.inp <<EOF ... EOFhere-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.shWindows 줄바꿈(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.out10–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 ProSlurm
제출qsub job.pbssbatch job.sh
내 작업 보기qstat -u $USERsqueue -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=4srun --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}"처럼 기본값을 주면 편하다.