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?
ls > ../ls.out
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?
ls > ../ls.out
Yanıtlar:
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)
sponge
böylece çalışmayı tamamlandığında boru geri kalanı sadece dosyaya yazar ls
önce çalıştırılır ls.out
(oluşturulur sponge
ile sağlanan moreutils
pakette):
$ 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 ls
iç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ı.
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, stdin
komutun ç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?
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.out
aşağıdaki olayları görebiliriz:
open("ls.out", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
Bu, ls.out
olarak 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.out
ve 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 sponge
ve 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
Yönlendirme ve boru operatörlerinin kabukta uygulanması hakkında güzel bir makale de var . Bu, yeniden yönlendirmenin nasıl uygulanabileceğini gösterir $ 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");
}
}