rc.local이 모든 명령을 실행하지 않는 이유는 무엇이며 어떻게해야합니까?


35

다음 rc.local스크립트가 있습니다.

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

sh /home/incero/startup_script.sh
/bin/chmod +x /script.sh
sh /script.sh

exit 0

첫 번째 줄은 startup_script.sh실제로 세 번째 줄에 언급 된 script.sh파일을 다운로드합니다 /script.sh.

불행히도 스크립트를 실행 가능하게 만들거나 실행하지 않는 것으로 보입니다. rc.local시작한 후 파일을 수동으로 실행하면 완벽하게 작동합니다. 시작할 때 chmod를 실행할 수 없습니까?

답변:


70

빠른 수정 으로 건너 뛸 수 있지만 반드시 최선의 선택은 아닙니다. 먼저이 모든 것을 읽는 것이 좋습니다.

rc.local 오류를 용납하지 않습니다.

rc.local지능적으로 오류를 복구 할 수있는 방법을 제공하지 않습니다. 명령이 실패하면 실행이 중지됩니다. 첫 번째 줄 #!/bin/sh -e-e플래그 와 함께 호출 된 쉘에서 실행되도록합니다 . -e플래그 (이 경우 스크립트를 만드는 것입니다 rc.local명령이 그 안에 실패 처음 실행을 중지).

당신이 원하는 rc.local이런 식으로 행동 할 수 있습니다. 명령이 실패하면, 당신은 할 수 없습니다 그것이 다른 시작 명령이 성공하는 데 그것에 의존 될 수 무엇 이건 계속하고 싶다.

따라서 명령이 실패하면 후속 명령이 실행되지 않습니다. 여기서 문제는 /script.sh실행되지 않은 것입니다 (실패하지 않음, 아래 참조). 실패하기 전에 명령이있을 가능성이 큽니다. 그러나 어느 것?

그렇습니까 /bin/chmod +x /script.sh?

아니.

chmod언제든지 잘 작동합니다. 포함 /bin된 파일 시스템 이 마운트 된 경우을 실행할 수 있습니다 /bin/chmod. 그리고 달리기 /bin전에 장착 rc.local됩니다.

루트로 실행할 때 /bin/chmod거의 실패하지 않습니다. 작동하는 파일이 읽기 전용 인 경우 실패하며, 파일 시스템이 사용 권한을 지원하지 않으면 실패 할 수 있습니다 . 여기도 없을 것입니다.

그건 그렇고, 실패 하면 실제로 문제 sh -e가되는 유일한 이유 chmod입니다. 인터프리터를 명시 적으로 호출하여 스크립트 파일을 실행할 때 파일이 실행 파일로 표시되어 있는지는 중요하지 않습니다. /script.sh파일의 실행 비트가 중요 하다고 말한 경우에만 . 그것은이라고 말하기 때문에 sh /script.sh(물론 실행되는 동안 /script.sh 스스로 호출 하지 않는 한 실행 가능한 것이 아니라 실패 할 수는 있지만 스스로 호출하지는 않습니다).

무엇이 실패 했습니까?

sh /home/incero/startup_script.sh실패한. 거의 확실합니다.

다운로드했기 때문에 실행되었음을 알고 있습니다 /script.sh.

