bash 스크립트 실행 중인 덮어쓰기 동작
개시하다
이 글의 목적은 bash가 실행 중인 스크립트 파일의 덮어쓰기가 어떻게 작동하는지 개인적인 흥미를 바탕으로 확인하는 것이지, 다음을 언급하는 것이 아니다.
bash 스크립트 파일을 덮어쓰는 동작
상기 내용에 따라 실행 중
echo foo
에서 echo bar
으로 변경하면 bar
표시됩니다.#!/bin/sh
sleep 30
echo foo
손 옆에서 해봤는데 안 나왔어bar
.SHEBANG을 bash
로 변경하려 했으나 재현되지 않았다.그러나 실행 중
cp
로 다음 파일을 덮어쓰면 재현됩니다.#!/bin/sh
sleep 30
echo bar
또 사용cp
이 아닌 사용mv
일 경우 재현되지 않는다.또한 echo bar
로 작성된 스크립트tar
를 아카이브하여 실행 중x
으로 덮어쓴 곳에서 재현되지 않습니다.총괄은 아래와 같다.했던 내용.
재현되지 않음
vim로 덮어쓰기
기색을 드러내지 않다
cp로 덮어쓰기
재현
데이터로 덮어쓰기
기색을 드러내지 않다
이 일로 미루어 보면 다음과 같은 결론을 얻을 수 있다.
bash는 스크립트를 실행할 때 통일적으로 읽지 않고 순서대로 파일을 읽기 때문에 같은 i-node 파일을 후속으로 읽습니다.i-node가 다른 경우 이미 열린 파일 설명자는 이전 파일에서 계속 읽을 수 있기 때문에 중복되지 않습니다.
Vim은 저장할 때 임시 파일에 파일을 쓴 다음 이름을 바꾸어 저장합니다. 이것이 Vim이 저장한 이유죠.실제로는 Vim입니다.
설정
:set backupcopy=yes
후 다시 시도하는 곳bar
을 표시합니다.backupcopy
는 기본적으로 편집 중인 파일을 통해 자동으로 판별auto
하기 때문에 시도할 때 yes
로 명확하게 설정해야 합니다.bash 설치 확인
evalstring.c
의 parse_and_execute
에서 명령을 처리하고 input.c
의 with_input_from_buffered_stream
에서 읽을 준비를 합니다.버퍼의 읽기 주체y.tab.c
는 분석기에서 직접 호출됩니다. 이 분석기는 fgets(2)
에서 읽기와 동시에 실행되기 때문에 파일을 한 번에 읽지 않습니다.while/do
에서 순환이 실행될 때 스크립트는 버퍼 메모리 형식으로 파일을 읽습니다. 파일을 다시 쓰면 목적지가 무엇인지 되돌려줍니다. 이 버퍼 메모리가 파일 시스템에서 다시 읽지 않으면 같은 순환을 재현합니다.따라서 done
를 찾았을 때 되돌아오는 주소는 메모리에 있습니다.bash가 이러쿵저러쿵 말하기보다는 I/O 파일 시스템에 있을 때 이것이 원자탄이다.같은 일을 했다면 어떤 조개껍질이나 언어 처리 시스템이든 일어날 수 있다.
이전의 php는 해석 결과를 AST로 유지하지 않았기 때문에 bash와 마찬가지로 YACC의 분석기에서 직접 처리를 실시하는 것이 생각납니다.
끝말
나는 bash 코드를 여러 번 읽었지만 스크립트의 읽기 주위는 읽지 않았기 때문에 보호되지 않은 모습을 볼 수 있었으면 좋겠다. 그리고 스크립트 처리 시스템에서 실행되는 스크립트 파일의 덮어쓰기가 더욱 두려웠다.
반복합니다. 이것은 bash의 문제가 아니라 I/O가 원자력으로 인한 문제가 아닙니다. 이 문제를 피하는 동시에 스크립트를 바꾸면 스크립트가 멈춘 것만 확인할 수 있습니다.mv를 사용하든지 rm&cp를 사용하든지 설치 명령을 사용하세요.
추기 2021/12/29
트위터에 데몬1995씨가 지적하고 확인한 후 리눅스의 대시(/bin/sh),zsh가 재현되고 FreeBSD의sh/bash가 재현되지 않았다.UNIX System-V를 재현한 것 같습니다.
추기 2021/12/30(1)
트위터에서 hdk FreeBSD로 재현할 수 없는 이벤트2 씨는 FreeBSD가 stdio의 버퍼 관계 때문에 512바이트 정도로 검증하는 것이 가장 좋다고 지적해 주십시오.
main1.c
#include <stdio.h>
int
main() {
char buf[512];
FILE* fp = fopen("test", "r");
fgets(buf, sizeof(buf), fp);
puts(buf);
getchar();
fgets(buf, sizeof(buf), fp);
puts(buf);
fclose(fp);
}
main2.c#include <stdio.h>
#include <memory.h>
int
main() {
char buf[512];
FILE* fp = fopen("test", "w");
memset(buf, '0', sizeof(buf));
fwrite(buf, sizeof(buf), 1, fp);
fflush(fp);
getchar();
memset(buf, '1', sizeof(buf));
fwrite(buf, sizeof(buf), 1, fp);
fclose(fp);
}
main1을 실행하고, 중지 중main2를 실행하며, main1에서 키를 두드려 계속 진행하는 곳에서 512바이트 이후에 읽습니다.따라서 FreeBSD도 재현할 수 있습니다.추기 2021/12/30(2)
트위터에 기시초는 이 점을 지적했고
jobs.c
의sync_buffered_stream
에서lseek임을 알게 됐다.dup에서 만든 fd에 같은 편이량을 전달하기 위해 버퍼에서 먼저 읽은 부분을 되돌려 놓은 것 같습니다.input.c
/* Seek backwards on file BFD to synchronize what we've read so far
with the underlying file pointer. */
int
sync_buffered_stream (bfd)
int bfd;
{
BUFFERED_STREAM *bp;
off_t chars_left;
if (buffers == 0 || (bp = buffers[bfd]) == 0)
return (-1);
chars_left = bp->b_used - bp->b_inputp;
if (chars_left) {
lseek (bp->b_fd, -chars_left, SEEK_CUR);
}
bp->b_used = bp->b_inputp = 0;
return (0);
}
버퍼 메모리의 읽기량이 딱 맞을 때도 이 현상이 발생하지만, 이것lseek
은 이 현상이 발생하기 쉬운 것 같다.check_bash_input
의 평론을 읽었는데 이 상황에 대한 대응인 것 같다.Reference
이 문제에 관하여(bash 스크립트 실행 중인 덮어쓰기 동작), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://zenn.dev/mattn/articles/5af86b61004bdc텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)