“{Exec> / dev / null kodunda; }> / dev / null ”başlık altında neler oluyor?


15

Bir exec yeniden yönlendirmesi içeren bir komut listesini yeniden yönlendirdiğinizde, exec> / dev / null daha sonra hala uygulanmıyormuş gibi görünür:

{ exec >/dev/null; } >/dev/null; echo "Hi"

"Merhaba" yazdırılır.

{}Bir boru hattının parçası olmadığı sürece komut listesinin bir alt kabuk olarak görülmediği izlenimi altındaydım , bu yüzden exec >/dev/nullhala aklımdaki mevcut kabuk ortamında uygulanmalıdır.

Şimdi şu şekilde değiştirirseniz:

{ exec >/dev/null; } 2>/dev/null; echo "Hi"

beklendiği gibi çıktı yok; dosya tanımlayıcı 1, gelecekteki komutlar için de / dev / null'a işaret ediyor. Bu yeniden çalıştırma ile gösterilir:

{ exec >/dev/null; } >/dev/null; echo "Hi"

hiçbir çıktı vermeyecek.

Bir senaryo hazırlamayı ve onu düzeltmeyi denedim, ama burada tam olarak ne olduğundan emin değilim.

Bu betiğin her bir noktasında STDOUT dosya tanımlayıcısına ne oluyor?

EDIT: strace çıktımı ekleme:

read(255, "#!/usr/bin/env bash\n{ exec 1>/de"..., 65) = 65
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD)                       = 0
fcntl(1, F_DUPFD, 10)                   = 10
fcntl(1, F_GETFD)                       = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
close(10)                               = 0
open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fcntl(1, F_GETFD)                       = 0
fcntl(1, F_DUPFD, 10)                   = 10
fcntl(1, F_GETFD)                       = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0
dup2(10, 1)                             = 1
fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
close(10)                               = 0
fstat(1, {st_mode=S_IFCHR|0666, st_rdev=makedev(1, 3), ...}) = 0
ioctl(1, TCGETS, 0x7ffee027ef90)        = -1 ENOTTY (Inappropriate ioctl for device)
write(1, "hi\n", 3)                     = 3

Bu garip; Ben çoğaltamıyorum close(10). Ayrıca üzerinde çalıştığınız tüm betik içeriklerini de yayınlayabilir misiniz?
DepressedDaniel

@DepressedDaniel İşte tam script ve strace: script strace
Joey Pabalinas

Bir başıboş var ;sonra }anlamını değiştirir, > /dev/nullbileşik listeye uygulanmaz için {}sonuçta.
DepressedDaniel

@DepressedDaniel Ah, tamamen haklısın! Şimdi çıktı beklediğim şey; cevaplarınız için teşekkürler!
Joey Pabalinas

Yanıtlar:


17

Takip edelim

{ exec >/dev/null; } >/dev/null; echo "Hi"

adım adım.

  1. İki komut vardır:

    a. { exec >/dev/null; } >/dev/null, bunu takiben

    b. echo "Hi"

    Kabuk önce (a) komutunu sonra da (b) komutunu yürütür.

  2. { exec >/dev/null; } >/dev/nullGelirlerin yürütülmesi aşağıdaki gibidir:

    a. İlk olarak, kabuk yeniden yönlendirmeyi gerçekleştirir >/dev/null ve komut sona erdiğinde geri almayı hatırlar .

    b. Ardından, kabuk yürütülür { exec >/dev/null; }.

    c. Son olarak, kabuk standart çıktıyı olduğu yere geri döndürür. (Bu, ls -lR /usr/share/fonts >~/FontList.txtyönlendirmelerle aynı mekanizmadır, yalnızca ait oldukları komutun süresi boyunca yapılır.)

  3. İlk komut tamamlandığında kabuk yürütülür echo "Hi". Standart çıktı, ilk komuttan önceki herhangi bir yerdedir.


2a'nın 2b'den önce yürütülmesinin bir nedeni var mı? (sağdan sola)
Joey Pabalinas

5
Yönlendirmeler , uygulandıkları komuttan önce yürütülmeli, değil mi? Başka türlü nasıl çalışabilirler?
AlexP

Aha, hiç böyle düşünmedin! İlk ikisi borh büyük cevaplar; Birine karar vermeden önce biraz verdim, ama her iki açıklamayı da takdir ediyorum!
Joey Pabalinas

