Neden 'ls> ls.out' 'ls.out' isimlerinin listesine dahil edilmesine neden oluyor?


26

Neden $ ls > ls.out'ls.out' dosyasının geçerli dizindeki dosya adlarının listesine dahil edilmesine neden oluyor? Bu neden seçildi? Neden başka türlü değil?


3
Muhtemelen ilk dosya ls.out oluşturduğundan ve sonra üzerine çıktı yazdığı için
Dimitri Podborski

1
Dahil etmekten kaçınmak istiyorsanız, çıktı dosyasını her zaman farklı bir dizinde saklayabilirsiniz. Örneğin, ana dizini (dosya sisteminin kökünde olmamanız şartıyla)ls > ../ls.out
Elder Geek ile 19

Yanıtlar:


36

Komutu değerlendirirken >ilk önce yeniden yönlendirme çözülür: bu nedenle zamanla lsçıktı dosyası zaten yaratılmıştır.

Bu, >aynı komut içerisinde yeniden yönlendirme kullanarak aynı dosyayı okumak ve yazmak için dosyayı kısaltır; komut çalıştığında dosya zaten kesildi:

$ echo foo >bar
$ cat bar
foo
$ <bar cat >bar
$ cat bar
$ 

Bunu önlemek için püf noktaları:

  • <<<"$(ls)" > ls.out (yönlendirme çözülmeden önce çalışması gereken herhangi bir komut için çalışır)

    Komut ikamesi, dış komut değerlendirilmeden lsönce çalıştırılır , bu nedenle çalıştırılmadan önce çalıştırılır ls.out:

    $ ls
    bar  foo
    $ <<<"$(ls)" > ls.out
    $ cat ls.out 
    bar
    foo
  • ls | sponge ls.out (yönlendirme çözülmeden önce çalışması gereken herhangi bir komut için çalışır)

    spongeböylece çalışmayı tamamlandığında boru geri kalanı sadece dosyaya yazar lsönce çalıştırılır ls.out(oluşturulur spongeile sağlanan moreutilspakette):

    $ ls
    bar  foo
    $ ls | sponge ls.out
    $ cat ls.out 
    bar
    foo
  • ls * > ls.out( ls > ls.outözel durum için çalışır )

    Dosya adı genişletme, yeniden yönlendirme çözülmeden önce gerçekleştirilir, bu nedenle aşağıdakileri lsiçermeyen argümanlarında çalışır ls.out:

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

Yönlendirmelerin neden program / komut dosyası / neyin çalıştırılmasından önce çözümlendiğine bağlı olarak, bunun zorunlu olmasının neden belirli bir neden olduğunu görmüyorum, ancak bunun neden daha iyi olduğunu gösteren iki neden görüyorum :

  • STDIN'i yeniden yönlendirmemek, önceden STDIN yönlendirilinceye kadar programı / betiği / her şeyi yapar;

  • STDOUT'u yönlendirmeden önce mutlaka kabuk, STDOUT yeniden yönlendirilinceye kadar programın / betiğinin / çıktısının tampon tamponunu yapmalıdır;

Yani ilk durumda zaman kaybı, ikinci durumda zaman kaybı ve bellek.

Bu sadece benim başıma gelenler, bunların asıl sebepler olduğunu iddia etmiyorum; ama sanırım sonuçta, birinin bir seçeneği varsa, yukarıda belirtilen nedenlerden dolayı yine de yönlendirme yapacaklardı.


1
Yeniden yönlendirme sırasında kabuğun verilere gerçekten değmediğini unutmayın (giriş yönlendirmesi veya çıkış yönlendirmesi). Sadece dosyayı açar ve dosya tanımlayıcısını programa aktarır.
Peter Green,

11

Kimden man bash:

REDIRECTION

Bir komut çalıştırılmadan önce, giriş ve çıkış, kabuk tarafından yorumlanan özel bir gösterim kullanılarak yönlendirilebilir. Yönlendirme, komut dosyalarının çoğaltılmasını, açılmasını, kapanmasını, farklı dosyalara başvurmasını sağlar ve komutun okuduğu ve yazdığı dosyaları değiştirebilir.

İlk cümle, stdinkomutun çalıştırılmasından hemen önce çıktının yönlendirme dışında bir yere gitmek için yapılmasını önerir . Bu nedenle, dosyaya yönlendirilmek için önce dosyanın kabuk tarafından yaratılması gerekir.

Bir dosyaya sahip olmamak için, çıktısını önce adlandırılmış yöneltmeye, sonra da dosyaya yönlendirmenizi öneririm. Kullanımına dikkat &kullanıcı terminal üzerinde dönüş kontrol

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

Ama neden?

Bir düşünün - çıktı nerede olacak? Bir program gibi işlevlere sahiptir printf, sprintf, puts, varsayılan halindeyken tarafından hangi tüm stdout, ancak dosya ilk etapta yoksa kendi çıkış dosyasına gitmiş olabilir? Su gibi. Önce musluğun altına cam koymadan bir bardak su alabilir misiniz?


10

Mevcut cevaplara katılmıyorum. Çıktı dosyası, komut çalıştırılmadan önce açılmalıdır, aksi halde komutun çıktısını yazacak bir yeri olmaz.

Çünkü dünyamızda "her şey bir dosyadır" . Ekran çıkışı SDOUT (aka dosya tanımlayıcısı 1). Bir uygulamanın terminale yazması için fd1 açılır ve bir dosya gibi yazar.

Bir uygulamanın çıktısını bir kabuğa yönlendirdiğinizde, fd1'i değiştirirsiniz, bu yüzden aslında dosyayı işaret eder. Borunuzu alırken bir uygulamanın STDOUT'unu bir başkasının STDIN (fd0) olması için değiştirirsiniz.


Ama bunu söylemek güzel, ama bunun nasıl çalıştığına kolayca göz atabilirsiniz strace. Oldukça ağır şeyler ama bu örnek oldukça kısa.

strace sh -c "ls > ls.out" 2> strace.out

İçinde strace.outaşağıdaki olayları görebiliriz:

open("ls.out", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3

Bu, ls.outolarak açılır fd3. Sadece yaz Varsa keser (üzerine yazar), aksi takdirde oluşturur.

fcntl(1, F_DUPFD, 10)                   = 10
close(1)                                = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0

Bu biraz hokkabazlık. STDOUT'u (fd1) fd10'a kapattık ve kapattık. Çünkü bu komutla gerçek STDOUT'a hiçbir şey çıkmıyoruz. Yazma tutamacını çoğaltarak ls.outve orijinali kapatarak biter .

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

Çalıştırılabilir dosyayı arıyor. Belki de uzun bir yolu olmayan bir ders;)

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

Sonra komut çalışır ve ebeveyn bekler. Bu işlem sırasında herhangi bir STDOUT aslında açık dosya tanıtıcısı ile eşleşmiş olacaktır ls.out. Çocuk sorun çıkarsa SIGCHLD, bu ebeveyn sürecine bitmiş olduğunu ve devam edebileceğini söyler. Biraz daha hokkabazlık ve yakın bir şeyle bitiyor ls.out.

Neden bu kadar çok hokkabazlık var? Hayır, ben de tamamen emin değilim.


Elbette bu davranışı değiştirebilirsiniz. Böyle bir şeyle hafızanıza tampon girebilirsiniz spongeve bu devam eden komuttan görünmez olacaktır. Hala dosya tanımlayıcılarını etkiliyoruz, ancak dosya sistemi tarafından görülebilen bir şekilde değil.

ls | sponge ls.out

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.