“$ ()” Tırnak işaretli değişken


12

Bu senaryoyu yazdım:

#!/bin/bash
while [ true ] 
do
    currentoutput="$(lsusb)"
    if [ "$currentoutput" != "$lastoutput" ]
    then
        echo "" date and Time >> test.log
        date +%x_r >> test.log
        lastoutput="$(lsusb)"
        lsusb >> test.log
    fi
    sleep 5
done

Hızlı öğrenmeye çalışan bir acemiyim ve değişkenin tırnak işaretleri hakkında bir sorum var .

$ () Arasına bir değişken koy, anlıyorum, ama neden tırnak işaretleriif ifadede bile gerekli ? Yuvalanmış bir komut yapmak mı?


5
Bunun while [ true ]sonsuz bir döngü oluşturduğunu, ancak belki de düşündüğünüz nedenden dolayı olmadığını unutmayın; while [ false ] Ayrıca sonsuz bir döngü üretir, çünkü tek bir argümanla, [ ... ]bu argüman boş olmayan bir dize ise başarılı olur. while trueaslında (her zaman başarılı olur) adlı bir komutu çalıştırırtrue .
chepner

4
İçeri bir değişken koymazsınız $(). Bir koyun komut içini $().
Wildcard

Yanıtlar:


23

Tırnak işaretleri "sözcük bölünmesini" önler. Yani: değişkenleri boşluk karakterlerinde birden çok öğeye ayırmak (veya boşluklarda, sekmelerde ve yeni satırlarda varsayılan $IFSkabuk değişkeninin değerinde tanımlandığı gibi daha kesin olmak gerekirse ).

Örneğin,

