「ls> ls.out」が原因で「ls.out」が名前のリストに含まれるのはなぜですか?


26

$ ls > ls.out現在のディレクトリ内のファイル名のリストに「ls.out」が含まれるのはなぜですか?なぜこれが選ばれたのですか?なぜそうでないのですか?


3
おそらくそれは、最初のファイルls.outを作成し、それに出力を書き込むため
ディミトリPodborski

1
含めることを避けたい場合は、常に別のディレクトリに出力ファイルを保存できます。たとえば、あなたが(あなたが提供するファイルシステムのルートではない)親ディレクトリを選択することができます ls > ../ls.out
エルダーオタク

回答:


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.outls > ls.outの特定のケースで機能します)

    ファイル名の展開は、リダイレクトが解決される前lsに実行されるため、その引数で実行されますls.out

    $ ls
    bar  foo
    $ ls * > ls.out
    $ cat ls.out 
    bar
    foo
    $

プログラム/スクリプト/実行される前にリダイレクトが解決される理由について、そうすることが必須である特定の理由はわかりませんが、そうする方が良い 2つの理由がわかります:

  • STDINを事前にリダイレクトしないと、STDINがリダイレクトされるまでプログラム/スクリプト/何でも保持されます。

  • STDOUTを事前にリダイレクトしない場合は、STDOUTがリダイレクトされるまで、シェルのバッファーをプログラムの/スクリプトの/何でも出力する必要があります。

したがって、最初のケースでは時間の無駄であり、2番目のケースでは時間とメモリの無駄です。

これは私に起こることです、私はこれらが実際の理由だと主張していません。しかし、選択があれば、上記の理由からとにかく先にリダイレクトすることになります。


1
リダイレクト中、シェルは実際にはデータに触れないことに注意してください(入力リダイレクトまたは出力リダイレクト)。ファイルを開き、ファイル記述子をプログラムに渡します。
ピーターグリーン

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ありsprintfputsこれらはすべてデフォルトで移動しstdoutますが、最初にファイルが存在しない場合は出力をファイルに移動できますか?それは水のようなものです。最初に蛇口の下にガラスを置かずにコップ一杯の水を得ることができますか?


10

現在の答えには同意しません。コマンドを実行する前に出力ファイルを開く必要があります。そうしないと、コマンドに出力を書き込む場所がなくなります。

これは、私たちの世界では「すべてがファイル」だからです。画面への出力はSDOUT(別名ファイル記述子1)です。アプリケーションが端末に書き込むために、fd1を開き、ファイルのように書き込みます。

アプリケーションの出力をシェルでリダイレクトすると、fd1が実際にファイルを指すように変更されます。パイプを使用すると、1つのアプリケーションの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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.