tcsh 스크립트 실행 및 위치 파악
tcsh 스크립트에서 두 가지 경우를 구분할 수 있어? 1) 쉘에서 “스크립트명”만 쳐서 실행한 경우 2) tcsh에서 “source 스크립트”를 한 경우 두 경우를 구분해서 1)번에서만 동작하는 스크립트(2로 시작시 경고 띄우고 종료), 2)번에서만 동작하는 스크립트(1로 시작시 경고 띄우고 종료) 각각 만들어주면 좋겠어
좋지! tcsh에서 “직접 실행” vs “source로 실행”을 구분하는 가장 안전한 방법은 $0(실행 엔트리 이름)을 보는 거야.
-
스크립트를 직접 실행 하면
$0= 스크립트 파일명(혹은 경로) -
스크립트를 source로 실행 하면
$0= 현재 쉘 이름(보통tcsh,-tcsh,csh,-csh)
이 원리를 이용해서 두 버전(직접 실행 전용 / source 전용)을 만들면 돼.
아래 예제는 각각 “틀린 방식”으로 실행되면 경고를 띄우고 바로 종료해.
1) “직접 실행”에서만 동작하는 스크립트 (source로 시작 시 경고 후 종료)
tcsh
#!/bin/tcsh
# run-only.csh — 직접 실행 전용
# basename만 뽑아서 쉘 이름과 비교
set base = "$0:t"
# base가 쉘 이름이면 => source로 실행된 것
if ( "$base" == "tcsh" || "$base" == "-tcsh" || "$base" == "csh" || "$base" == "-csh" ) then
echo "경고: 이 스크립트는 'source'로 실행하면 안 됩니다. 파일명을 직접 실행하세요."
echo "예) chmod +x run-only.csh && ./run-only.csh"
# source 환경에서는 exit가 사용자의 쉘을 종료하므로 return으로 빠져나감
return 1
endif
# ---- 여기부터 정상 동작 영역 (직접 실행된 경우만 도달) ----
echo "OK: 직접 실행됨 (not sourced)"
# 당신의 실제 로직을 여기에 작성
# ...
exit 0
테스트:
chmod +x run-only.csh
./run-only.csh # 정상 동작
source run-only.csh # 경고 후 조용히 종료(return)
2) “source”에서만 동작하는 스크립트 (직접 실행 시 경고 후 종료)
tcsh
#!/bin/tcsh
# source-only.csh — source 전용
set base = "$0:t"
# base가 쉘 이름이 아니면 => 직접 실행된 것(새 프로세스)
if ( "$base" != "tcsh" && "$base" != "-tcsh" && "$base" != "csh" && "$base" != "-csh" ) then
echo "경고: 이 스크립트는 'source'로만 실행해야 합니다."
echo "예) source source-only.csh"
# 여기서는 별도의 프로세스이므로 안전하게 exit로 종료
exit 1
endif
# ---- 여기부터 정상 동작 영역 (source된 경우만 도달) ----
echo "OK: source로 실행됨"
# 당신의 실제 로직을 여기에 작성
# ...
# source된 파일 끝에 도달하면 자동으로 반환되지만, 의도를 명확히 하려면 return 사용 가능
return 0
테스트:
source source-only.csh # 정상 동작
./source-only.csh # 경고 후 종료(exit 1)
참고/주의사항 (한눈에)
-
exit는 직접 실행된 새 tcsh 프로세스 를 끝낼 때만 사용하세요.source상황에서exit를 쓰면 사용자의 쉘이 통째로 종료됩니다. 그래서 위처럼source로 들어온 경우에는return으로 빠져나갑니다. -
$0을 패턴*csh로만 검사하면, 파일명이something.csh일 때 오탐지될 수 있어요. 그래서 정확히tcsh,-tcsh,csh,-csh와 비교합니다(위 코드처럼:t로 basename만 비교). -
사용자가
tcsh myscript.csh처럼 “쉘 이름 + 스크립트”로 실행하면$0이tcsh가 되어 source처럼 인식 됩니다. 질문에서 1) “스크립트명만 쳐서 실행”이라고 했으니, 위 방식이 요구사항에 맞습니다(실행은 shebang + 직접 실행을 가정).
source 시 소스 당한 tcsh 스크립트 입장에서 현재 실행된 스크립트의 파일 위치와 디렉터리를 알 수 있어? 실행했을 때도 알 수 있어?
결론부터 말하면:
-
실행된(tcsh가 새 프로세스로 스크립트를 실행) 경우:
$0에 스크립트 경로가 옵니다. -
source 된(현재 쉘에서 불려 들어온) 경우: 스크립트 맨 첫 줄에서
$_(직전 명령줄)을 읽으면source <파일>형태가 들어있어서 파일 경로를 뽑아낼 수 있습니다. 이건 tcsh가$_를 “마지막에 실행된 명령줄”로 유지하기 때문이에요. Unix & Linux Stack Exchange+1
아래는 두 경우 모두에서 스크립트 파일의 절대 경로(SELF)와 디렉터리(DIR) 를 얻는 “붙여넣기용” 헤더입니다. 반드시 파일 최상단(주석 다음, 모든 명령 이전)에 넣으세요 — 그래야 $_가 source …를 가리킵니다.
tcsh
# ==== BEGIN: resolve SELF and DIR (works for exec + source) ====
# 캡처: 직전 명령줄 (예: 'source /path/to/me.csh ...')
set __prev = ($_)
# 어떻게 호출됐는지 판단 + 스크립트 경로 얻기
set SELF = ""
set __0t = "$0:t"
# 1) 보통 실행: shebang으로 직접 실행되면 $0에 파일 경로가 옴
if ( "$__0t" != "tcsh" && "$__0t" != "-tcsh" && "$__0t" != "csh" && "$__0t" != "-csh" ) then
set SELF = "$0"
endif
# 2) source 되었거나 'tcsh script.csh'처럼 쉘로 실행한 경우: $_을 파싱
if ( "$SELF" == "" ) then
if ( $#__prev >= 2 && "$__prev[1]" == "source" ) then
# source <file> [args...]
set SELF = "$__prev[2]"
else if ( $#__prev >= 2 && ( "$__prev[1]" == "tcsh" || "$__prev[1]" == "-tcsh" || "$__prev[1]" == "csh" || "$__prev[1]" == "-csh" ) ) then
# tcsh <file> [args...] 로 실행된 케이스
set SELF = "$__prev[2]"
endif
endif
# 3) 최후의 보루: 호출자가 파일 경로를 첫 인자로 넘겨줬다면 사용
if ( "$SELF" == "" && $#argv >= 1 && -e "$argv[1]" ) then
set SELF = "$argv[1]"
shift argv
endif
# 4) 절대 경로화 + DIR 계산 (+선택: 심볼릭 링크 해제)
if ( "$SELF" !~ /* ) set SELF = "$cwd/$SELF"
if ( -x /usr/bin/realpath ) then
set SELF = `/usr/bin/realpath "$SELF"`
else if ( -x /bin/readlink ) then
set __tmp = `/bin/readlink -f "$SELF" 2>/dev/null`
if ( "$status" == 0 ) set SELF = "$__tmp"
endif
set DIR = "$SELF:h"
# 정리
unset __prev __0t __tmp
# ==== END: resolve SELF and DIR ====
이제 어디서든 echo $SELF / echo $DIR를 쓰면 됩니다.
(:h, :t 같은 tcsh 파일명 모디파이어로 경로/파일명을 쉽게 분리할 수 있어요. 예: $SELF:h는 디렉터리, $SELF:t는 파일명. 위키책)
동작 원리(한 걸음씩)
-
실행(exec)
shebang(#!/bin/tcsh) + 실행권한으로 돌리면$0에 스크립트 경로가 그대로 들어옵니다. 그래서 1번 분기에서 바로SELF=$0. (만약tcsh script.csh처럼 쉘로 실행하면$0이tcsh가 되므로 다음 단계로 넘어갑니다.) cs.plattsburgh.edu -
source
tcsh의$_는 “마지막으로 실행한 명령줄”을 담습니다.source로 불리면 그 직전 명령줄이source /경로/파일.csh ...이므로, 스크립트 첫 줄에서set __prev = ($_ )로 잡아두고__prev[2]에서 파일 경로를 꺼냅니다. (여기서 공백·따옴표가 있어도 단어 분리가 유지되는 편이라 실무에서 잘 통합니다.) Unix & Linux Stack Exchange+1 -
쉘로 실행(
tcsh file.csh)
이 경우$0은tcsh라 1번이 비어 있고,$_에는 방금 명령줄tcsh file.csh ...이 남아 있으므로__prev[2]에서 파일을 얻습니다. Unix & Linux Stack Exchange -
예외 대비(안전망)
드물게 비대화·특수 환경에서$_이 기대대로 안 채워질 수 있습니다. 그럴 땐 호출 측에서
source mylib.csh "$cwd/mylib.csh"처럼 파일 경로를 첫 인자로 넘겨 두면 위 코드가 자동으로 주워서 씁니다. tcsh의source는 인자를 허용하며, 이 인자들은$argv로 들어옵니다. IBM
예시
A. 실행해 보기
chmod +x whereami.csh
./whereami.csh
# -> SELF=/절대/경로/whereami.csh
# -> DIR=/절대/경로
B. source 해 보기
tcsh
tcsh> source whereami.csh
# -> SELF=/절대/경로/whereami.csh
# -> DIR=/절대/경로
C. ‘tcsh 스크립트명’으로 실행
tcsh whereami.csh
# -> SELF=/절대/경로/whereami.csh
# -> DIR=/절대/경로
팁
-
경로를 더 정교하게 쪼갤 때는 tcsh 모디파이어를 활용하세요:
$SELF:h(디렉터리),$SELF:t(파일명),$SELF:r(확장자 제외),$SELF:e(확장자). 위키책 -
source 스크립트에서
exit를 쓰면 사용자의 쉘이 종료됩니다. 소스된 스크립트를 끝낼 땐return을 쓰세요. (이건 지난 답변에서 다뤘던 포인트 👀)
필요하면 위 헤더를 살짝 래핑해서 공용 유틸로 만들어 드릴 수도 있어요.
어렵네
맞아요, tcsh에선 좀 까다롭죠 😅
“쉽게 쓰는 초간단판”을 드릴게요. 그대로 복붙해서 스크립트 맨 위 에 넣으면 됩니다.
tcsh
# ==== simple-whereami.csh (exec + source 둘 다) ====
# 반드시 파일 최상단에 둘 것!
set SELF = "$0"
# $0이 쉘 이름이면(= source 되었거나 'tcsh file.csh'로 실행)
if ( "$SELF:t" == "tcsh" || "$SELF:t" == "-tcsh" || "$SELF:t" == "csh" || "$SELF:t" == "-csh" ) then
# 직전 명령줄($_)에서 파일명을 뽑는다: [source|tcsh] <file> ...
set __w = ( $_ )
if ( $#__w >= 2 ) set SELF = "$__w[2]"
endif
# 절대경로로 만들고, 디렉터리 계산
if ( "$SELF" !~ /* ) set SELF = "$cwd/$SELF"
set DIR = "$SELF:h"
# 필요 시 확인용 출력
# echo "SELF=$SELF"
# echo "DIR=$DIR"
# ==== end ====
어떻게 동작하나요? (step-by-step)
-
우선
$0을 잡아요.- 직접 실행(
./myscript.csh)이면$0이 스크립트 경로를 그대로 가리킵니다.
- 직접 실행(
-
$0:t가tcsh/csh(또는-tcsh/-csh)면,-
이건 source로 불렸거나
tcsh myscript.csh처럼 쉘을 통해 실행된 거예요. -
그래서 직전 명령줄
$_을 단어 배열로 쪼개서__w[2]에서 파일 경로를 가져옵니다.
-
-
상대경로면
$cwd와 합쳐 절대경로 로 바꾸고,:h모디파이어로 디렉터리(DIR) 를 얻습니다.
바로 써보기
chmod +x myscript.csh
# 1) 직접 실행
./myscript.csh
# 2) source
tcsh> source myscript.csh
둘 다 SELF는 스크립트의 절대경로, DIR은 그 디렉터리가 됩니다.
보너스: 모드(직접/소스) 판별 초간단 스니펫
tcsh
set __base = "$0:t"
if ( "$__base" == "tcsh" || "$__base" == "-tcsh" || "$__base" == "csh" || "$__base" == "-csh" ) then
set MODE = "sourced" # source 또는 'tcsh file.csh'
else
set MODE = "executed" # ./file.csh
endif
# echo "MODE=$MODE"
참고: 위 “초간단판”은 대부분의 실무 케이스에 충분합니다. 정말 모든 엣지케이스까지 막으려면 지난번에 드린 “견고판”을 쓰셔요. 필요하면 그 버전도 더 줄여 드릴게요!