이 질문에 대해 bash 쉘 스크립트를 고려해 봅시다.이 질문은 모든 유형의 쉘 스크립트에 적용 가능해야합니다.
누군가가 쉘 스크립트를 실행할 때 Linux는 모든 스크립트를 한 번에 (메모리에)로드합니까 아니면 스크립트 명령을 하나씩 읽습니다 (한 줄씩)?
즉, 실행이 완료되기 전에 쉘 스크립트를 실행하고 삭제하면 실행이 종료됩니까, 아니면 그대로 계속됩니까?
이 질문에 대해 bash 쉘 스크립트를 고려해 봅시다.이 질문은 모든 유형의 쉘 스크립트에 적용 가능해야합니다.
누군가가 쉘 스크립트를 실행할 때 Linux는 모든 스크립트를 한 번에 (메모리에)로드합니까 아니면 스크립트 명령을 하나씩 읽습니다 (한 줄씩)?
즉, 실행이 완료되기 전에 쉘 스크립트를 실행하고 삭제하면 실행이 종료됩니까, 아니면 그대로 계속됩니까?
답변:
사용 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 및 기타 셸은 파일을 전체적으로로드하지 않고 블록 단위로 읽습니다.
이것은 OS 의존성보다 쉘 의존성입니다.
버전 ksh
에 따라 요청시 8k 또는 64k 바이트 블록으로 스크립트를 읽습니다.
bash
스크립트를 한 줄씩 읽으십시오. 그러나 사실 행은 임의 길이 일 수 있으므로 다음 행의 시작 부분부터 8176 바이트마다 구문 분석 할 때마다 읽습니다.
간단한 구성, 즉 일반 명령 모음입니다.
루프, 스위치, here 문서, 괄호로 묶인 서브 쉘, 함수 정의 등과 같은 쉘 구조화 된 명령이 사용 되는 경우 ( 응답이 누락 된 경우 ) 쉘 인터프리터가 읽습니다. 구문 오류가 없는지 먼저 확인하십시오.for/do/done
case/esac
동일한 코드를 여러 번 반복해서 읽을 수 있지만이 내용이 정상적으로 캐시된다는 사실로 인해 완화되므로 다소 비효율적입니다.
쉘 인터프리터가 무엇이든, 쉘이 스크립트의 일부를 자유롭게 읽을 수 있으므로 쉘 스크립트가 실행되는 동안 쉘 스크립트를 수정하는 것은 현명하지 않으며 동기화되지 않은 경우 예기치 않은 구문 오류가 발생할 수 있습니다.
너무 큰 스크립트 구성을 저장할 수없는 경우 ksh93이 완벽하게 읽을 수있는 경우 bash가 분할 위반으로 충돌 할 수 있습니다.
스크립트를 실행하는 인터프리터의 작동 방식에 따라 다릅니다. 모든 커널은 실행할 파일이로 시작하는 것을 주목하고 #!
, 본질적으로 나머지 라인을 프로그램으로 실행하고 실행 파일을 인수로 제공합니다. 거기에 나열된 인터프리터가 파일을 한 줄씩 읽으면 (대화식 쉘이 입력 한 내용과 마찬가지로) 얻는 것입니다 (그러나 다중 줄 루프 구조를 읽고 반복하기 위해 유지합니다). 인터프리터가 파일을 메모리에 넣은 경우 파일을 처리하고 (아마도 Perl 및 Pyton과 같은 중간 표현으로 컴파일) 파일을 실행하기 전에 전체를 읽습니다.
그 동안 파일을 삭제하면 인터프리터가 파일을 닫을 때까지 파일이 삭제되지 않습니다 (항상 파일은 마지막 참조, 디렉토리 항목 또는 파일을 열어 놓는 프로세스 일 때 사라짐).
'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를 제거합니다.