답변:
명령을 평가할 때 경로 >
재 지정이 먼저 해결되므로 시간 ls
이 지날 수록 출력 파일이 이미 작성되었습니다.
이는 >
동일한 명령 내에서 경로 재 지정을 사용하여 동일한 파일을 읽고 쓰는 것이 파일을 자르는 이유이기도 합니다. 명령이 실행될 때까지 파일이 이미 잘 렸습니다.
$ echo foo >bar
$ cat bar
foo
$ <bar cat >bar
$ cat bar
$
이를 피하기위한 요령 :
<<<"$(ls)" > ls.out
(리디렉션이 해결되기 전에 실행해야하는 모든 명령에서 작동)
명령 대체는 외부 명령이 평가 ls
되기 전에 실행 되므로 ls.out
작성 되기 전에 실행됩니다 .
$ ls
bar foo
$ <<<"$(ls)" > ls.out
$ cat ls.out
bar
foo
ls | sponge ls.out
(리디렉션이 해결되기 전에 실행해야하는 모든 명령에서 작동)
sponge
파이프의 나머지 부분이 실행을 완료 한 경우에만 파일에 기록하므로 작성 ls
되기 전에 실행 됩니다 ls.out
( 패키지 sponge
와 함께 제공됨 moreutils
).
$ ls
bar foo
$ ls | sponge ls.out
$ cat ls.out
bar
foo
ls * > ls.out
( ls > ls.out
의 특정 사례에 적용)
파일 이름 확장은 리디렉션이 해결되기 전에 수행되므로 ls
다음을 포함하지 않는 인수에서 실행됩니다 ls.out
.
$ ls
bar foo
$ ls * > ls.out
$ cat ls.out
bar
foo
$
프로그램 / 스크립트 / 실행되기 전에 리디렉션이 해결되는 이유에 대해 필수 이유는 보이지 않지만 그렇게하는 것이 더 좋은 이유는 두 가지입니다 .
STDIN을 미리 리디렉션하지 않으면 STDIN이 리디렉션 될 때까지 프로그램 / 스크립트 / 보류 상태가됩니다.
STDOUT을 재지 정하지 않으면 반드시 STDOUT이 재 지정 될 때까지 쉘 버퍼를 프로그램의 / 스크립트 / 출력으로 만들어야합니다.
첫 번째 경우에는 시간 낭비, 두 번째 경우에는 시간 낭비 및 메모리 낭비입니다.
이것은 나에게 일어나는 일이며, 이것이 실제 이유라고 주장하지는 않습니다. 그러나 나는 선택의 여지가 있다면 위에서 언급 한 이유로 어쨌든 리디렉션을 수행 할 것이라고 생각합니다.
보낸 사람 man bash
:
방향 전환
명령이 실행되기 전에 셸이 해석하는 특수 표기법을 사용하여 입력 및 출력을 리디렉션 할 수 있습니다. 리디렉션을 통해 명령의 파일 핸들을 복제, 열기, 닫기, 다른 파일을 참조하도록 만들 수 있으며 명령을 읽고 쓰는 파일을 변경할 수 있습니다.
첫 번째 문장 stdin
은 명령이 실행되기 직전에 경로 재 지정이 아닌 다른 곳으로 출력이 진행되도록 제안합니다 . 따라서 파일로 경로 재 지정하려면 먼저 쉘 자체에서 파일을 작성해야합니다.
파일이 없도록하려면 출력을 명명 된 파이프로 먼저 리디렉션 한 다음 파일로 리디렉션하는 것이 좋습니다. &
터미널에 대한 제어권을 사용자에게 돌려주는 것에주의하십시오
DIR:/xieerqi
skolodya@ubuntu:$ mkfifo /tmp/namedPipe.fifo
DIR:/xieerqi
skolodya@ubuntu:$ ls > /tmp/namedPipe.fifo &
[1] 14167
DIR:/xieerqi
skolodya@ubuntu:$ cat /tmp/namedPipe.fifo > ls.out
그런데 왜?
이것에 대해 생각해보십시오-출력은 어디에 있습니까? 프로그램과 같은 기능을 가지고있다 printf
, sprintf
, puts
,에 기본 이동로하는 모든 stdout
하지만 파일이 처음부터 존재하지 않을 경우 자신의 출력이 파일에 사라질 수 있는가? 물과 같습니다. 수도꼭지 아래에 유리를 넣지 않고 물 한 잔을 얻을 수 있습니까?
나는 현재 답변에 동의하지 않습니다. 명령을 실행하기 전에 출력 파일을 열어야합니다. 그렇지 않으면 명령에 출력을 쓸 위치가 없습니다.
이것은 세상에서 "모든 것이 파일 이기 " 때문 입니다. 화면에 출력은 SDOUT (일명 파일 설명자 1)입니다. 응용 프로그램이 터미널에 쓰려면 fd1 을 열고 파일처럼 씁니다.
셸에서 응용 프로그램의 출력을 리디렉션하면 실제로 파일을 가리 키도록 fd1을 변경합니다. 파이프 할 때 한 응용 프로그램의 STDOUT이 다른 응용 프로그램의 STDIN (fd0)이되도록 변경합니다.
그러나 그것은 모두 좋은 말이지 만, 이것이 어떻게 작동하는지 쉽게 볼 수 있습니다 strace
. 꽤 무거운 물건이지만이 예제는 매우 짧습니다.
strace sh -c "ls > ls.out" 2> strace.out
내에서 strace.out
우리는 다음과 같은 하이라이트를 볼 수 있습니다
open("ls.out", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
이 열립니다 ls.out
로 fd3
. 쓰기 만하십시오. 존재하는 경우 잘립니다 (덮어 쓰기).
fcntl(1, F_DUPFD, 10) = 10
close(1) = 0
fcntl(10, F_SETFD, FD_CLOEXEC) = 0
dup2(3, 1) = 1
close(3) = 0
이것은 약간 저글링입니다. STDOUT (fd1)을 fd10으로 분류하고 닫습니다. 이 명령으로 실제 STDOUT에 아무것도 출력하지 않기 때문입니다. 쓰기 핸들을 복제 ls.out
하고 원래 핸들을 닫으면 완료됩니다 .
stat("/opt/wine-staging/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/home/oli/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/local/sbin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/local/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/sbin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/sbin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/bin/ls", {st_mode=S_IFREG|0755, st_size=110080, ...}) = 0
실행 파일을 검색하는 것입니다. 긴 길을 가지지 않는 교훈;)
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f0961324a10) = 31933
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 31933
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=31933, si_status=0, si_utime=0, si_stime=0} ---
rt_sigreturn() = 31933
dup2(10, 1) = 1
close(10) = 0
그런 다음 명령이 실행되고 부모가 기다립니다. 이 작업 동안 STDOUT은 실제로 열린 파일 핸들에 매핑됩니다 ls.out
. 자식이 발행 SIGCHLD
하면 부모 프로세스가 완료되었으며 다시 시작할 수 있음을 알려줍니다. 조금 더 저글링과 마무리로 마무리합니다 ls.out
.
저글링 이 그렇게 많은 이유는 무엇 입니까? 아니요, 확실하지 않습니다.
물론이 행동을 바꿀 수 있습니다. 당신은 무언가를 메모리에 버퍼링 할 수 sponge
있으며 그것은 진행중인 명령에서 보이지 않을 것입니다. 우리는 여전히 파일 디스크립터에 영향을 미치지 만 파일 시스템에 보이는 방식으로는 영향을 미치지 않습니다.
ls | sponge ls.out
쉘에서 리디렉션 및 파이프 연산자의 구현에 대한 좋은 기사도 있습니다 . 다음은 리디렉션이 구현되는 방법을 보여줍니다 $ ls > ls.out
.
main(){
close(1); // Release fd no - 1
open("ls.out", "w"); // Open a file with fd no = 1
// Child process
if (fork() == 0) {
exec("ls");
}
}