Her zaman değişken değiştirmeler ve komut değiştirmelerin çift tırnak kullanın: "$foo"
,"$(foo)"
Eğer $foo
alıntılanmamış kullanıyorsanız, betiğiniz girdi veya parametreler (veya komut çıktısı ile birlikte $(foo)
) boşluk veya boğulacak \[*?
.
Orada okumayı bırakabilirsin. Tamam, işte birkaç tane daha:
read
- Giriş satırını read
yerleşik yapıyla satır satır okumak içinwhile IFS= read -r line; do …
Plain read
ters eğik çizgi ve boşlukları özel olarak kullanın.
xargs
- Kaçınınxargs
. Eğer kullanmak zorundaysan xargs
, bunu yap xargs -0
. Yerine find … | xargs
, tercihfind … -exec …
.
xargs
boşluk ve karakterleri \"'
özel olarak ele alır .
Bu cevap Bourne / POSIX tarzı kabukları (uygulanır sh
, ash
, dash
, bash
, ksh
, mksh
, yash
...). Zsh kullanıcıları bunu atlamalı ve Çift alıntı ne zaman gereklidir? yerine. Nitty-gritty'nin tamamını istiyorsanız, standardı veya kabuğunuzun kullanım kılavuzunu okuyun.
Aşağıdaki açıklamaların birkaç yaklaşım içerdiğini unutmayın (çoğu durumda doğru olan ifadeler, ancak çevre bağlamdan veya yapılandırmadan etkilenebilir).
Neden yazmam gerekiyor "$foo"
? Tırnaklar olmadan ne olur?
$foo
“Değişkenin değerini al” anlamına gelmez foo
. Çok daha karmaşık bir şey demektir:
- İlk önce değişkenin değerini alın.
- Alan bölme: bu değeri boşlukla ayrılmış alan listesi olarak ele alın ve elde edilen listeyi oluşturun. Değişken içeriyorsa, örneğin,
foo * bar
bu aşamanın sonucu 3-eleman listesi foo
, *
, bar
.
- Dosya adı oluşturma: Her alanı bir küreye, örneğin bir joker kalıbı olarak kabul edin ve bu kalıba uyan dosya adları listesi ile değiştirin. Desen herhangi bir dosyayla eşleşmezse, değiştirilmemiş olarak bırakılır. Örneğimizde bu
foo
, geçerli dizindeki dosyaların listesini izleyen ve son olarak içeren listeyle sonuçlanır bar
. Geçerli dizin boşsa, sonuç foo
, *
, bar
.
Sonuç, dizelerin bir listesidir. Kabuk sözdiziminde iki bağlam vardır: list bağlamı ve string içeriği. Alan bölme ve dosya adı oluşturma yalnızca liste bağlamında gerçekleşir, ancak çoğu zaman bu olur. Çift tırnak, bir dize bağlamını sınırlar: çift tırnaklı dizenin tamamı bölünmeyecek tek bir dizedir. (İstisna: "$@"
konumsal parametreler listesine genişletmek için, örneğin üç konumsal parametre varsa buna "$@"
eşdeğerdir "$1" "$2" "$3"
. Bkz. $ * Ve $ @ arasındaki fark nedir? )
Aynısı $(foo)
veya ile ikame komutuna olur `foo`
. Bir yandan, kullanmayın `foo`
: alıntı kuralları tuhaf ve taşınabilirdir ve tüm modern kabuklar $(foo)
, sezgisel alıntı kurallarına sahip olması dışında kesinlikle eşdeğerdir.
Aritmetik ikamenin çıktısı da aynı genişlemelere maruz kalır, ancak normalde yalnızca genişletilemeyen karakterler içerdiğinden endişe IFS
duymaz ( rakamlar içermez veya -
).
Bkz . Çift alıntı ne zaman gereklidir? Teklifleri dışarıda bırakabileceğiniz durumlar hakkında daha fazla bilgi için
Tüm bu rigmarollerin gerçekleşmesini kastetmediğin sürece, değişken ve komut değişimlerinin etrafında her zaman çift tırnak kullanmayı unutma. Dikkatli olun: tırnak işaretleri dışında bırakmak sadece hatalara değil güvenlik deliklerine de yol açabilir .
Dosya adlarının bir listesini nasıl işlerim?
myfiles="file1 file2"
Dosyaları ayırmak için boşluklarla yazarsanız , boşluk içeren dosya adlarıyla çalışamazsınız. Unix dosya adları /
(her zaman bir dizin ayırıcı olan) ve boş bayt (çoğu kabuklu kabuk komut dosyalarında kullanamayacağınız) dışında herhangi bir karakter içerebilir .
Aynı sorun myfiles=*.txt; … process $myfiles
. Bunu yaptığınızda, değişken myfiles
5-karakter dizesi içeren *.txt
ve yazarken bu kadar $myfiles
joker genişletilmiş olması. Bu örnek aslında siz komut dosyasını değiştirinceye kadar çalışacaktır myfiles="$someprefix*.txt"; … process $myfiles
. Eğer someprefix
ayarlanmışsa final report
, bu işe yaramaz.
Herhangi bir türün (dosya adları gibi) işlenmesi için, bir diziye yerleştirin. Bu, mksh, ksh93, yash veya bash (ya da tüm bu alıntılama konularına sahip olmayan zsh) gerektirir; Düz bir POSIX kabuğu (kül veya çizgi gibi) dizi değişkenlerine sahip değildir.
myfiles=("$someprefix"*.txt)
process "${myfiles[@]}"
Ksh88 farklı atama sözdizimine sahip dizi değişkenlerine sahiptir set -A myfiles "someprefix"*.txt
( ksh88 / bash taşınabilirliğine ihtiyacınız varsa farklı ksh ortamı altındaki atama değişkenine bakınız ). Bourne / POSIX stili mermilerin tek bir dizisi vardır, "$@"
ayarladığınız set
ve bir işleve yerel olan konumsal parametre dizisi :
set -- "$someprefix"*.txt
process -- "$@"
Peki ya başlayan dosya isimleri -
?
İlgili bir notta, dosya adlarının -
çoğu komutun bir seçeneği ifade ettiği şeklinde yorumladığı bir (kısa çizgi / eksi) ile başlayabileceğini unutmayın . Değişken bir bölümle başlayan bir dosya adınız --
varsa, yukarıdaki snippet'te olduğu gibi bundan önce geçtiğinizden emin olun . Bu, komutun seçeneklerin sonuna ulaştığını gösterir, bundan sonra herhangi bir şey, başlasa bile bir dosya adıdır -
.
Alternatif olarak, dosya adlarınızın başka bir karakterle başladığından emin olabilirsiniz -
. Mutlak dosya adları ile başlar /
ve ./
göreli adların başına ekleyebilirsiniz . Aşağıdaki kod parçası, değişkenin içeriğini, f
başlamaması garanti edilen aynı dosyaya gönderme yapmanın “güvenli” bir yoluna dönüştürür -
.
case "$f" in -*) "f=./$f";; esac
Bu konuyla ilgili son bir notta, bazı komutların -
, sonrasında bile standart girdi veya standart çıktı olarak yorumlandığına dikkat edin --
. Adında gerçek bir dosyaya başvurmanız gerekiyorsa -
veya böyle bir programı çağırıyorsanız ve stdin'den okumak veya stdout'a yazmak istemiyorsanız -
, yukarıdaki gibi tekrar yazdığınızdan emin olun . Bkz. "Du -sh *" ve "du -sh ./*" arasındaki fark nedir? daha fazla tartışma için.
Bir değişkeni bir komutu nasıl saklarım?
“Komut” üç şey anlamına gelebilir: bir komut adı (tam yollu veya tam yolu olmayan yürütülebilir bir ad veya bir işlev adı, yerleşik veya diğer ad), bağımsız değişkenli bir komut adı veya bir kabuk kodu parçası. Bunları değişkende saklamanın farklı yolları vardır.
Bir komut adınız varsa, sadece saklayın ve değişkeni her zamanki gibi çift tırnaklı kullanın.
command_path="$1"
…
"$command_path" --option --message="hello world"
Argümanları olan bir komutunuz varsa, sorun yukarıdaki dosya adlarının listesiyle aynıdır: bu bir string değil, bir string listesidir. Argümanları yalnızca aralarında boşluk olan tek bir dizgeye dolduramazsınız, çünkü bunu yaparsanız argümanların parçası olan ve argümanları ayıran boşluklar arasındaki farkı söyleyemezsiniz. Eğer kabuğunuzda diziler varsa, onları kullanabilirsiniz.
cmd=(/path/to/executable --option --message="hello world" --)
cmd=("${cmd[@]}" "$file1" "$file2")
"${cmd[@]}"
Ya dizileri olmayan bir kabuk kullanıyorsanız? Bunları değiştirmek sakıncası yoksa, pozisyon parametrelerini kullanabilirsiniz.
set -- /path/to/executable --option --message="hello world" --
set -- "$@" "$file1" "$file2"
"$@"
Ya yönlendirmeler, borular vb. Gibi karmaşık bir kabuk komutunu saklamanız gerekirse? Veya konumsal parametreleri değiştirmek istemiyorsanız? Sonra komutu içeren bir dize oluşturabilir ve eval
yerleşimi kullanabilirsiniz .
code='/path/to/executable --option --message="hello world" -- /path/to/file1 | grep "interesting stuff"'
eval "$code"
İçindeki tırnakların tanımındaki notlara dikkat edin code
: tekli tırnaklar '…'
bir dize değişmezini sınırlandırır, böylece değişkenin değeri code
dizedir /path/to/executable --option --message="hello world" -- /path/to/file1
. eval
Builtin Senaryoda çıktı sanki bir argüman olarak geçirilen dize ayrıştırmak kabuk söyler, böylece bu noktada tırnak ve boru vb işlendiğinden
Kullanımı eval
zor. Ne zaman ayrıştırılacağını dikkatlice düşünün. Özellikle, bir dosya adını koda yazamazsınız: bir kaynak kod dosyasında olduğu gibi alıntı yapmanız gerekir. Bunu yapmanın doğrudan yolu yok. Gibi bir şey code="$code $filename"
dosya adı herhangi kabuk özel karakter içeriyorsa sonları (boşluk, $
, ;
, |
, <
, >
, vb.) code="$code \"$filename\""
Hala kırılıyor "$\`
. code="$code '$filename'"
Dosya adı a içeriyorsa, sonları bile '
. İki çözüm var.
Dosya adının etrafına bir tırnak katmanı ekleyin. Bunu yapmanın en kolay yolu, etrafına tek tırnak işaretleri eklemek ve tek tırnak işaretleri yerine kullanmaktır '\''
.
quoted_filename=$(printf %s. "$filename" | sed "s/'/'\\\\''/g")
code="$code '${quoted_filename%.}'"
Değişken genişlemesini kodun içinde tutun, böylece kod parçası oluşturulduğunda değil, kod değerlendirildiğinde aranır. Bu daha basittir ancak yalnızca değişken kodun yürütüldüğü sırada hala aynı değere sahipse çalışır, örneğin kod bir döngüde oluşturulmuşsa değil.
code="$code \"\$filename\""
Son olarak, gerçekten kod içeren bir değişkene ihtiyacınız var mı? Bir kod bloğuna isim vermenin en doğal yolu bir işlev tanımlamaktır.
Neyin var read
?
Olmadan -r
, read
devam satırlarına izin verir - bu tek bir mantıksal girdi satırıdır:
hello \
world
read
giriş satırını, karakterlerle sınırlandırılmış alanlara böler $IFS
(olmadan -r
, ters eğik çizgi de bunlardan kaçınır). Örneğin, giriş üç kelimelik bir çizgi ise, girişin ilk kelimesine , ikinci kelimeye ve üçüncü kelimeye read first second third
ayarlanır . Daha fazla kelime varsa, son değişken öncekileri ayarladıktan sonra geriye kalan her şeyi içerir. Lider ve takip eden boşluklar kırpılıyor.first
second
third
IFS
Boş dizeye ayarlamak herhangi bir kırpmayı önler. Bkz. Neden “` IFS = read` `, IFS = yerine; okurken ... daha uzun bir açıklama için.
Neyin var xargs
?
Giriş biçimi xargs
, isteğe bağlı olarak tek veya çift tırnaklı olabilen, boşlukla ayrılmış dizelerdir. Bu formatta hiçbir standart araç çıkmamaktadır.
Girdi xargs -L1
veya xargs -l
neredeyse bir çizgi listesidir, ancak tam olarak değil - bir satırın sonunda boşluk varsa, aşağıdaki satır bir devam satırıdır.
Uygun olan xargs -0
yerlerde kullanabilirsiniz (ve mümkün olan yerlerde: GNU (Linux, Cygwin), BusyBox, BSD, OSX, ancak POSIX’de değildir). Bu güvenlidir, çünkü boş baytlar çoğu veride, özellikle de dosya adlarında görünemez. Boş bir dosya adı listesi oluşturmak için, kullanın find … -print0
(veya find … -exec …
aşağıda açıklandığı şekilde kullanabilirsiniz ).
Tarafından bulunan dosyaları nasıl işlerim find
?
find … -exec some_command a_parameter another_parameter {} +
some_command
harici bir komut olması gerekir, bir kabuk işlevi veya diğer adı olamaz. Dosyaları işlemek için bir kabuk çağırmanız gerekirse, sh
açıkça arayın .
find … -exec sh -c '
for x do
… # process the file "$x"
done
' find-sh {} +
Başka bir sorum var
Bu sitedeki alıntı etiketine veya kabuk veya kabuk komut dosyasına göz atın . (Bazı genel ipuçlarını ve el ile seçilen bir ortak sorular listesini görmek için “daha fazla bilgi…” seçeneğine tıklayın.) Eğer aradıysanız ve bir cevap bulamadıysanız, sorun .
shellcheck
programlarınızın kalitesini yükseltmenize yardımcı olur.