“source file.sh”,“./file.sh”,“sh file.sh”,“를 사용하여 쉘 스크립트를 실행하는 것의 차이점은 무엇입니까? ./file.sh”?


13

코드를 살펴보십시오.

#!/bin/bash
read -p "Eneter 1 for UID and 2 for LOGNAME" choice
if [ $choice -eq 1 ]
then
        read -p "Enter UID:  " uid
        logname=`cat /etc/passwd | grep $uid | cut -f1 -d:`
else
        read -p "Enter Logname:  " logname
fi
not=`ps -au$logname | grep -c bash`
echo  "The number of terminals opened by $logname are $not"

이 코드는 같은 PC에서 사용자가 여는 터미널 수를 찾는 데 사용됩니다. 이제 x와 y라는 두 명의 사용자가 로그온했습니다. 현재 y로 로그인했으며 사용자 x에 3 개의 터미널이 열려 있습니다. 위에서 언급 한 것처럼 다른 방법으로 y 에서이 코드를 실행하면 결과는 다음과 같습니다.

$ ./file.sh
The number of terminals opened by x are 3

$ bash file.sh
The number of terminals opened by x are 5

$ sh file.sh
The number of terminals opened by x are 3

$ source file.sh
The number of terminals opened by x are 4

$ . ./file.sh
The number of terminals opened by x are 4

참고 : 나는이 모든 실행 파일에 1과 uid 1000을 전달했습니다.

이제이 모든 것의 차이점을 설명해 주시겠습니까?


차이점은 어떤 쉘이 실행되는지입니다. sh는 강타하지 않습니다
j0h

2
동일한 컨텍스트에서 실행 중이므로 마지막 두 실행도 다릅니다. 더 여기
자카 ELAB

(. 단말기의 여기가 더 동일) 나는 다른 사용자 (하지 우리가 로그인 동일한 사용자) 열 수 bash는 인스턴스를 계산하지하려고 다른 수는 각각의 경우에 온 이유를 설명 할 수
라마나 레디

@RamanaReddy 다른 사용자가 스크립트를 실행했거나 새 탭을 시작했을 수 있습니다. 누가 알아?
muru

답변:


21

유일한 주요 차이점은 스크립트 소싱과 실행 간의 차이입니다. source foo.sh소스와 당신이 보여주는 다른 모든 예제가 실행 중입니다. 더 자세하게:

  1. ./file.sh

    file.sh현재 디렉토리 ( ./) 에 있는 스크립트가 실행됩니다 . 일반적으로을 실행 command하면 셸은 디렉토리에서 $PATH라는 실행 파일을 찾습니다 command. 당신은 전체 경로를 제공하는 경우 등 /usr/bin/command이나 ./command, 그 다음은 $PATH무시되고 특정 파일이 실행됩니다.

  2. ../file.sh

    이는 ./file.sh현재 디렉토리를 찾는 대신 file.sh상위 디렉토리 ( ../) 를 찾는 것을 제외하고 는 기본적으로 동일 합니다.

  3. sh file.sh

    이 동등한에는 sh ./file.sh, 위와 같이이 호출 한 스크립트 실행 file.sh현재 디렉토리에 있습니다. 차이점은 sh쉘을 사용하여 명시 적으로 실행한다는 것입니다 . 우분투 시스템에서는 dash그렇지 않습니다 bash. 일반적으로 스크립트에는 실행해야하는 프로그램을 제공 하는 shebang 줄 이 있습니다. 다른 것으로 호출하면 재정의됩니다. 예를 들면 다음과 같습니다.

    $ cat foo.sh
    #!/bin/bash  
    ## The above is the shebang line, it points to bash
    ps h -p $$ -o args='' | cut -f1 -d' '  ## This will print the name of the shell

    이 스크립트는 단순히 실행에 사용 된 쉘의 이름을 인쇄합니다. 다른 방법으로 호출 할 때 반환되는 것을 보자.

    $ bash foo.sh
    bash
    $ sh foo.sh 
    sh
    $ zsh foo.sh
    zsh

    따라서 스크립트 호출을 호출 shell script하면 shebang 행 (있는 경우)을 무시하고 사용자가 지정한 쉘로 스크립트를 실행합니다.

  4. source file.sh 또는 . file.sh

    이것은 놀랍게도 스크립트를 소싱 하는 것입니다. 키워드 source는 쉘 내장 .명령 의 별명 입니다. 이것은 현재 쉘 내에서 스크립트를 실행하는 방법입니다. 일반적으로 스크립트가 실행될 때 현재 스크립트와 다른 자체 쉘에서 실행됩니다. 설명하기 위해 :

    $ cat foo.sh
    #!/bin/bash
    foo="Script"
    echo "Foo (script) is $foo"

    이제 변수 foo를 부모 셸에서 다른 것으로 설정하고 스크립트를 실행하면 스크립트가 다른 값을 인쇄 foo하지만 (스크립트 내에 설정되어 있기 때문에) foo부모 셸 의 값은 변경되지 않습니다.

    $ foo="Parent"
    $ bash foo.sh 
    Foo (script) is Script  ## This is the value from the script's shell
    $ echo "$foo"          
    Parent                  ## The value in the parent shell is unchanged

    그러나 스크립트를 실행하는 대신 소스를 지정하면 동일한 쉘에서 실행되므로 foo부모 의 값 이 변경됩니다.

    $ source ./foo.sh 
    Foo (script) is Script   ## The script's foo
    $ echo "$foo" 
    Script                   ## Because the script was sourced, 
                             ## the value in the parent shell has changed

    따라서 소싱은 스크립트가 실행중인 쉘에 영향을 줄 수있는 몇 가지 경우에 사용됩니다. 일반적으로 쉘 변수를 정의하고 스크립트가 완료된 후 사용 가능하게하는 데 사용됩니다.


