Bash betiğinde argümanlar üzerinde yineleme
Kabuk/bash betiği yapmak istediğim karmaşık bir komutum var. Bunu $1
cinsinden kolayca yazabilirim:
foo $1 args -o $1.ext
Script'e birden fazla girdi adı aktarabilmek istiyorum. Bunu yapmanın doğru yolu nedir?
Ve elbette, içinde boşluk olan dosya adlarını işlemek istiyorum.
830
3
Tüm argümanları temsil etmek için
"$@"
kullanın:Bu, her argümanı yineleyecek ve ayrı bir satıra yazdıracaktır. $@, $* gibi davranır, ancak alıntılandığında argümanlar içinde boşluk varsa düzgün bir şekilde ayrılır:
Şimdi silinmiş bir answer'ın VonC tarafından yeniden yazılması. Robert Gamble'ın kısa ve özlü cevabı doğrudan soruyla ilgilidir. Bu cevap, boşluk içeren dosya adları ile ilgili bazı sorunları güçlendirmektedir. Ayrıca bakınız: ${1:+"$@"} in /bin/sh Temel tez:
"$@"
doğrudur ve$*
(tırnaksız) neredeyse her zaman yanlıştır. Çünkü"$@"
argümanlar boşluk içerdiğinde iyi çalışır ve çalışmadığında$*
ile aynı şekilde çalışır. Bazı durumlarda,"$*"
da tamamdır, ancak"$@"
genellikle (ancak her zaman) aynı yerlerde çalışır. Tırnak içine alınmamış,$@
ve$*
eşdeğerdir (ve neredeyse her zaman yanlıştır). Peki,$*
,$@
,"$*"
ve"$@"
arasındaki fark nedir? Hepsi 'kabuğun tüm argümanları' ile ilgilidir, ancak farklı şeyler yaparlar. Tırnak içine alınmadığında,$*
ve$@
aynı şeyi yapar. Her 'kelimeyi' (boşluksuz diziler) ayrı bir argüman olarak ele alırlar. Ancak alıntılanmış formlar oldukça farklıdır:"$*"
argüman listesini boşluk bırakılarak ayrılmış tek bir dize olarak ele alırken,"$@"
argümanları neredeyse tam olarak komut satırında belirtildikleri gibi ele alır."$@"
pozisyonel argümanlar olmadığında hiçbir şeye genişlemez;"$*"
boş bir dizeye genişler — ve evet, algılamak zor olsa da bir fark var'dır. Aşağıda, (standart olmayan)al
komutunun tanıtımından sonra daha fazla bilgiye bakın. İkincil tez: boşluk içeren argümanları işlemeniz gerekiyorsa ve ardından bunları diğer komutlara aktarırsanız, bazen standart olmayan yardımcı olacak araçlar. (Ya da dizileri dikkatli kullanmalısınız:"${array[@]}"
,"$@"
ile benzer şekilde davranır). Örnek:Bu neden işe yaramıyor? Çalışmıyor çünkü kabuk tırnak işaretlerini genişletmeden önce işliyor değişkenler. Yani, kabuğun
$var
içine gömülü tırnak işaretlerine dikkat etmesini sağlamak için, 'eval' kullanmanız gerekir:gibi dosya adlarına sahip olduğunuzda bu gerçekten zorlaşır, "Don't do this!"`" (tırnak işaretleri, çift tırnak işaretleri ve boşluklarla).
Kabuklar (hepsi) bu tür işlemlerin yapılmasını özellikle kolaylaştırmaz şeyler, bu yüzden (yeterince komik bir şekilde) birçok Unix programı iyi bir iş yapmıyor onları idare etmek. Unix'te, bir dosya adı (tek bileşen) aşağıdakiler dışında herhangi bir karakter içerebilir eğik çizgi ve NUL
'\0'
. Bununla birlikte, kabuklar boşluk, yeni satır veya sekme olmamasını şiddetle tavsiye eder bir yol adının herhangi bir yerinde. Standart Unix dosya adlarının boşluk vb. içermemesinin nedeni de budur. Boşluk içerebilen dosya adları ile uğraşırken ve diğer sorunlu karakterler, son derece dikkatli olmak zorundasınız ve ben Uzun zaman önce Unix'te standart olmayan bir programa ihtiyacım olduğunu fark ettim. Ben bunaescape
adını verdim (1.1 sürümü 1989-08-23T16:01:45Z tarihlidir). İşte SCCS kontrol sistemi ile kullanılan birescape
örneği. Bu, hemdelta
(check-in olarak düşünün) hem deget
(check-out olarak düşünün). Çeşitli argümanlar, özellikle-y
(değişikliği neden yaptığınız) boşlukları ve satırsonlarını içerecektir. Kodun 1992'den kalma olduğunu unutmayın, bu nedenle $(cmd ...)notasyonunu kullanır ve ilk satırda
#!/bin/sh` kullanmaz.(Muhtemelen bugünlerde kaçışı bu kadar kapsamlı kullanmazdım - bu örneğin
-e
argümanı ile gerekli değildir - ancak genel olarak bu kullanarak daha basit komut dosyalarımdan biri). escapeprogramı,
echogibi argümanlarını basitçe çıktı olarak verir yapar, ancak argümanların aşağıdakilerle kullanım için korunmasını sağlar
eval(
evalin bir seviyesi; uzaktan kabukla çalışan bir programım var yürütme ve
escape` çıktısından kaçmak için gerekli olan).Argümanlarını her satırda bir listeleyen
al
adında başka bir programım var (ve daha da eskidir: 1987-01-27T14:35:49 tarihli 1.1 sürümü). En çok komut dosyalarında hata ayıklama yaparken kullanışlıdır, çünkü bir komut satırında komuta gerçekte hangi argümanların aktarıldığını görmek için.[Eklendi: Ve şimdi çeşitli
"$@"
gösterimleri arasındaki farkı göstermek için, işte bir örnek daha:Komut satırındaki
*
ve*/*
arasındaki orijinal boşlukların hiçbir şekilde korunmadığına dikkat edin. Ayrıca, kabukta 'komut satırı argümanlarını' kullanarak değiştirebileceğinizi unutmayın:Bu, '
-new
', '-opt
', 'and
' ve 'arg with space
' olmak üzere 4 seçenek belirler.] Hmm, bu oldukça uzun bir cevap - belki de tefsir daha iyi bir terimdir. escape
için kaynak kodu istek üzerine mevcuttur (ilkisim nokta'ya e-posta lastname at gmail dot com). al
için kaynak kodu inanılmaz derecede basittir:Hepsi bu. Robert Gamble
ın gösterdiği
test.shbetiğine eşdeğerdir ve bir kabuk fonksiyonu olarak yazılabilir (ancak
alı ilk yazdığımda Bourne kabuğunun yerel sürümünde kabuk fonksiyonları yoktu). Ayrıca
al` komutunu basit bir kabuk betiği olarak yazabileceğinizi de unutmayın:Hiçbir argüman iletilmediğinde hiçbir çıktı üretmemesi için koşul gereklidir. printf` komutu sadece format string argümanı ile boş bir satır üretecektir, ancak C programı hiçbir şey üretmez.
Robert'ın cevabının doğru olduğunu ve `sh' içinde de çalıştığını unutmayın. Bunu (taşınabilir şekilde) daha da basitleştirebilirsiniz:
eşdeğerdir:
Yani, hiçbir şeye ihtiyacınız yok!
Test (
$
komut istemidir):Bu konuyu ilk olarak Kernighan ve Pike tarafından yazılan Unix Programming Environment adlı kitapta okumuştum.
bash
içinde,
help for` bunu belgelemektedir: