Bash'in Dosya Okuma Komutu Değişikliğini Anlama


11

Bash'in aşağıdaki çizgiyi tam olarak nasıl ele aldığını anlamaya çalışıyorum:

$(< "$FILE")

Bash man sayfasına göre, bu şuna eşdeğerdir:

$(cat "$FILE")

ve bu ikinci çizginin mantık çizgisini takip edebilirim. Bash değişken genişleme gerçekleştirir $FILE, komut yerine geçer, değerini $FILEverir cat, cat içeriğini $FILEstandart çıktıya çıkarır, tüm satırı içerideki komuttan kaynaklanan standart çıktı ile değiştirerek komut yerine koyma bitirir ve Bash bunu yürütmeyi dener basit bir komut.

Ancak, yukarıda bahsettiğim ilk satır için şunu anlıyorum: Bash değişken ikameyi gerçekleştiriyor $FILE, Bash $FILEstandart girdide okumak için açılıyor , bir şekilde standart giriş standart çıktıya kopyalanıyor , komut ikamesi bitiyor ve Bash sonuçta ortaya çıkan standardı yürütmeye çalışıyor çıktı.

Birisi bana $FILEstdin'den stdout'a nasıl geçtiğini açıklayabilir mi?

Yanıtlar:


-3

Bu <doğrudan bash komut değiştirmesinin bir yönü değildir . Bazı kabukların komut olmadan izin verdiği bir yönlendirme operatörüdür (boru gibi) (POSIX bu davranışı belirtmez).

Belki daha fazla alanla daha açık olurdu:

echo $( < $FILE )

bu etkili bir şekilde * daha POSIX güvenli olanla aynıdır

echo $( cat $FILE )

... ki bu da etkili bir şekilde *

echo $( cat < $FILE )

Bu son sürümle başlayalım. Bu cathiçbir argüman olmadan çalışır , yani standart girdiden okunacaktır. $FILEnedeniyle standart girdiye yönlendirilir <, bu nedenle catiçeriği standart çıktıya konur. $(command)Subsitution sonra iter catiçin argümanlar içine 'ın çıkışını echo.

Bölümünde bash(ancak POSIX standardında değil), <komut olmadan kullanabilirsiniz . bash(ve zshve kshama değil dash) cat <yeni bir alt işlem başlatmadan da sanki bunu yorumlayacaktır . Bu kabuk için doğal olduğundan, harici komutu tam anlamıyla çalıştırmaktan daha hızlıdır cat. * Bu yüzden "etkili bir şekilde aynı" diyorum.


Peki " bashbunu şu şekilde yorumlayacak " dediğiniz son paragrafta cat filename, bu davranışın komut ikamesine özgü olduğunu mu kastediyorsunuz? Çünkü eğer < filenamekendi başıma koşarsam, bash onu kandırmaz. Hiçbir şey çıktılamaz ve beni bir isteme geri döndürür.
Stanley Yu

Hala bir komut gerekiyor. @cuonglm benim orijinal metni değişmiş cat < filenameiçin cat filenamehangi ı karşı ve döndürülebilir.
Adam Katz

1
Pipo, bir dosya türüdür. Kabuk operatörü |iki alt işlem arasında (veya bazı kabuklarla, bir alt işlemden kabuğun standart girişine) bir boru oluşturur. Kabuk operatörü $(…)bir alt süreçten kabuğun kendisine bir boru oluşturur (standart girişine değil). Kabuk operatörü <bir boru içermez, sadece bir dosya açar ve dosya tanımlayıcısını standart girdiye taşır.
Gilles 'SO- kötü olmayı bırak'

