it-swarm-ko.tech

소스 쉘 스크립트의 경로 결정

sourced Shell 스크립트가 자신의 경로를 찾는 방법이 있습니까? tcsh를 사용하는 동료가 있지만 주로 bash에 관심이 있습니다.

소싱으로 인해 현재 셸에서 명령이 실행되므로 운이 좋지 않을 수 있습니다. $0는 여전히 소스 스크립트가 아닌 현재 쉘의 호출입니다. 내 생각은 현재 source $script $script이므로 첫 번째 위치 매개 변수에 필요한 정보가 포함됩니다. 더 나은 방법이 있습니까?

분명히, 나는 스크립트를 실행하지 않고 sourcing 스크립트입니다.

source foo.bash
86
Cascabel

tcsh, $_ 파일 시작시 스크립트 시작 부분의 _는 위치를 포함하고 $0 실행 된 경우 포함합니다.

#!/bin/tcsh
set sourced=($_)
if ("$sourced" != "") then
    echo "sourced $sourced[2]"
endif
if ("$0" != "tcsh") then
    echo "run $0"
endif

배쉬에서 :

#!/bin/bash
[[ $0 != $BASH_SOURCE ]] && echo "Script is being sourced" || echo "Script is being run"

$BASH_SOURCE 변수. 실행 된 경로를 반환합니다.

[email protected] ~ $ /home/pbm/a.sh 
/home/pbm/a.sh
[email protected] ~ $ ./a.sh
./a.sh
[email protected] ~ $ source /home/pbm/a.sh 
/home/pbm/a.sh
[email protected] ~ $ source ./a.sh
./a.sh

따라서 다음 단계에서는 경로가 상대적인지 여부를 확인해야합니다. 상대가 아닌 경우 모든 것이 정상입니다. 만약 pwd로 경로를 확인할 수 있다면 /$BASH_SOURCE.

32
pbm

이 솔루션은 tcsh가 아닌 bash에만 적용됩니다. 함수 내에서 경로를 찾으려면 일반적으로 제공되는 답변 ${BASH_SOURCE[0]}이 작동하지 않습니다.

파일이 소스인지 스크립트로 실행되는지에 관계 없이이 줄이 항상 작동하는 것으로 나타났습니다.

echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

심볼릭 링크를 따르려면 위의 경로에서 readlink을 재귀 적 또는 비재 귀적으로 사용하십시오.

다음은 시도해보고 다른 제안 된 솔루션과 비교하는 스크립트입니다. source test1/test2/test_script.sh 또는 bash test1/test2/test_script.sh로 호출하십시오.

#
# Location: test1/test2/test_script.sh
#
echo $0
echo $_
echo ${BASH_SOURCE}
echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}

cur_file="${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}"
cur_dir="$(dirname "${cur_file}")"
source "${cur_dir}/func_def.sh"

function test_within_func_inside {
    echo ${BASH_SOURCE}
    echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}

echo "Testing within function inside"
test_within_func_inside

echo "Testing within function outside"
test_within_func_outside

