리눅스는 쉘 스크립트를 어떻게 다루는가?


22

이 질문에 대해 bash 쉘 스크립트를 고려해 봅시다.이 질문은 모든 유형의 쉘 스크립트에 적용 가능해야합니다.

누군가가 쉘 스크립트를 실행할 때 Linux는 모든 스크립트를 한 번에 (메모리에)로드합니까 아니면 스크립트 명령을 하나씩 읽습니다 (한 줄씩)?

즉, 실행이 완료되기 전에 쉘 스크립트를 실행하고 삭제하면 실행이 종료됩니까, 아니면 그대로 계속됩니까?


3
시도 해봐. (계속 될 것입니다)
devnull

1
@devnull 실제로 흥미로운 질문이 있습니다. 테스트를 계속할 것인지 여부는 사소한 것이지만 바이너리 파일 (메모리에로드되는)과 shebang 줄이있는 스크립트 또는 shebang 줄이없는 스크립트 사이에는 차이가 있습니다.
terdon


23
실제 의도의 목적을 위해, 실행 중에 쉘 스크립트를 삭제하려는 경우 한 번에 또는 한 줄씩 읽은 것인지는 중요하지 않습니다. 유닉스에서, 마지막으로 열린 파일이 닫힐 때까지 실제로 inode는 (디렉토리에서 링크가없는 경우에도) 삭제되지 않습니다. 다시 말해서, 실행 중에 쉘이 쉘 스크립트를 한 줄씩 읽어도 여전히 삭제하는 것이 안전합니다. 쉘이 매번 쉘 스크립트를 닫았다가 다시 여는 종류 인 경우는 예외이지만, 그렇게하면 훨씬 더 큰 (보안) 문제가 있습니다.
Chris Jester-Young

답변:


33

사용 strace하면 쉘 스크립트가 실행될 때 어떻게 실행되는지 볼 수 있습니다.

이 쉘 스크립트가 있다고 가정 해보십시오.

$ cat hello_ul.bash 
#!/bin/bash

echo "Hello Unix & Linux!"

다음을 사용하여 실행하십시오 strace.

$ strace -s 2000 -o strace.log ./hello_ul.bash
Hello Unix & Linux!
$

strace.log파일 내부를 살펴보면 다음을 알 수 있습니다.

...
open("./hello_ul.bash", O_RDONLY)       = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff0b6e3330) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 80) = 40
lseek(3, 0, SEEK_SET)                   = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
fcntl(255, F_GETFD)                     = -1 EBADF (Bad file descriptor)
dup2(3, 255)                            = 255
close(3)     
...

파일을 읽고 나면 다음과 같이 실행됩니다.

...
read(255, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 40) = 40
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc0b38ba000
write(1, "Hello Unix & Linux!\n", 20)   = 20
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(255, "", 40)                       = 0
exit_group(0)                           = ?

위에서 우리는 전체 스크립트가 하나의 엔티티로 읽혀지고 나서 실행 된 것으로 보인다는 것을 분명히 알 수 있습니다. 따라서 적어도 Bash의 경우 파일을 읽은 다음 실행하는 경우 "나타납니다" . 스크립트가 실행되는 동안 편집 할 수 있다고 생각하십니까?

참고 : 하지 마십시오! 실행중인 스크립트 파일을 엉망으로 만들 수없는 이유를 이해하려면 계속 읽으십시오.

다른 통역사는 어떻습니까?

그러나 귀하의 질문은 약간 벗어났습니다. 파일의 내용을 반드시로드하는 것은 Linux가 아니며, 내용을로드하는 인터프리터이므로 파일을 한 번에 전체 또는 블록 또는 라인으로로드하는지 여부는 실제로 인터프리터가 어떻게 구현되는지에 달려 있습니다.

왜 파일을 편집 할 수 없습니까?

그러나 훨씬 더 큰 스크립트를 사용하면 위의 테스트가 약간 잘못되었다는 것을 알 수 있습니다. 실제로 대부분의 인터프리터는 파일을 블록으로로드합니다. 이것은 파일의 블록을로드하고 처리 한 다음 다른 블록을로드하는 많은 유닉스 도구에서 매우 표준입니다. 이 행동은 얼마 전에 글 grep: grep / egrep이 매번 얼마나 많은 텍스트를 소비합니까?에 관해 쓴 U & L Q & A에서 볼 수 있습니다 . .

다음과 같은 쉘 스크립트를 작성한다고 가정하십시오.

$ ( 
    echo '#!/bin/bash'; 
    for i in {1..100000}; do printf "%s\n" "echo \"$i\""; done 
  ) > ascript.bash;
$ chmod +x ascript.bash

이 파일의 결과 :

$ ll ascript.bash 
-rwxrwxr-x. 1 saml saml 1288907 Mar 23 18:59 ascript.bash

다음과 같은 유형의 콘텐츠가 포함됩니다.

$ head -3 ascript.bash ; echo "..."; tail -3 ascript.bash 
#!/bin/bash
echo "1"
echo "2"
...
echo "99998"
echo "99999"
echo "100000"