$ var="one two"
$ howmany(){ echo $#;  }
$ howmany $var
2
$ howmany "$var"
1

Burada howmanykaç tane konum parametresinin verildiğini bize bildiren fonksiyonu tanımlıyoruz . Gördüğünüz gibi, değişkene iki öğe aktarılır ve tırnak işaretleri ile değişken içindeki metin bir birim olarak kabul edilir.

Bilginin doğru bir şekilde iletilmesi için bu önemlidir. Örneğin, değişken dosya yolu içeriyorsa ve dosya adı yolun herhangi bir yerinde boşluk içeriyorsa, çalıştırmaya çalıştığınız komut başarısız olabilir veya yanlış sonuçlar verebilir. $varDeğişkenle bir dosya touch $varoluşturmaya çalışsaydık , iki dosya oluşturacaktı, touch "$var"sadece bir tane.

Aynı şey senin için de geçerli [ "$currentoutput" != "$lastoutput" ]. Bu özel test iki karakter dizisi üzerinde bir karşılaştırma yapar. Test çalıştırıldığında, [komutun bir metin dizesi, !=operatör ve başka bir metin dizesi olmak üzere 3 bağımsız değişkeni görmesi gerekir . Çift tırnak tutmak kelime bölünmesini önler ve [komut tam olarak bu 3 argümanı görür. Şimdi değişkenler tırnak içine alınmazsa ne olur?

$ var="hello world"
$ foo="hi world"
$ [ $var != $foo ]
bash: [: too many arguments
$ 

Burada, kelime bölme oluşur ve bunun yerine [iki dizeleri görür hellove worldbunu takiben !=diğer iki dizeleri ardından hi world. Kilit nokta, çift tırnak işaretleri olmadan, değişkenlerin içeriğinin bir tam öğe yerine ayrı birimler olarak anlaşılmasıdır.

Komut ikamesi atamak, aşağıdaki gibi çift tırnak işareti gerektirmez

var=$( df )

burada dfkomutun çıktısını kaydedersiniz var. Ancak, $(...)çıktının ayrı öğeler olarak ele alınmasını istemiyorsanız, değişkenleri her zaman ikiye katlamak ve ikame komutunu değiştirmek iyi bir alışkanlıktır .


Yan notta,

while [ true ]

bölüm olabilir

while true

[argümanlarını değerlendiren bir komuttur ve [ whatever ]içinde ne olursa olsun her zaman doğrudur. Buna karşılık, her zaman başarı çıkış durumunu döndüren while truekomutu kullanır true(ve tam olarak whiledöngü gereklidir). Aradaki fark biraz daha fazla netlik ve daha az test yapılmasıdır. Alternatif olarak, kullanabilirsiniz :yerinetrue

echo "" date and TimeKısmen çift ​​tırnak işaretleri muhtemelen kaldırılabilir. Yalnızca boş bir dize ve çıktıya fazladan boşluk eklerler. İstenirse, onları orada tutmaktan çekinmeyin, ancak bu durumda belirli bir fonksiyonel değer yoktur.

lsusb >> test.log

Bu bölüm muhtemelen değiştirilebilir echo "$currentoutput" >> test.log. lsusbZaten içeri girdikten sonra tekrar koşmak için hiçbir sebep yok currentoutput=$(lsusb). Sondaki satırsonlarının çıktıda korunması gerektiği durumlarda, bir komutun birden çok kez çalıştırılmasının değeri görülebilir, ancak lsusbbuna gerek yoktur. Ne kadar az harici komut çağırırsanız o kadar iyidir, çünkü yerleşik olmayan bir komuta her çağrı CPU, bellek kullanımı ve yürütme süresinde maliyete neden olur (komutlar muhtemelen bellekten önceden yüklenmiş olsa bile).


Ayrıca bakınız:


1
Harika cevap, daha önce yapmadıysanız hakkında bir kitap yazmalısınız bash. Ama son bölümde echo "$currentoutput" >> test.log daha iyi olmamalı printf '%s\n' "$currentoutput" >> test.log mıydı?
pLumo

2
@RoVo Evet, printftaşınabilirlik için tercih edilmelidir. Burada bash'a özgü komut dosyası kullandığımız için kullanmak için mazur görülebilir echo. Ancak gözleminiz çok doğru
Sergiy Kolodyazhnyy

1
@SergiyKolodyazhnyy, ... echoKabuğun bash olduğu bilinse bile (ve xpg_echove posixbayraklarının değeri biliniyorsa) bunun tamamen güvenilir olduğundan emin değilim ; değeriniz olabilir -nveya -eyazdırılmak yerine kaybolabilir.
Charles Duffy

4

Gelen currentoutput="$(lsusb)"lsusb değişken değil, bir komuttur. Bu ifadenin yaptığı, lsusbkomutu yürütür ve çıktısını currentoutputdeğişkene atar .

Bunun için eski sözdizimi

currentoutput=`lsusb`

birçok örnekte ve komut dosyasında bulabilirsiniz

Sorunuzun diğer kısmını yanıtlamak için, if [ ]söz diziminin ifbash'de nasıl tanımlandığıdır. Https://www.tldp.org/LDP/Bash-Beginners-Guide/html/sect_07_01.html adresinde daha fazlasını görün


3
Bunun [ ]aslında testkomut olduğunu söylemek önemli . ifÇıkışları için 0 veya sıfır olmayan bir sınamaya dayandığından, ifadeleri diğer komutlarla da kullanabilirsiniz .
Arronik

... gerçekten, koşmak if grep -qe "somestring" filekoşmaktan çok daha iyidir grep -qe "somestring" file; if [ $? = 0 ]; then ..., bu yüzden sözdiziminin if [ ...tanımının bir parçası olduğu iddiası ifsadece yanıltıcı değildir, aynı zamanda kötü uygulamalara yol açar.
Charles Duffy

4

Aşağıdaki komut harici komutu çalıştırır commandve çıktısını verir.

"$(command)"

Köşeli ayraçlar / parantezler olmadan, bu bir komut çalıştırmak yerine bir değişken arar:

"$variable"

Arasındaki fark gelince $variableve "$variable"bu zaman önem kazanır $variableboşluklar içeriyor. Kullanırken "$variable", içerik boşluklar içeriyor olsa bile değişken içeriğin tamamı tek bir dizeye eklenir. $variableDeğişken içeriği kullanılırken çoklu argümanların bir argüman listesine genişletilebilir.


HI @thomasrutter, üzgünüm, tırnak işareti demek istedim ... Yorumumu şimdi düzenledim!
Shankhara

1

Karşıt olmak için - bash , testteki değişkenleri alıntılamaktan kaçınmak [[ ... ]]için [ ... ]yapıyı üzerinden kullanmanızı ve böylece diğerlerinin işaret ettiği kelime bölünmesinin ilişkili problemlerini kullanmanızı önerir .

[altında çalıştırmak #!/bin/shveya bash üzerine taşınan komut dosyalarıyla POSIX uyumluluğu için bash'da sağlanmıştır - çoğunlukla, bundan kaçınmalısınız [[.

Örneğin

# With [ - quotes are needed

$ foo='one two'; bar='one two'; [ $foo = $bar ] && echo "they're equal"
-bash: [: too many arguments

$ foo='one two'; bar='one two'; [ "$foo" = "$bar" ] && echo "they're equal"                                                                                                              
they're equal

# versus [[ - quotes not needed

$ foo='one two'; bar='one two'; [[ $foo = $bar ]] && echo "they're equal"
they're equal


bashböyle bir öneri sunmaz; o sağlar [[ ... ]] daha uygun olabilen ve bazı işlevler vardır [ ... ]değil ama kullanılması sorun yoktur [ ... ] doğru .
chepner

@chepner - Belki burada daha açık olmalıyım. Tabii ki bash sayfasında açık bir öneri değil ama verilen [[her şeyi [yapar ve daha fazlasını yapar ve hala birçok kez [insanları yukarı gezer ve sonra sadece başarısız olmak ve hataları hakkında dağınık bırakmak için [[sırt özelliklerini geri deneyin ve güçlendirin [- bunun bir en alakalı bash stil rehberlerinin asla kullanılmamasını önerdiği ölçüde topluluk tavsiyesi . [
shalomb
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.