#
# Location: test1/test2/func_def.sh
#
function test_within_func_outside {
    echo ${BASH_SOURCE}
    echo ${BASH_SOURCE[${#BASH_SOURCE[@]} - 1]}
}

원 라이너가 작동하는 이유는 BASH_SOURCE 환경 변수와 관련 FUNCNAME를 사용하여 설명합니다.

BASH_SOURCE

멤버가 FUNCNAME 배열 변수에서 해당 쉘 함수 이름이 정의 된 소스 파일 이름 인 배열 변수입니다. 쉘 기능 $ {FUNCNAME [$ i]}은 $ {BASH_SOURCE [$ i]} 파일에 정의되어 있으며 $ {BASH_SOURCE [$ i + 1]}에서 호출되었습니다.

기능 명

현재 실행 호출 스택에있는 모든 셸 함수의 이름을 포함하는 배열 변수입니다. 인덱스가 0 인 요소는 현재 실행중인 셸 함수의 이름입니다. 맨 아래 요소 (인덱스가 가장 높은 요소)는 "main"입니다. 이 변수는 Shell 함수가 실행될 때만 존재합니다. FUNCNAME에 대한 지정은 적용되지 않으며 오류 상태를 리턴합니다. FUNCNAME을 설정하지 않으면 나중에 다시 설정하더라도 특수 특성이 손실됩니다.

이 변수는 BASH_LINENO 및 BASH_SOURCE와 함께 사용할 수 있습니다. FUNCNAME의 각 요소에는 호출 스택을 설명하기 위해 BASH_LINENO 및 BASH_SOURCE에 해당 요소가 있습니다. 예를 들어 $ {FUNCNAME [$ i]}은 (는) 행 번호 $ {BASH_LINENO [$ i]}의 $ {BASH_SOURCE [$ i + 1]} 파일에서 호출되었습니다. 발신자 기본 제공은이 정보를 사용하여 현재 호출 스택을 표시합니다.

[출처 : 배쉬 매뉴얼]

21
gkb0986

철저하고 검색을 위해 여기에있는 일이 있습니다 ... 커뮤니티 위키이므로 다른 Shell의 해당 항목을 자유롭게 추가하십시오 (분명히 $ BASH_SOURCE가 다름).

test.sh :

#! /bin/sh
called=$_
echo $called
echo $_
echo $0
echo $BASH_SOURCE

test2.sh :

#! /bin/sh
source ./test.sh

세게 때리다:

$./test2.sh
./test2.sh
./test2.sh
./test2.sh
./test.sh
$ sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh
./test.sh

대시

$./test2.sh
./test2.sh
./test2.sh
./test2.sh

$/bin/sh ./test2.sh
/bin/sh
/bin/sh
./test2.sh

$

Zsh

$ ./test2.sh
./test.sh
./test.sh
./test.sh

$ zsh test.sh

echo
test.sh

$
18
Shawn J. Goff

이것은 bash, dash, ksh 및 zsh에서 나를 위해 일했습니다.

if test -n "$BASH" ; then script=$BASH_SOURCE
Elif test -n "$TMOUT"; then script=${.sh.file}
Elif test -n "$ZSH_NAME" ; then script=${(%):-%x}
Elif test ${0##*/} = dash; then x=$(lsof -p $$ -Fn0 | tail -1); script=${x#n}
else script=$0
fi

echo $script

이 쉘에 대한 출력 :

BASH source: ./myscript
ZSH source: ./myscript
KSH source: /home/pbrannan/git/theme/src/theme/web/myscript
DASH source: /home/pbrannan/git/theme/src/theme/web/myscript
BASH: ./myscript
ZSH: ./myscript
KSH: /home/pbrannan/git/theme/src/theme/web/myscript
DASH: ./myscript

Csh/tcsh에서 작동 시키려고했지만 너무 어렵습니다. POSIX를 고수하고 있습니다.

16
Paul Brannan

나는 커뮤니티 위키 답변 (숀 J. 고프의)에 약간 혼란 스러웠으므로 물건을 정리하는 스크립트를 작성했습니다. $_에 대해, 나는 이것을 발견했다 : 명령에 전달 된 환경 변수로서 _의 사용법 . 환경 변수이므로 값을 잘못 테스트하기 쉽습니다.

아래는 스크립트이며 출력입니다. 그들은 또한 이 요점 에 있습니다.

test-Shell-default-variables.sh

#!/bin/bash

# test-Shell-default-variables.sh

# Usage examples (you might want to `Sudo apt install zsh ksh`):
#
#  ./test-Shell-default-variables.sh dash bash
#  ./test-Shell-default-variables.sh dash bash zsh ksh
#  ./test-Shell-default-variables.sh dash bash zsh ksh | less -R

# `-R` in `less -R` to have less pass escape sequences directly to the terminal
# so we have colors.


# The "invoking with name `sh`" tests are commented because for every Shell I
# tested (dash, bash, zsh and ksh), the output was the same as that of dash.

# The `test_expression` function also work with expansion changes. You can try
# lines like `test_expression '{BASH_SOURCE:-$0}'`.

echolor() {
    echo -e "\e[1;[email protected]\e[0m"
}

tell_file() {
    echo File \`"$1"\` is:
    echo \`\`\`
    cat "$1"
    echo \`\`\`
    echo
}

Shell_ARRAY=("[email protected]")

test_command() {
    for Shell in "${Shell_ARRAY[@]}"
    do
        prepare "$Shell"
        cmd="$(eval echo $1)"
        # echo "cmd: $cmd"
        printf '%-4s: ' "$Shell"
        { env -i $cmd 2>&1 1>&3 | sed 's/^/[err]/'; } 3>&1
        teardown
    done
    echo
}

prepare () {
    Shell="$1"
    PATH="$PWD/$Shell/sh:$PATH"
}

teardown() {
    PATH="${PATH#*:}"
}


###
### prepare
###
for Shell in "${Shell_ARRAY[@]}"
do
    mkdir "$Shell"
    ln -sT "/bin/$Shell" "$Shell/sh"
done

echo > printer.sh
echo '. ./printer.sh' > sourcer.sh
rm linked.sh &>/dev/null; ln -sT "printer.sh" "linked.sh"

tell_file sourcer.sh

###
### run
###
test_expression() {
    local expr="$1"

    # prepare
    echo "echo $expr" > printer.sh
    tell_file printer.sh

    # run
    cmd='$Shell ./printer.sh'
    echolor "\`$cmd\` (simple invocation) ($expr):"
    test_command "$cmd"

    # cmd='sh ./printer.sh'
    # echolor "\`$cmd\` (when executable name is \`sh\`) ($expr):"
    # test_command "$cmd"

    cmd='$Shell ./sourcer.sh'
    echolor "\`$cmd\` (via sourcing) ($expr):"
    test_command "$cmd"

    # cmd='sh ./sourcer.sh'
    # echolor "\`$cmd\` (via sourcing, when name is \`sh\`) ($expr):"
    # test_command "$cmd"

    cmd='$Shell ./linked.sh'
    echolor "\`$cmd\` (via symlink) ($expr):"
    test_command "$cmd"

    # cmd='sh ./linked.sh'
    # echolor "\`$cmd\` (via symlink, when name is \`sh\`) ($expr):"
    # test_command "$cmd"

    echolor "------------------------------------------"
    echo
}

test_expression '$BASH_SOURCE'
test_expression '$0'
test_expression '$(/bin/true x y; true a b c; echo $_)' # Rq: true is a builtin
test_expression '$_'

###
### teardown
###
for Shell in "${Shell_ARRAY[@]}"
do
    rm "$Shell/sh"
    rm -d "$Shell"
done

rm sourcer.sh
rm linked.sh
rm printer.sh

./test-Shell-default-variables.sh {da,ba,z,k}sh 출력

File `sourcer.sh` is:
```
. ./printer.sh
```

File `printer.sh` is:
```
echo $BASH_SOURCE
```

`$Shell ./printer.sh` (simple invocation) ($BASH_SOURCE):
dash: 
bash: ./printer.sh
zsh : 
ksh : 

`$Shell ./sourcer.sh` (via sourcing) ($BASH_SOURCE):
dash: 
bash: ./printer.sh
zsh : 
ksh : 

`$Shell ./linked.sh` (via symlink) ($BASH_SOURCE):
dash: 
bash: ./linked.sh
zsh : 
ksh : 

------------------------------------------

File `printer.sh` is:
```
echo $0
```

`$Shell ./printer.sh` (simple invocation) ($0):
dash: ./printer.sh
bash: ./printer.sh
zsh : ./printer.sh
ksh : ./printer.sh

`$Shell ./sourcer.sh` (via sourcing) ($0):
dash: ./sourcer.sh
bash: ./sourcer.sh
zsh : ./printer.sh
ksh : ./sourcer.sh

`$Shell ./linked.sh` (via symlink) ($0):
dash: ./linked.sh
bash: ./linked.sh
zsh : ./linked.sh
ksh : ./linked.sh

------------------------------------------

File `printer.sh` is:
```
echo $(/bin/true x y; true a b c; echo $_)
```

`$Shell ./printer.sh` (simple invocation) ($(/bin/true x y; true a b c; echo $_)):
dash: 
bash: c
zsh : c
ksh : 

`$Shell ./sourcer.sh` (via sourcing) ($(/bin/true x y; true a b c; echo $_)):
dash: 
bash: c
zsh : c
ksh : 

`$Shell ./linked.sh` (via symlink) ($(/bin/true x y; true a b c; echo $_)):
dash: 
bash: c
zsh : c
ksh : 

------------------------------------------

File `printer.sh` is:
```
echo $_
```

`$Shell ./printer.sh` (simple invocation) ($_):
dash: 
bash: bash
zsh : 
ksh : 

`$Shell ./sourcer.sh` (via sourcing) ($_):
dash: 
bash: bash
zsh : ./printer.sh
ksh : 

`$Shell ./linked.sh` (via symlink) ($_):
dash: 
bash: bash
zsh : 
ksh : 

------------------------------------------

우리는 무엇을 배웠습니까?

$BASH_SOURCE

  • $BASH_SOURCE는 bash에서만 작동합니다.
  • $0와의 유일한 차이점은 현재 파일이 다른 파일에 의해 제공되었을 때입니다. 이 경우 $BASH_PROFILE는 소스 파일 이름이 아닌 소스 파일 이름을 포함합니다.

$0

  • Zsh에서 $0의 값은 bash의 $BASH_SOURCE와 같습니다.

$_

  • $_는 대시와 ksh로 그대로 유지됩니다.
  • Bash 및 zsh에서 $_는 마지막 호출의 마지막 인수로 감소합니다.
  • bash는 $_를 "bash"로 초기화합니다.
  • zsh는 $_를 그대로 둡니다. (소싱 할 때 "마지막 인수"규칙의 결과 일뿐입니다).

심볼릭 링크

  • 스크립트가 심볼릭 링크를 통해 호출되면 변수에는 링크 대상에 대한 참조가 포함되며 이름 만 포함됩니다.

ksh

  • 이러한 테스트와 관련하여 ksh는 대시처럼 동작합니다.

  • Bash 또는 zsh가 sh라는 심볼릭 링크를 통해 호출되면 해당 테스트와 관련하여 대시처럼 작동합니다.
2
Mathieu CAROFF

이 답변lsof와 약간의 grep 마술이 tcsh에서 중첩 된 소스 파일에 대해 작업 할 가능성이있는 유일한 방법을 설명합니다.

/usr/sbin/lsof +p $$ | grep -oE /.\*source_me.tcsh
0
Patrick Maupin

tl; dr script=$(readlink -e -- "${BASH_SOURCE}") ( bash 분명히)


$BASH_SOURCE 테스트 사례

주어진 파일 /tmp/source1.sh

echo '$BASH_SOURCE '"(${BASH_SOURCE})"
echo 'readlink -e $BASH_SOURCE'\
     "($(readlink -e -- "${BASH_SOURCE}"))"

다른 방식으로 파일 source

source from /tmp

$> cd /tmp

$> source source1.sh
$BASH_SOURCE (source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

$> source ./source1.sh
$BASH_SOURCE (./source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

$> source /tmp/source1.sh
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

source from /

cd /
$> source /tmp/source1.sh
$0 (bash)
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

다른 상대 경로에서 source/tmp/a/var

$> cd /tmp/a

$> source ../source1.sh
$BASH_SOURCE (../source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

$> cd /var

$> source ../tmp/source1.sh
$BASH_SOURCE (../tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)

$0

모든 경우에 스크립트에 추가 된 명령이있는 경우

echo '$0 '"(${0})"

source 스크립트는 항상 인쇄

$0 (bash)

그러나, 스크립트가 run 인 경우 (예 :.

$> bash /tmp/source1.sh

$0은 문자열 값입니다. /tmp/source1.sh.

$0 (/tmp/source1.sh)
$BASH_SOURCE (/tmp/source1.sh)
readlink -e $BASH_SOURCE (/tmp/source1.sh)
0

Bash Shell의 경우 @ Dennis Williamson의 답변 가장 도움이되었지만 Sudo의 경우에는 작동하지 않았습니다. 이것은 :

if ( [[ $_ != $0 ]] && [[ $_ != $Shell ]] ); then
    echo "I'm being sourced!"
    exit 1
fi
0
Matt

If 문을 사용하지 않고 bash 및 zsh 호환 스크립트를 만들려면 간단히 ${BASH_SOURCE[0]:-${(%):-%x}}을 작성하면됩니다. 결과 값은 정의 될 때 BASH_SOURCE[0]에서, BASH_SOURCE [0]이 정의되지 않은 경우 ${(%):-%x}}에서 가져옵니다.

0
dols3m

가장 어려운 부분은 현재 소스 파일을 찾는 것이 우분투에서 sh 교체로 사용되는 대시 쉘입니다. 다음 코드 스 니펫은 소스에서 제공되는 스크립트에서 절대 경로를 판별하는 데 사용될 수 있습니다. bash에서 테스트 한 zsh 및 dash는 dash 및 sh로 호출되었습니다.

NB : 최신 realpath (1) 유틸리티 from GNU coreutils 패키지에 따라 다름

NB : lsof (1) 옵션도 확인해야합니다.이 페이지와 다른 페이지의 비슷한 조언이 Ubuntu 18 및 19에서 저에게 효과적이지 않았으므로 이것을 다시 만들어야했습니다.

getShellName() {
    [ -n "$BASH" ] && echo ${BASH##/*/} && return
    [ -n "$ZSH_NAME" ] && echo $ZSH_NAME && return
    echo ${0##/*/}
}

getCurrentScript() {
    local result
    case "$(getShellName)" in
        bash )  result=${BASH_SOURCE[0]}
                ;;
        zsh )   emulate -L zsh
                result=${funcfiletrace[1]%:*}
                ;;
        dash | sh )
                result=$(
                    lsof -p $$ -Fn  \
                    | tail --lines=1  \
                    | xargs --max-args=2  \
                    | cut --delimiter=' ' --fields=2
                )
                result=${result#n}
                ;;
        * )     result=$0
                ;;
    esac
    echo $(realpath $result)
}
0
maoizm