매번 가져올 필요 없이 이 스크립트 파일의 기능을 어떻게 로드할 수 있습니까?
foo
실행하려는 스크립트 기능이 포함된 파일을 만들었습니다. PATH에 있습니다 /usr/bin
.
문서부자:
#!/bin/bash
echo "Running foo"
function x {
echo "x"
}
x
그러나 터미널에 함수 이름을 입력하면 다음과 같습니다.
x: command not found
입력하면 foo
다음
Running foo
이 표시됩니다(따라서 파일은 PATH에 있고 실행 가능합니다).
일단 입력하면 실행 기능을 source foo
입력할 수 있습니다 xx
나도 알아요, 이건 아주 기본적인 질문이에요. 나는 스크립트를 추상화하여 관리하기 쉽도록 하고 싶습니다(모든 것을 .profile 또는 .bashrc에 덤프하는 것과 비교하여).
답변1
문제는 .../bin
디렉토리의 스크립트가 exec
다른 쉘 환경에서 편집되었다는 것입니다. 해당 환경은 실행 후 지속되지 않으므로 완료 시 정의가 x() { ... ; }
현재 쉘 환경에서 지속되지 않습니다.
때를. ./somescript.sh
(또는 및 source
와 같은 일부 쉘에서 )bash
zsh
현재 쉘은 스크립트를 현재 환경으로 읽고 마치 프롬프트에서 발행된 것처럼 내용을 실행합니다. 이는 x() { ... ; }
현재 쉘 환경인 대화형 쉘에 정의되어 있습니다.
기본적으로 문제는 다음과 같이 증명될 수 있습니다.
sh <<\HEREDOC_CMD_FILE #runs sh shell with heredoc input file
{ #begins current shell compound expression
( #begins subshell compound expression
x() { echo 'I am function x.' ; } #define function x
x #x invoked in subshell
) #ends subshell compound expression
x #x invoked in current shell
} #ends current shell compound expression
#v_end_v
HEREDOC_CMD_FILE
###OUTPUT###
I am function x.
sh: line 6: x: command not found
마찬가지로 ( : subshell )
위의 예에 정의된 환경은 완료 후에도 유지되지 않으며 실행하는 스크립트에 정의된 환경도 유지되지 않습니다. 마찬가지로 sh
읽어들일 때 HEREDOC_CMD_FILE
사용자와 동일한 기능을 수행 source ../file
하고 현재 쉘 실행 환경에서 내용을 실행합니다. 비록 해당 환경이 이를 실행한 프롬프트를 발행한 쉘의 하위 쉘 하위이므로 해당 환경에서는 아무것도 수행되지 않습니다. 거기에서도. 그러나 다음과 같이 그렇게 할 수 있습니다.
. /dev/fd/0 <<\HEREDOC_CMD_FILE && x
x() { echo 'I am function x.' ; }
HEREDOC_CMD_FILE
###OUTPUT###
I am function x.
...이것은 당신이 하고 있는 일과 거의 똑같습니다. source ${script}
단, 여기에서는 .dot
디스크에 있는 파일의 내용을 가져오는 반면 stdin
, 당신은 디스크에 있는 파일의 내용을 가져옵니다.
그러나 실행 스크립트와 실행 스크립트의 주요 차이점 ( : subshell )
은 실행 스크립트의 실행 환경이 어떻게 생겼는지입니다. 스크립트를 실행하면새로운운영 환경 - 따라서 명령줄에서 명시적으로 내보내거나 선언하지 않는 한 현재 셸에서 선언된 모든 변수는 해당 환경으로 전송되지 않습니다. 이 동작은 다음과 같이 설명할 수 있습니다.
{ x() { printf "$FMT" "$0" "$var2" x ; } #describe env
export FMT='argv0: %s\t\tvar2: %s\t\tI am function %s.\n' \
var1=val1 #var1 and FMT are explicitly exported
var2=shell_val2 #var2 is not
cat >./script && #read out stdin to >./script
chmod +x ./script && #then make ./script executable
var3=val3 ./script #then define var3 for ./script's env and execute
} <<\SCRIPT ; x ; y #send heredoc to block's stdin then call {x,y}()
#!/usr/bin/sh
#read out by cat to >./script then executed in a separate environment
y() { printf "$FMT" "$0" "$var2" y ; } #describe env
echo "${var1:-#var1 is unset or null}" #$var1 if not unset or null else :-this}
echo "${var2:-#var2 is unset or null}"
echo "${var3:-#var3 is unset or null}"
export var2=script_val2
x ; y #run x() ; y() in script
#v_end_v
SCRIPT
###OUTPUT###
val1
#var2 is unset or null
val3
./script: line 8: x: command not found
argv0: ./script var2: script_val2 I am function y.
argv0: sh var2: shell_val2 I am function x.
sh: line 18: y: command not found
이 동작은 프롬프트에서 실행하거나 현재 셸의 자식으로 실행하는 것과 다릅니다. 즉 ( : subshells )
, 위에서 언급한 것처럼 실행 중인 자식은 명시적으로 편집해야 하는 반면 부모의 환경을 자동으로 상속한다는 점입니다 export
.
var1=val1 ; export var2=val2
x() { echo 'I am function x.' ; }
(
printf '%s\n' "$var1" "$var2"
x
)
###OUTPUT###
val1
val2
I am function x.
.dot
마지막으로 주목하고 싶은 점은 현재 셸에서 하는 것처럼 실행된 스크립트에 함수 정의가 포함된 파일을 가져오지 않고 상위 셸에서 실행된 스크립트로 함수를 내보내는 이식 가능한 방법이 없다는 것입니다 . 기본적으로 변수를 사용하는 것처럼 이식 source
할 수 없습니다 . 그러나 실행 중인 스크립트 에 export function
귀하가 있을 수 있다고 생각합니다 . 물론 하위 환경(실행 중이든 아니든)은 하위 환경과 함께 사라집니다.export fn_def='fn() { : fn body ; }'
eval "$fn_def"
따라서 스크립트에 정의된 함수를 원하지만 스크립트 자체를 가져오고 싶지 않은 경우 함수를 일부 명령 및 해당 출력으로 읽어야 합니다 eval
.
eval "$(cat ./script)"
하지만 본질적으로 똑같습니다 . ./script
. 단지 덜 효율적일 뿐입니다. 그냥 받아도 됩니다.
이를 수행하는 가장 좋은 방법은 스크립트 자체에서 함수 정의를 제거하고 자체 파일에 넣은 다음 필요할 때 스크립트와 현재 셸 모두에서 가져오는 것입니다. 이와 같이:
{
echo 'x() { echo "I am function x and my argv0 is ${0}." ; }' >./fn_x
echo '. ./fn_x ; x' >./script
chmod +x ./script && ./script
. ./fn_x ; x
}
###OUTPUT###
I am function x and my argv0 is ./script.
I am function x and my argv0 is sh.
대화형 셸에서 항상 사용할 수 있도록 하려면 . /path/to/fn_x
셸 ENV
파일에 를 추가하세요. 예를 들어 에 있는 함수가 포함된 스크립트의 경우 bash
다음 행을 다음에 추가합니다.foo
/usr/bin
~/.bashrc
. /usr/bin/foo
시작하는 동안 어떤 이유로 인해 해당 위치에서 스크립트를 사용할 수 없는 경우 쉘은 ENV
예상대로 파일의 나머지 부분을 읽고 가져오지만 stderr
함수 정의 파일을 가져오는 데 문제가 있음을 알려주는 진단 메시지를 인쇄합니다.
답변2
마이크 셀프의 답변"비하인드 스토리"에 대한 자세한 내용은 좋지만 정확한 제목 질문에 대한 간단하고 유용한 답변이 포함되어 있지 않기 때문에 여기에 또 다른 답변이 필요하다고 생각합니다.
매번 가져올 필요 없이 이 스크립트 파일의 기능을 어떻게 로드할 수 있습니까?
대답은 다음과 같습니다. 실행하는 모든 셸에서 사용할 수 있도록 .bashrc
사용자로부터 가져오는 것입니다 ..bash_profile
예를 들어, 내 안에는 다음이 있습니다 .bash_profile
.
if [ -d ~/.bash_functions ]; then
for file in ~/.bash_functions/*.sh; do
. "$file"
done
fi