모든 것을 염두에두고, 당신이 다른 대답을 얻는 이유는 무엇보다도 스크립트가 생각하는 것을하지 않기 때문입니다. bash의 출력에 나타나는 횟수를 계산합니다 ps. 이것은 열린 터미널의 수가 아니며 실행중인 쉘 의 수입니다 (사실, 그조차는 아니지만 다른 토론입니다). 명확히하기 위해 스크립트를 약간 단순화했습니다.

#!/bin/bash
logname=terdon
not=`ps -au$logname | grep -c bash`
echo  "The number of shells opened by $logname is $not"

그리고 하나의 터미널 만 열어서 다양한 방법으로 실행하십시오 :

  1. 직접 발사 ./foo.sh.

    $ ./foo.sh
    The number of shells opened by terdon is 1

    여기, 당신은 shebang 라인을 사용하고 있습니다. 이는 스크립트가 설정 한 내용에 따라 스크립트가 직접 실행됨을 의미합니다. 이는의 출력에 스크립트가 표시되는 방식에 영향을줍니다 ps. 대신에 상장되는 bash foo.sh, 그것은 단지로 표시됩니다 foo.sh당신의 그 어떤 수단 grep을 그리워합니다. 실제로는 3 개의 bash 인스턴스가 있습니다 : 부모 프로세스, 스크립트를 실행하는 bash ps명령 을 실행하는 다른 bash 인스턴스 . 마지막으로 중요합니다. 명령 대체 ( `command`또는 $(command))를 사용하여 명령을 실행하면 부모 셸의 복사본이 시작되고 명령이 실행됩니다. 그러나 여기서는 ps출력 을 표시 하는 방식으로 인해 이들 중 어느 것도 표시되지 않습니다 .

  2. 명시 적 (bash) 셸을 사용하여 직접 시작

    $ bash foo.sh 
    The number of shells opened by terdon is 3

    여기에서를 실행하고 있기 때문에 bash foo.sh의 출력 ps이 표시 bash foo.sh되고 계산됩니다. 따라서 여기에는 부모 프로세스, bash스크립트 실행 복제 된 셸 (을 실행 ps)이 모두 표시되므로 ps명령에 단어가 포함되므로 각각에 표시됩니다 bash.

  3. 다른 쉘로 직접 시작 ( sh)

    $ sh foo.sh
    The number of shells opened by terdon is 1

    당신이 스크립트를 실행하기 때문에이 다릅니다 sh하지 bash. 따라서 유일한 bash인스턴스는 스크립트를 시작한 상위 쉘입니다. 위에서 언급 한 다른 모든 쉘은 sh대신 실행 됩니다.

  4. 소싱 ( .또는 source같은 것)

    $ . ./foo.sh 
    The number of shells opened by terdon is 2

    위에서 설명한 것처럼 스크립트를 소싱하면 부모 프로세스와 동일한 셸에서 스크립트가 실행됩니다. 그러나 ps명령 을 시작하기 위해 별도의 서브 쉘이 시작되어 총 2 개가됩니다.


마지막으로 실행중인 프로세스를 계산하는 올바른 방법은 구문 분석이 ps아니라 사용하는 것 pgrep입니다. 당신이 방금 달리면 이러한 모든 문제를 피할 수 있었을 것입니다

pgrep -cu terdon bash

따라서 항상 올바른 숫자를 인쇄하는 작업 스크립트 버전은 다음과 같습니다 (명령 대체가 없음).

#!/usr/bin/env bash
user="terdon"

printf "Open shells:"
pgrep -cu "$user" bash

다른 모든 시작 방법에서는 소스가 공급되면 1이 반환되고 (스크립트를 실행하기 위해 새 bash가 시작되기 때문에) 2가 반환됩니다. sh자식 프로세스가 아니기 때문에 시작하면 여전히 1을 반환 bash합니다.


명령 대체가 상위 쉘의 사본을 시작한다고 할 때,이 사본이 ./foo.sh를 사용하여 스크립트를 실행할 때와 같이 서브 쉘과 어떻게 다른가요?
Didier A.

그리고 명령 대체없이 pgrep을 실행할 때 스크립트가 실행되는 것과 동일한 쉘에서 실행되고 있다고 가정합니까? 소싱과 비슷합니까?
Didier A.

@didibus 무슨 말인지 잘 모르겠습니다. 명령 대체는 서브 쉘에서 실행됩니다. ./foo.sh부모의 사본이 아닌 새 쉘에서 실행됩니다. 예를 들어 foo="bar"터미널에서 설정 한 다음을 실행하는 스크립트를 실행 echo $foo하면 스크립트 셸이 변수 값을 상속하지 않으므로 빈 줄이 표시됩니다. pgrep별도의 바이너리이며, 실행중인 스크립트에 의해 실행됩니다.
terdon

기본적으로 "명령 대체가 없음"에 대한 설명이 필요합니다. 스크립트에서 pgrep 바이너리를 실행하면 추가 쉘이 추가되지 않지만 명령 대체로 ps 바이너리를 실행하면 왜됩니까? 둘째, "부모 쉘의 복사"에 대한 설명이 필요합니다. 부모의 쉘 변수가 자식에 복사되는 하위 쉘과 같은 것입니까? 왜 명령 대체가 그렇게합니까?
Didier A.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.