'ls> ls.out'으로 인해 'ls.out'이 이름 목록에 포함되는 이유는 무엇입니까?


26

$ ls > ls.out'ls.out'이 현재 디렉토리의 파일 이름 목록에 포함되는 이유는 무엇 입니까? 이것이 왜 선택 되었습니까? 왜 그렇지 않습니까?


3
아마 먼저 파일 ls.out을 만든 다음 파일에 출력하기 때문에
Dimitri Podborski

1
포함을 피하려면 항상 출력 파일을 다른 디렉토리에 저장할 수 있습니다. 예를 들어 다음과 같이 상위 디렉토리 (파일 시스템의 루트에 있지 않다면)를 선택할 수 있습니다. ls > ../ls.out
Geek

답변:


36

명령을 평가할 때 경로 >재 지정이 먼저 해결되므로 시간 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이 재 지정 될 때까지 쉘 버퍼를 프로그램의 / 스크립트 / 출력으로 만들어야합니다.

첫 번째 경우에는 시간 낭비, 두 번째 경우에는 시간 낭비 및 메모리 낭비입니다.

이것은 나에게 일어나는 일이며, 이것이 실제 이유라고 주장하지는 않습니다. 그러나 나는 선택의 여지가 있다면 위에서 언급 한 이유로 어쨌든 리디렉션을 수행 할 것이라고 생각합니다.


1
리디렉션 동안 쉘은 실제로 데이터를 건드리지 않습니다 (입력 리디렉션 또는 출력 리디렉션). 파일을 열고 파일 디스크립터를 프로그램으로 전달합니다.
Peter Green

11

보낸 사람 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하지만 파일이 처음부터 존재하지 않을 경우 자신의 출력이 파일에 사라질 수 있는가? 물과 같습니다. 수도꼭지 아래에 유리를 넣지 않고 물 한 잔을 얻을 수 있습니까?


10

나는 현재 답변에 동의하지 않습니다. 명령을 실행하기 전에 출력 파일을 열어야합니다. 그렇지 않으면 명령에 출력을 쓸 위치가 없습니다.

이것은 세상에서 "모든 것이 파일 이기 " 때문 입니다. 화면에 출력은 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.outfd3. 쓰기 만하십시오. 존재하는 경우 잘립니다 (덮어 쓰기).

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

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