3
< fileile aynı değildir cat < file( zshnerede olduğu hariç $READNULLCMD < file). < filemükemmel POSIX ve sadece fileokumak için açılır ve sonra hiçbir şey yapmaz (bu yüzden filehemen yakındır). O var $(< file)ya `< file`o özel operatörüdür ksh, zshve bash(ve davranış POSIX'deki belirtilmemiş bırakılır). Bkz Cevabımı detayları için.
Stéphane Chazelas

2
@ StéphaneChazelas'ın yorumunu başka bir ışığa koymak: ilk yaklaşıma, $(cmd1) $(cmd2)tipik olarak aynı olacaktır $(cmd1; cmd2). Ama nerede cmd2olduğuna bak < file. Diyorsak $(cmd1; < file), dosya okunmaz, ancak ile birlikte okunur $(cmd1) $(< file). Yani bunun $(< file)sadece $(command)bir emri olan sıradan bir durum olduğunu söylemek yanlıştır < file.   normal bir yönlendirme kullanımı değil, özel$(< …) bir komut ikamesi örneğidir .
Scott

14

$(<file)(ayrıca çalışır `<file`) zshve tarafından kopyalanan Korn kabuğunun özel bir operatörüdür bash. Komut yerine koymaya çok benziyor ama gerçekten değil.

POSIX mermilerinde basit bir komut:

< file var1=value1 > file2 cmd 2> file3 args 3> file4

Tüm parçalar isteğe bağlıdır, yalnızca yeniden yönlendirmelere, yalnızca komutlara, yalnızca atamalara veya kombinasyonlara sahip olabilirsiniz.

Yönlendirmeler varsa ancak komut yoksa, yönlendirmeler gerçekleştirilir (böylece a > fileaçılır ve kesilir file), ancak hiçbir şey olmaz. Yani

< file

Açar fileokumak için, ama hiç komutu var olduğu bir şey olmayabilir. Böylece filekapatılır ve hepsi bu kadar. Eğer $(< file)bir basitti komut ikamesi daha sonra hiçbir şey artıracağı,.

Olarak POSIX tarifnamede , içinde $(script), eğer scriptbu, sadece yönlendirmeler oluşur belirtilmemiş sonuçlar üretir . Bu Korn kabuğunun özel davranışına izin verir.

Ksh'ta (burada test edilir ksh93u+), komut dosyası yalnızca yeniden yönlendirmelerden (komut yok, atama yok) oluşan bir ve yalnızca tek bir basit komuttan (yorumlara izin verilirse) ve ilk yeniden yönlendirme bir stdin (fd) içeriyorsa 0) girişi sadece ( <, <<ya da <<<) yönlendirme, yani:

  • $(< file)
  • $(0< file)
  • $(<&3)( $(0>&3)aslında aynı operatör olduğu için)
  • $(< file > foo 2> $(whatever))

Ama değil:

  • $(> foo < file)
  • ne de $(0<> file)
  • ne de $(< file; sleep 1)
  • ne de $(< file; < file2)

sonra

  • ilk yönlendirme hariç tümü yok sayılır (ayrıştırılır)
  • ve / heredoc / herestring dosyasının içeriğine (ya da eğer böyle şeyler kullanılıyorsa dosya tanımlayıcıdan okunabilecek her ne olursa olsun) ek satır sonundaki <&3yeni karakterlere eklenir .

sanki $(cat < file)bunun dışında

  • okuma dahili olarak kabuk tarafından yapılır, cat
  • boru veya ekstra işlem söz konusu değildir
  • Yukarıdakilerin bir sonucu olarak, içerideki kod bir alt kabukta çalıştırılmadığından, herhangi bir değişiklik bundan sonra kalır ( $(<${file=foo.txt})veya içinde olduğu gibi $(<file$((++n))))
  • okuma hataları (dosyaları açarken veya dosya tanımlayıcılarını kopyalarken değil) sessizce yok sayılır.

Gelen zsh, sadece bir dosya giriş yönlendirme varken bu özel davranışı yalnızca tetiklenir dışında aynıdır ( <fileveya 0< filehayır <&3, <<<here, < a < b...)

Bununla birlikte, diğer mermileri taklit ederken hariç:

< file
<&3
<<< here...

yani komut olmadan yalnızca komutlar olmadan girdi yeniden yönlendirmeleri olduğunda, komut yerine dışında zsh, $READNULLCMD(varsayılan olarak bir çağrı cihazı) çalıştırır ve hem giriş hem de çıkış yeniden yönlendirmeleri olduğunda, $NULLCMD( catvarsayılan olarak), bu nedenle $(<&3)özel olarak tanınmasa bile operatör, bunu kshyapmak için bir çağrı cihazı çağırarak hala çalışacaktır ( catstdout bir boru olacağı için bu çağrı cihazı gibi davranır ).

Ancak ederken ksh'ın $(< a < b)içeriğine kadar genişleyip aiçinde, zshbu içeriğiyle genişler ave b(veya sadece beğer multiosseçenek devre dışı), $(< a > b)kopyalamak olurdu aetmek bve hiçbir şey vb genişletmek

bash benzer bir operatöre sahip, ancak birkaç farklılık var:

  • yorumlara daha önce izin verilir, ancak sonra izin verilmez:

    echo "$(
       # getting the content of file
       < file)"
    

    çalışır ama:

    echo "$(< file
       # getting the content of file
    )"
    

    hiçbir şeye genişlemez.

  • gibi zshbir hayır sonbahar arka tarafta olsa sadece bir dosya Stdin yönlendirme, $READNULLCMDböylece $(<&3), $(< a < b)yönlendirmeleri yapmak ama hiçbir şey genişletmek yoktur.

  • bazı nedenlerden dolayı, bashçağrılmasa da cat, yine de dosyanın içeriğini bir borudan besleyen ve diğer kabuklardan daha az optimizasyon sağlayan bir işlem ister. Bu a gibi yürürlükte bulunuyor bir yerleşik olacağını .$(cat < file)catcat
  • yukarıdakilerin bir sonucu olarak, içinde yapılan herhangi bir değişiklik daha sonra kaybolur ( $(<${file=foo.txt})örneğin yukarıda bahsedilen söz konusu $fileatama daha sonra kaybolur).

