이 질문에 대해서는 bash 쉘 스크립트를 고려해 보겠습니다. 하지만 이 질문은 모든 유형의 쉘 스크립트에 적용되어야 합니다.
누군가가 쉘 스크립트를 실행할 때,Linux는 모든 스크립트를 한 번에(메모리에) 로드합니까, 아니면 스크립트 명령을 하나씩 읽습니까?(한줄씩)?
즉, 쉘 스크립트를 실행했다가 실행이 완료되기 전에 삭제하면 실행이 종료되나요, 아니면 계속 진행되나요?
답변1
를 사용하면 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는 반드시 파일의 내용을 로드하는 것은 아니며, 내용을 로드하는 것은 인터프리터이므로 파일을 완전히 로드하는지, 한 번에 청크 또는 줄씩 로드하는지 여부는 인터프리터가 구현되는 방식에 따라 달라집니다.
그럼 왜 파일을 편집할 수 없나요?
그러나 더 큰 스크립트를 사용하면 위 테스트가 약간 오해의 소지가 있다는 것을 알게 될 것입니다. 실제로 대부분의 인터프리터는 파일을 청크로 로드합니다. 이는 파일 청크를 로드하고 처리한 다음 다른 청크를 로드하는 많은 Unix 도구의 표준입니다. 얼마 전에 제가 쓴 U&L Q&A에서 이 동작을 볼 수 있습니다 grep
.grep/egrep은 매번 얼마나 많은 텍스트를 소비합니까?.
예
다음과 같은 쉘 스크립트를 작성한다고 가정해 보겠습니다.
$ (
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 및 기타 셸은 파일을 완전히 로드하지 않고 대신 청크로 읽을 수 있습니다.
인용하다
답변2
이는 운영 체제보다 셸에 더 많이 의존합니다.
버전에 따라 ksh
요청 시 스크립트를 8,000바이트 또는 64,000바이트 단위로 읽습니다.
bash
스크립트를 한 줄씩 읽어보세요. 그러나 라인의 길이에는 제한이 없다는 점을 고려하여 구문 분석할 때마다 다음 라인의 시작 부분부터 8176바이트를 읽습니다.
이는 간단한 구조, 즉 간단한 명령 세트를 위한 것입니다.
쉘 구조 명령을 사용하는 경우(허용되는 답변은 사례를 고려하지 않습니다.) for/do/done
루프, case/esac
스위치, here 문서, 괄호로 묶인 하위 쉘, 함수 정의 등과 위의 조합과 마찬가지로 쉘 인터프리터는 먼저 구문 오류가 없는지 확인하기 위해 구문의 끝까지 읽습니다.
이는 동일한 코드를 여러 번 읽을 수 있기 때문에 다소 비효율적이지만 이러한 내용은 일반적으로 캐시되므로 완화될 수 있습니다.
어떤 쉘 인터프리터를 사용하든 쉘 스크립트가 실행되는 동안 쉘 스크립트를 수정하는 것은 매우 현명하지 않습니다. 쉘은 스크립트의 모든 부분을 자유롭게 다시 읽을 수 있기 때문에 동기화되지 않은 경우 예기치 않은 구문 오류가 발생할 수 있습니다.
또한 bash가 ksh93이 완벽하게 읽을 수 없을 만큼 큰 스크립트 구성을 저장할 수 없는 경우 분할 위반으로 인해 충돌이 발생할 수 있습니다.
답변3
스크립트를 실행하는 인터프리터의 작동 방식에 따라 다릅니다. 커널이 하는 일은 실행될 파일이 로 시작 #!
하고 본질적으로 나머지 줄을 프로그램으로 실행하여 실행 파일을 인수로 제공한다는 것을 알아차리는 것입니다. 여기에 나열된 인터프리터가 파일을 한 줄씩 읽는 경우(대화식 쉘이 입력한 내용을 처리하는 것처럼), 다음과 같은 결과를 얻게 됩니다(그러나 여러 줄 루프 구조는 반복을 위해 읽고 유지됩니다). 이를 처리한 다음(아마도 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를 제거합니다.