이제 위와 동일한 기술을 사용하여 이것을 실행할 때 strace:

$ strace -s 2000 -o strace_ascript.log ./ascript.bash
...    
read(255, "#!/bin/bash\necho \"1\"\necho \"2\"\necho \"3\"\necho \"4\"\necho \"5\"\necho \"6\"\necho \"7\"\necho \"8\"\necho \"9\"\necho \"10\"\necho 
...
...
\"181\"\necho \"182\"\necho \"183\"\necho \"184\"\necho \"185\"\necho \"186\"\necho \"187\"\necho \"188\"\necho \"189\"\necho \"190\"\necho \""..., 8192) = 8192

파일을 8KB 단위로 읽는다는 것을 알 수 있으므로 Bash 및 기타 셸은 파일을 전체적으로로드하지 않고 블록 단위로 읽습니다.

참고 문헌


@ terdon-예, 그 Q & A를 전에 본 것을 기억합니다.
slm

5
40 바이트 스크립트를 사용하면 한 블록으로 읽습니다. > 8kB 스크립트로 시도하십시오.
Gilles 'SO- 악마 그만'

나는 시도하지 않았지만 실제로 모든 프로세스가 제거 된 파일과 관련된 파일 설명자를 닫을 때까지 파일 제거가 수행되지 않는다고 생각하므로 bash는 제거 된 파일에서 계속 읽을 수 있습니다.
Farid Nouri Neshat

@Gilles-예, 예를 추가했습니다.
slm

2
이 동작은 버전에 따라 다릅니다. bash 버전 3.2.51 (1)-릴리스로 테스트했으며 현재 줄을지나 버퍼링하지 않는다는 것을 알았습니다 ( 이 stackoverflow 답변 참조 ).
Gordon Davisson

11

이것은 OS 의존성보다 쉘 의존성입니다.

버전 ksh에 따라 요청시 8k 또는 64k 바이트 블록으로 스크립트를 읽습니다.

bash스크립트를 한 줄씩 읽으십시오. 그러나 사실 행은 임의 길이 일 수 있으므로 다음 행의 시작 부분부터 8176 바이트마다 구문 분석 할 때마다 읽습니다.

간단한 구성, 즉 일반 명령 모음입니다.

루프, 스위치, here 문서, 괄호로 묶인 서브 쉘, 함수 정의 등과 같은 쉘 구조화 된 명령이 사용 되는 경우 ( 응답이 누락 된 경우 ) 쉘 인터프리터가 읽습니다. 구문 오류가 없는지 먼저 확인하십시오.for/do/donecase/esac

동일한 코드를 여러 번 반복해서 읽을 수 있지만이 내용이 정상적으로 캐시된다는 사실로 인해 완화되므로 다소 비효율적입니다.

쉘 인터프리터가 무엇이든, 쉘이 스크립트의 일부를 자유롭게 읽을 수 있으므로 쉘 스크립트가 실행되는 동안 쉘 스크립트를 수정하는 것은 현명하지 않으며 동기화되지 않은 경우 예기치 않은 구문 오류가 발생할 수 있습니다.

너무 큰 스크립트 구성을 저장할 수없는 경우 ksh93이 완벽하게 읽을 수있는 경우 bash가 분할 위반으로 충돌 할 수 있습니다.


7

스크립트를 실행하는 인터프리터의 작동 방식에 따라 다릅니다. 모든 커널은 실행할 파일이로 시작하는 것을 주목하고 #!, 본질적으로 나머지 라인을 프로그램으로 실행하고 실행 파일을 인수로 제공합니다. 거기에 나열된 인터프리터가 파일을 한 줄씩 읽으면 (대화식 쉘이 입력 한 내용과 마찬가지로) 얻는 것입니다 (그러나 다중 줄 루프 구조를 읽고 반복하기 위해 유지합니다). 인터프리터가 파일을 메모리에 넣은 경우 파일을 처리하고 (아마도 Perl 및 Pyton과 같은 중간 표현으로 컴파일) 파일을 실행하기 전에 전체를 읽습니다.

그 동안 파일을 삭제하면 인터프리터가 파일을 닫을 때까지 파일이 삭제되지 않습니다 (항상 파일은 마지막 참조, 디렉토리 항목 또는 파일을 열어 놓는 프로세스 일 때 사라짐).


4

'x'파일 :

cat<<'dog' >xyzzy
LANG=C
T=`tty`
( sleep 2 ; ls -l xyzzy >$T ) &
( sleep 4 ; rm -v xyzzy >$T ) &
( sleep 4 ; ls -l xyzzy >$T ) &
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
dog

sh xyzzy

질주:

~/wrk/tmp$ sh x
alive.
alive.
alive.
-rw-r--r-- 1 yeti yeti 287 Mar 23 16:57 xyzzy
alive.
removed `xyzzy'
ls: cannot access xyzzy: No such file or directory
alive.
alive.
alive.
alive.
~/wrk/tmp$ _

IIRC 파일이 프로세스가 열린 상태로 유지되는 한 파일은 삭제되지 않습니다. 삭제는 주어진 DIRENT를 제거합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.