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)

참고/주의사항 (한눈에)

  1. exit직접 실행된 새 tcsh 프로세스 를 끝낼 때만 사용하세요. source 상황에서 exit를 쓰면 사용자의 쉘이 통째로 종료됩니다. 그래서 위처럼 source로 들어온 경우에는 return으로 빠져나갑니다.

  2. $0을 패턴 *csh로만 검사하면, 파일명이 something.csh일 때 오탐지될 수 있어요. 그래서 정확히 tcsh, -tcsh, csh, -csh와 비교합니다(위 코드처럼 :t로 basename만 비교).

  3. 사용자가 tcsh myscript.csh처럼 “쉘 이름 + 스크립트”로 실행하면 $0tcsh가 되어 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는 파일명. 위키책)


동작 원리(한 걸음씩)

  1. 실행(exec)
    shebang(#!/bin/tcsh) + 실행권한으로 돌리면 $0에 스크립트 경로가 그대로 들어옵니다. 그래서 1번 분기에서 바로 SELF=$0. (만약 tcsh script.csh처럼 쉘로 실행하면 $0tcsh가 되므로 다음 단계로 넘어갑니다.) cs.plattsburgh.edu

  2. source
    tcsh의 $_는 “마지막으로 실행한 명령줄”을 담습니다. source로 불리면 그 직전 명령줄이 source /경로/파일.csh ... 이므로, 스크립트 첫 줄에서 set __prev = ($_ )로 잡아두고 __prev[2]에서 파일 경로를 꺼냅니다. (여기서 공백·따옴표가 있어도 단어 분리가 유지되는 편이라 실무에서 잘 통합니다.) Unix & Linux Stack Exchange+1

  3. 쉘로 실행(tcsh file.csh)
    이 경우 $0tcsh라 1번이 비어 있고, $_에는 방금 명령줄 tcsh file.csh ...이 남아 있으므로 __prev[2]에서 파일을 얻습니다. Unix & Linux Stack Exchange

  4. 예외 대비(안전망)
    드물게 비대화·특수 환경에서 $_이 기대대로 안 채워질 수 있습니다. 그럴 땐 호출 측에서
    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)

  1. 우선$0을 잡아요.

    • 직접 실행(./myscript.csh)이면 $0이 스크립트 경로를 그대로 가리킵니다.
  2. $0:ttcsh/csh(또는 -tcsh/-csh)면,

    • 이건 source로 불렸거나 tcsh myscript.csh처럼 쉘을 통해 실행된 거예요.

    • 그래서 직전 명령줄 $_을 단어 배열로 쪼개서 __w[2]에서 파일 경로를 가져옵니다.

  3. 상대경로면 $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"

참고: 위 “초간단판”은 대부분의 실무 케이스에 충분합니다. 정말 모든 엣지케이스까지 막으려면 지난번에 드린 “견고판”을 쓰셔요. 필요하면 그 버전도 더 줄여 드릴게요!

Categories:

Updated: