“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.