Ne yazık ki sadece bir cevap seçebilirim, bu yüzden biraz daha az teknik olduğundan ve bu yüzden daha az teknoloji meraklı kullanıcılara bile yardımcı olabileceğini düşünüyorum. Ancak @DepressedDaniel aynı ölçüde büyük bir cevabı vardı burada , bu teklif daha derinlemesine açıklama.
Joey Pabalinas

14

Bir alt kabuk veya alt işlem kullanmamak için, bir bileşik listesinin çıktısı {}borulandığında >, kabuk, STDOUT tanımlayıcısını bileşik listesini çalıştırmadan önce kaydeder ve sonra geri yükler. Dolayısıyla, exec >bileşik listesindeki etkisi, eski tanımlayıcının STDOUT olarak eski durumuna getirildiği noktadan sonra etkisini taşımamaktadır.

Şimdi ilgili kısmına bakalım strace bash -c '{ exec >/dev/null; } >/dev/null; echo hi' 2>&1 | cat -n:

   132  open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
   133  fcntl(1, F_GETFD)                       = 0
   134  fcntl(1, F_DUPFD, 10)                   = 10
   135  fcntl(1, F_GETFD)                       = 0
   136  fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
   137  dup2(3, 1)                              = 1
   138  close(3)                                = 0
   139  open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
   140  fcntl(1, F_GETFD)                       = 0
   141  fcntl(1, F_DUPFD, 10)                   = 11
   142  fcntl(1, F_GETFD)                       = 0
   143  fcntl(11, F_SETFD, FD_CLOEXEC)          = 0
   144  dup2(3, 1)                              = 1
   145  close(3)                                = 0
   146  close(11)                               = 0
   147  dup2(10, 1)                             = 1
   148  fcntl(10, F_GETFD)                      = 0x1 (flags FD_CLOEXEC)
   149  close(10)                               = 0

134. satırda nasıl tanımlayıcı 1(STDOUT ) 'un en azından indeksli başka bir tanımlayıcıya nasıl kopyalandığını görebilirsiniz 10(işte F_DUPFDbu; o tanımlayıcıya çoğaltıldıktan sonra verilen sayıdan başlayarak mevcut en düşük tanımlayıcıyı döndürür). Ayrıca 137 satırında open("/dev/null")(tanımlayıcı 3) sonucunun tanımlayıcı 1( STDOUT) üzerine nasıl kopyalandığını görün . Son olarak, satırda 147, STDOUTtanımlayıcıda kaydedilen eski 10tanımlayıcıya 1( STDOUT) geri kopyalanır . Net etki, değişimi STDOUTçevrimiçi olana 144(iç kısma karşılık gelen exec >/dev/null) izole etmektir .


137 numaralı satırda FD 1'in üzerine FD 3 yazıldığından, 141 nolu satır neden 10 / dev / null değerini göstermiyor?
Joey Pabalinas

@JoeyPabalinas Satır 141, FD 1'i (yani, stdout) 10'dan sonra bir sonraki kullanılabilir tanımlayıcıya kopyalar ve bu sistem çağrısından dönüş değerinde görebileceğiniz gibi 11 olur. 10 sadece bash'a sabit kodlanmıştır, böylece bash'ın tanımlayıcı tasarrufu, komut dosyanızda manipüle edebileceğiniz tek haneli tanımlayıcıları etkilemez exec.
DepressedDaniel

Peki o zaman fcntl (1, F_DUPFD, 10) FD 1'in şu anda nerede olduğuna bakılmaksızın her zaman STDOUT'a atıfta bulunacaktır?
Joey Pabalinas

@JoeyPabalinas Sorunuzun ne olduğundan emin değilim. FD 1 IS STDOUT. Aynı şey.
DepressedDaniel

Orijinal yazıma tam strace çıktı eklendi.
Joey Pabalinas

8

Arasındaki fark { exec >/dev/null; } >/dev/null; echo "Hi"ve { exec >/dev/null; }; echo "Hi"çift yönlendirme yapmasıdır dup2(10, 1);kapanış orijinalin kopyası olan 10 fd önce stdoutsonraki komutu çalıştırmadan önce, ( echo).

Bu şekilde olur, çünkü dış yönlendirme aslında iç yönlendirmeyi kaplar. Bu yüzden stdouttamamlandığında orijinal fd'yi kopyalar .


Farkı kolay bir şekilde açıklamak için +1. AlexP'nin cevabı bu açıklamadan yoksundur.
Kamil Maciorowski
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.