İçinde bash, IFS= read -rd '' var < file (içinde de çalışır zsh) bir metin dosyasının içeriğini bir değişkene okumak için daha etkili bir yoldur . Ayrıca, sondaki yeni satır karakterlerini koruma avantajına da sahiptir. Ayrıca $mapfile[file], ikili dosyalarla da çalışan zsh( zsh/mapfilemodüldeki ve yalnızca normal dosyalar için) bölümüne bakın .

Pdksh tabanlı varyantların kshksh93 ile karşılaştırıldığında birkaç varyasyonu olduğunu unutmayın. İlgilenilen, mksh(bu pdksh'den türetilmiş kabuklardan biri),

var=$(<<'EOF'
That's multi-line
test with *all* sorts of "special"
characters
EOF
)

burada belgenin içeriğinin (sondaki karakterler olmadan) geçici bir dosya veya boru kullanılmadan genişletilmesi, aksi takdirde burada belgelerde olduğu gibi genişletilmesi, böylece onu etkili bir çok satırlı tırnak sözdizimi haline getirir.

Tüm sürümlerine taşınabilir olmak ksh, zshve bash, en iyi sadece sınıra olan $(<file)değişkenlere modifikasyonlar may içinde veya korunmayabilir yapılan akılda görüş ve rulmanı kaçınarak.


$(<)Dosya adlarında bir operatör olması doğru mu? Mı <içinde $(<)bir yönlendirme operatörü, ya da değil kendi başına bir operatör ve tüm operatör parçası olmalıdır $(<)?
Tim

@ Zaman, onları nasıl aramak istediğiniz önemli değil. $(<file), içeriğine filebenzer şekilde genişlemesi amaçlanmaktadır $(cat < file). Nasıl yapıldığı cevabın uzunluğunda tarif edilen kabuktan kabuğa değişir. İsterseniz, bir komut ikamesine benzeyen (sözdizimsel olarak) tek bir stdin yönlendirmesine (sözdizimsel) benzeyen bir şey içerdiğinde tetiklenen özel bir operatör olduğunu söyleyebilirsiniz, ancak yine burada listelenen kabuğa bağlı olarak uyarılar ve varyasyonlar ile .
Stéphane Chazelas

@ StéphaneChazelas: Her zamanki gibi büyüleyici; Bunu yer imlerine ekledim. Yani, n<&mve n>&maynı şeyi yapmak? Bunu bilmiyordum, ama sanırım çok da şaşırtıcı değil.
Scott

@Scott, evet, ikisi de bir dup(m, n). Stdio ve bazılarını kullanarak ksh86'nın bazı kanıtlarını görebiliyorum fdopen(fd, "r" or "w"), o zaman önemli olabilirdi. Ancak stdio'yu bir kabukta kullanmak çok mantıklı değil, bu yüzden fark yaratacak herhangi bir modern kabuk bulacağınızı sanmıyorum. Bir fark olduğunu >&nolan dup(n, 1)(kısa 1>&nsüre) <&nolan dup(n, 0)(kısa 0<&n).
Stéphane Chazelas

Sağ. Tabii ki, dosya tanımlayıcı çoğaltma çağrısının iki bağımsız değişken formu denir dup2(); dup()yalnızca bir argüman alır ve open()kullanılabilir en düşük dosya tanımlayıcıyı kullanır. (Bugün bir dup3()işlev olduğunu öğrendim .)
Scott

8

Çünkü bashsizin için dahili olarak yapar, dosya adını genişletir ve dosyayı, yaptığınız gibi standart çıktıya gönderir $(cat < filename). Bu bir bash özelliğidir, belki de bashtam olarak nasıl çalıştığını bilmek için kaynak koduna bakmanız gerekir .

İşte bu özelliği işleme fonksiyonu ( bashKaynak kodundan, dosyadan builtins/evalstring.c):

/* Handle a $( < file ) command substitution.  This expands the filename,
   returning errors as appropriate, then just cats the file to the standard
   output. */
static int
cat_file (r)
     REDIRECT *r;
{
  char *fn;
  int fd, rval;

  if (r->instruction != r_input_direction)
    return -1;

  /* Get the filename. */
  if (posixly_correct && !interactive_shell)
    disallow_filename_globbing++;
  fn = redirection_expand (r->redirectee.filename);
  if (posixly_correct && !interactive_shell)
    disallow_filename_globbing--;

  if (fn == 0)
    {
      redirection_error (r, AMBIGUOUS_REDIRECT);
      return -1;
    }

  fd = open(fn, O_RDONLY);
  if (fd < 0)
    {
      file_error (fn);
      free (fn);
      return -1;
    }

  rval = zcatfd (fd, 1, fn);

  free (fn);
  close (fd);

  return (rval);
}

$(<filename)Tam olarak eşdeğer olmayan bir not $(cat filename); dosya adı bir tire işareti ile başlarsa, ikincisi başarısız olur -.

$(<filename)Başlangıçta oldu kshve eklendi bashden Bash-2.02.


1
cat filenamedosya seçenekleri kabul ettiğinden dosya adı bir tire işareti ile başlarsa başarısız olur. İle en modern sistemlerde bunun etrafında çalışabilirsiniz cat -- filename.
Adam Katz

-1

Komut yerine koymayı bir komutu her zamanki gibi çalıştırmayı ve çıktıyı komutu çalıştırdığınız noktada boşaltma olarak düşünün.

Komutların çıktısı başka bir komuta argüman olarak, bir değişken ayarlamak ve hatta bir for döngüsünde argüman listesi oluşturmak için kullanılabilir.

foo=$(echo "bar")değişkenin $foodeğerini bar; komutun çıktısı echo bar.

Komut Değiştirme


1
OP'nin komuta ikamesinin temellerini anladığı sorudan oldukça açık olduğuna inanıyorum; soru özel durumla ilgilidir $(< file)ve genel durum hakkında bir eğiticiye ihtiyacı yoktur. Eğer $(< file)bunun sadece bir $(command)emri olan sıradan bir durum olduğunu söylüyorsanız , Adam Katz'ın söylediği< file aynı şeyi söylüyorsunuz ve ikiniz de yanılıyorsunuz.
Scott
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.