(그렇지 않으면 경우에 어떻게 든, 그것을 실행 않았다 있는지 확인하는 것이 중요 할 것 /bin아니 었 PATH - rc.local반드시 동일하지 않습니다 PATH. 당신이 로그인 할 때 가지고있는 경우가 /bin에없는 rc.local'의 경로,이 필요 sh로 실행하는 /bin/sh. 그것이 실행 않았기 때문에, /binPATH, 당신은에있는 다른 명령을 실행할 수 있습니다 의미 /bin완벽하게 자신의 이름을 미치지 않는. 예를 들어, 당신은 단지 실행할 수 있습니다 chmod보다는 /bin/chmod.하지만, 당신의 스타일과 유지에 에서 실행을 제안 할 때마다를 rc.local제외한 모든 명령에 정규화 된 이름을 사용 sh했습니다.)

우리는 /bin/chmod +x /script.sh절대로 실행되지 않을 것이라고 확신 할 수 있습니다 (또는 /script.sh실행 된 것을 볼 수 있습니다 ). 그리고 우리 sh /script.sh는 또한 실행되지 않았다는 것을 알고 있습니다.

그러나 그것은 다운로드했다 /script.sh. 성공했습니다! 어떻게 실패 할 수 있습니까?

성공의 두 가지 의미

명령이 성공했다고 말할 때 두 가지 다른 의미가 있습니다.

  1. 그것은 당신이 원하는 것을했습니다.
  2. 성공했다고보고했다.

그리고 그것은 실패를위한 것입니다. 사람이 명령이 실패했다고 말하면 다음을 의미 할 수 있습니다.

  1. 그것은 당신이 원하는 것을하지 않았습니다.
  2. 실패했다고보고했습니다.

sh -e같이 스크립트를 실행 rc.local하면 명령이 처음 실패했다고보고 할 때 실행이 중지됩니다 . 명령이 실제로 수행 한 것과 차이가 없습니다.

당신이 의도하지 않는 한 startup_script.sh당신이 원하는 것을 할 때 보고서 오류로,이 버그가 있습니다 startup_script.sh.

  • 일부 버그는 스크립트가 원하는 작업을 수행하지 못하게합니다. 이것은 프로그래머가 부작용 이라고 부르는 것에 영향을줍니다 .
  • 또한 일부 버그로 인해 스크립트의 성공 여부에 관계없이 스크립트가 올바르게보고되지 않습니다. 프로그래머가 리턴 값 (이 경우 종료 상태 ) 이라고하는 것에 영향을줍니다 .

실패했다고보고 한 것을 제외하고startup_script.sh 는 모든 것을 다 했을 가능성이 높습니다 .

성공 또는 실패보고 방법

스크립트는 0 개 이상의 명령 목록입니다. 각 명령에는 종료 상태가 있습니다. 실제로 스크립트를 실행하는 데 실패가 없다고 가정하면 (예 : 인터프리터가 스크립트를 실행하는 동안 다음 줄을 읽을 수없는 경우) 스크립트의 종료 상태는 다음과 같습니다.

  • 0 (성공) 스크립트가 비어있는 경우 (즉, 명령이없는 경우)
  • N, 명령의 결과로 스크립트가 종료 된 경우 종료 코드는 어디에 있습니까 ?exit NN
  • 그렇지 않으면 스크립트에서 실행 된 마지막 명령의 종료 코드입니다.

실행 파일이 실행될 때 자체 종료 코드를보고합니다. 스크립트 만이 아닙니다. (기술적으로 스크립트의 종료 코드는 스크립트를 실행하는 셸에서 반환 한 종료 코드입니다.)

예를 들어, C 프로그램이로 끝나 exit(0);거나 return 0;main()기능을 수행하는 경우 코드 0는 운영 체제에 제공되어 호출 프로세스 (예 : 프로그램이 실행 된 쉘일 수 있음)를 제공합니다.

0프로그램이 성공했음을 의미합니다. 다른 숫자는 모두 실패했음을 의미합니다. (이 방법으로, 다른 숫자는 때때로 프로그램이 실패한 다른 이유를 나타낼 수 있습니다.)

실패한 명령

때로는 실패 할 것이라는 의도로 프로그램을 실행하기도합니다. 이러한 상황에서는 프로그램이 실패를보고하는 버그가 아니지만 실패를 성공으로 생각할 수 있습니다. 예를 들어, rm이미 존재하지 않는 것으로 의심되는 파일을 삭제했는지 확인하기 위해 사용할 수 있습니다.

startup_script.sh실행이 중단되기 직전에 이런 일이 일어나고있을 것입니다 . 스크립트에서 실행 되는 마지막 명령 은 실패를보고하는 것입니다 ( "실패"가 완전히 양호하거나 필요한 경우에도 해당).

실패한 테스트

특별한 종류의 명령 중 하나 는 test 인데, 이는 부작용보다는 반환 값에 대한 명령 실행을 의미합니다. 즉, 테스트는 종료 상태를 검사 (및 조치) 할 수 있도록 실행되는 명령입니다.

예를 들어, 4가 5 인 경우를 잊어 버렸다고 가정 해 봅시다.

if [ 4 -eq 5 ]; then
    echo "Yeah, they're totally the same."
fi

여기서 테스트 [ -eq 5 ]는 결국 4 ≠ 5이기 때문에 실패합니다. 그렇다고 테스트가 제대로 수행되지 않았다는 의미는 아닙니다. 그게했다. 4 = 5인지 확인한 다음 성공하면보고하고 그렇지 않은 경우 실패를보고했습니다.

쉘 스크립팅에서 성공은 true 를 의미 하고 실패는 false 를 의미 할 수 있습니다 .

echo명령문이 실행되지 않더라도 if전체 블록은 성공을 리턴합니다.

그러나 더 짧게 작성했다고 가정합니다.

[ 4 -eq 5 ] && echo "Yeah, they're totally the same."

이것은 일반적인 속기입니다. &&A는 부울 연산자. &&로 구성되어 표현, &&양쪽이 참 (성공)을 반환하지 않는 양쪽에 문장과 거짓 (실패)을 반환합니다. 그냥 정상처럼 하고 .

누군가가 당신에게 묻는다면, "데릭이 쇼핑몰에 가서 나비를 생각하셨습니까?" 데릭이 쇼핑몰에 가지 않았다는 것을 알고 있다면, 나비를 생각하는지 알아낼 필요가 없습니다.

마찬가지로 왼쪽의 명령이 &&실패하면 (false) 전체 &&표현식이 즉시 실패합니다 (false). 오른쪽에있는 문장 &&은 실행되지 않습니다.

여기에, [ 4 -eq 5 ]실행됩니다. "실패"(거짓을 반환). 따라서 전체 &&표현이 실패합니다. echo "Yeah, they're totally the same."절대로 실행되지 않습니다. 모든 것이 정상적으로 동작하지만이 명령은 실패를 보고합니다 ( if위의 조건 과 동등한 조건이 성공 하더라도 ).

이것이 스크립트의 마지막 문장 (그리고 스크립트가 어느 시점 이전에 종료되지 않고 스크립트에 도달 한 경우)이면 전체 스크립트는 failure를 보고 합니다.

이 외에도 많은 테스트가 있습니다. 예를 들어, ||( "또는") 테스트가 있습니다. 그러나 위의 예제는 테스트가 무엇인지 설명하기에 충분해야하며 문서를 효과적으로 사용하여 특정 명령문 / 명령이 테스트인지 판별 할 수 있습니다.

shvs. sh -e, 재 방문

이후 라인 (참조 : 이 질문 의 상단에있는) 가 이 명령을 호출하는 것처럼 운영 시스템은 스크립트를 실행합니다 :#!/etc/rc.localsh -e

sh -e /etc/rc.local

대조적으로와 같은 다른 스크립트 는 플래그 없이startup_script.sh 실행 됩니다-e .

sh /home/incero/startup_script.sh

결과적으로 명령이 실패를보고하더라도 계속 실행됩니다.

이것은 정상적이고 좋습니다. rc.local로 실행해야하며 sh -e대부분의 다른 스크립트 (예 : 대부분의 스크립트 포함)는 rc.local호출하지 않아야합니다.

차이점을 기억하십시오.

  • 스크립트에 sh -e포함 된 명령 이 종료보고 실패를 처음 시작할 때 스크립트는 종료보고 실패로 실행 됩니다.

    마치 스크립트가 &&연산자 와 결합 된 스크립트의 모든 명령으로 구성된 단일 긴 명령 인 것처럼 보입니다.

  • sh(없이 -e) 스크립트는 스크립트 를 종료 (종료)하는 명령이 나올 때까지 또는 스크립트가 끝날 때까지 계속 실행됩니다. 모든 명령의 성공 또는 실패는 본질적으로 관련이 없습니다 (다음 명령이 확인하지 않는 한). 스크립트는 마지막 명령 실행의 종료 상태로 종료됩니다.

스크립트가 결국 실패가 아님을 이해하도록 돕는 것

실패했을 때 스크립트가 실패했다고 생각하지 않게하려면 어떻게해야합니까?

당신은 실행이 끝나기 직전에 무슨 일이 일어나는지 봅니다.

  1. 명령이 성공했을 때 실패한 경우 이유를 파악하고 문제를 해결하십시오.

  2. 명령이 실패했고 이것이 올바르게 발생했다면 해당 실패 상태가 전파되는 것을 방지하십시오.

    • 실패 상태가 전파되지 않도록하는 한 가지 방법은 성공한 다른 명령을 실행하는 것입니다. /bin/true부작용이 없으며 성공을보고합니다 ( /bin/false아무것도하지 않고 실패 함).

    • 다른 하나는로 스크립트를 종료시키는 것 exit 0입니다.

      그것은 반드시 exit 0스크립트의 끝에 있는 것과 반드시 ​​같은 것은 아닙니다 . 예를 들어, if스크립트가 내부에서 종료 되는 -block 이있을 수 있습니다 .

성공을보고하기 전에 스크립트가 실패를보고하는 원인을 아는 것이 가장 좋습니다. 실제로 어떤 방식 으로든 실패한 경우 (원하는 작업을 수행하지 않는다는 의미에서) 성공을보고하기를 원하지 않습니다.

빠른 수정

startup_script.sh엑시트보고 성공을 할 수없는 경우 , rc.local실행 보고 명령을 변경하여 명령이 실패하더라도 성공을 보고 할 수 있습니다 startup_script.sh.

현재 당신은 :

sh /home/incero/startup_script.sh

이 명령은 동일한 부작용 (예 : running의 부작용 startup_script.sh)이 있지만 항상 성공을보고합니다.

sh /home/incero/startup_script.sh || /bin/true

startup_script.sh보고서가 실패한 이유를 알고 수정하는 것이 좋습니다.

빠른 수정의 작동 방식

이것은 실제로 ||테스트, 또는 테스트 의 예입니다 .

내가 쓰레기를 꺼내거나 부엉이를 닦았는지 물어 보자. 쓰레기를 꺼내면 올빼미를 닦았는지 여부를 기억하지 않아도 진실로 "예"라고 말할 수 있습니다.

왼쪽에있는 명령이 ||실행됩니다. 성공하면 (true) 오른쪽을 실행할 필요가 없습니다. 따라서 startup_script.sh성공을보고하면 true명령 이 실행되지 않습니다.

그러나 startup_script.sh실패를보고 하면 (쓰레기를 꺼내지 않았다) , /bin/true [부엉이를 닦으면] 의 결과가 중요합니다.

/bin/true항상 성공을 반환합니다 (또는 때때로 호출하기 때문에 true ). 결과적으로 전체 명령이 성공하고 다음 명령 rc.local이 실행될 수 있습니다.

성공 / 실패, 참 / 거짓, 0 / 제로에 대한 추가 참고 사항.

이것을 무시하십시오. 그러나 둘 이상의 언어로 프로그래밍하는 경우 (예를 들어 쉘 스크립팅이 아니라)이를 읽을 수 있습니다.

C와 같은 프로그래밍 언어를 사용하는 쉘 스크립터와 쉘 스크립팅을 사용하는 C 프로그래머는 다음을 발견하는 데 큰 혼란이 있습니다.

  • 쉘 스크립팅에서 :

    1. 반환 값은 success 및 / 또는 true0의미 합니다 .
    2. 이외의 반환 값은 failure 및 / 또는 false를0 의미 합니다.
  • C 프로그래밍에서 :

    1. 반환 값은 false0의미합니다 .
    2. 이외의 반환 값은 true를0 의미 합니다 .
    3. 성공의 의미와 실패의 의미에 대한 간단한 규칙은 없습니다. 때로는 0성공, 다른 경우는 실패, 다른 경우는 방금 추가 한 두 숫자의 합이 0임을 의미합니다. 일반 프로그래밍에서 반환 값은 다양한 종류의 정보를 알리는 데 사용됩니다.
    4. 프로그램의 숫자 종료 상태는 쉘 스크립팅 규칙에 따라 성공 또는 실패를 표시해야합니다. 즉, 0C 프로그램에서 false를 의미 하더라도 프로그램 0이 성공했다고보고하려면 프로그램 이 종료 코드로 리턴하게합니다 (쉘이 true 로 해석 함 ).

3
와우 좋은 답변! 거기에 내가 몰랐던 많은 정보가 있습니다. 첫 번째 스크립트의 끝에서 0을 반환하면 나머지 스크립트가 실행됩니다. (chmod + x가 실행되었음을 알 수 있습니다). 불행히도 내 스크립트의 / usr / bin / wget이 script.sh에 넣을 웹 페이지를 검색하지 못하는 것 같습니다. 이 rc.local 스크립트가 실행될 때까지 네트워크가 준비되지 않았다고 생각합니다. 네트워크가 시작될 때, 그리고 시작할 때 자동으로 실행되도록이 명령을 어디에 둘 수 있습니까?
Programster

sudo visudo를 실행하고 다음 줄을 추가하여 '해킹'했습니다. username ALL = (ALL) NOPASSWD : 'startup applications'프로그램을 실행하는 모든 (이에 대한 CLI 명령은 무엇입니까?), 이것에 startup_script를 추가하면, startupscript는 이제 root로 실행하기 위해 sudo 명령과 함께 script.sh를 실행합니다 (더 이상 암호가 필요하지 않음). 의심 할 여지없이 이것은 매우 안전하지 않으며 끔찍한 것이지만 이것은 사용자 정의 pxe-boot 라이브 배포 디스크를위한 것입니다.
Programster

@ Stu2000 가정 incero(사용자 이름이라고 가정) 은 어쨌든 관리자 이므로 root(자신의 사용자 암호를 입력하여) 어떤 명령도 실행할 수 있으므로 매우 안전하지 않으며 끔찍하지 않습니다 . 다른 솔루션을 원하면 이에 대한 새로운 질문을 게시하는 것이 좋습니다 . 문제 중 하나는에있는 명령이 rc.local실행되지 않은 이유였습니다 (그리고 명령을 계속 실행하면 어떤 결과가 발생하는지 확인했습니다). 이제 다른 문제가 있습니다. rc.local실행 전에 시스템을 네트워크에 연결 하거나 startup_script.sh나중에 실행을 예약 하려고합니다 .
Eliah Kagan

1
그 후이 문제로 돌아와서 rc.local을 편집했으며 wget이 '작동하지 않습니다. /usr/bin/sudo service networking restartwget 명령 전에 시작 스크립트에 추가하면 wget이 작동합니다.
Programster

3
@EliahKagan 훌륭한 철저한 답변입니다. 나는 거의에 더 나은 문서를 가로 질러 오지rc.local
souravc

1

유닉스 변형에서 다음을 변경하여 기본 동작을 무시할 수 있습니다.

#!/bin/sh -e

에:

#!/bin/sh

스크립트 시작시 "-e"플래그는 sh에게 첫 번째 오류 발생시 종료하도록 지시합니다. 이 플래그의 긴 이름은 "errexit"이므로 원래 줄은 다음과 같습니다.

#!/bin/sh --errexit

예, -e라즈 비안 제시에서 작업을 제거 합니다.
Oki Erie Rinaldi 12

-e이유가있다. 내가 당신이라면 그렇게하지 않을 것입니다. 그러나 나는 당신의 부모가 아닙니다.
Wyatt8740

-4

첫 번째 줄에서 변경 : #!/bin/sh -e!/bin/sh -e


3
와의 구문 #!이 올바른 구문입니다 . (최소한의 #!부분은 올바른 것입니다. 그리고 나머지는 보통을 위해, 잘 작동합니다 rc.local.)를 참조하십시오 이 문서이 질문을 . 어쨌든 Ask Ubuntu에 오신 것을 환영합니다!
Eliah Kagan
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.