Sadece echo vs printf değil
İlk olarak, read a b c
kısmen ne olduğunu anlayalım. space-tab-newline read
olan IFS
değişkenin varsayılan değerine dayalı olarak kelime ayırma gerçekleştirir ve buna dayalı her şeye uyar. Tutmak için değişkenlerden daha fazla girdi varsa, bölünmüş parçaları ilk değişkenlere sığdırır ve yerleştirilemez - en sonuncusuna gider. Demek istediğim şu:
bash-4.3$ read a b c <<< "one two three four"
bash-4.3$ echo $a
one
bash-4.3$ echo $b
two
bash-4.3$ echo $c
three four
Bu tam olarak bash
kılavuzunda (cevabın sonundaki alıntıya bakınız).
Sizin durumunuzda olan şey şu ki, 1 ve 2 a ve b değişkenlerine uyuyor ve c diğer her şeyi alıyor, yani 3 4 5 6
.
Ayrıca birçok kez göreceğiniz şey, insanların while IFS= read -r line; do ... ; done < input.txt
metin dosyalarını satır satır okumak için kullandıklarıdır . Yine, IFS=
kelime bölmeyi kontrol etmek veya daha spesifik olarak - devre dışı bırakmak ve tek bir metin satırını bir değişkene okumak için burada. Orada read
olmasaydı , her bir kelimeyi line
değişkene sığdırmaya çalışırdı. Ama bu, daha sonra çalışmanızı teşvik ettiğim başka bir hikaye, çünkü while IFS= read -r variable
çok sık kullanılan bir yapı.
echo vs printf davranışı
echo
burada beklediğinizi yapar. Değişkenlerinizi tam olarak read
düzenledikleri gibi görüntüler . Bu, daha önceki tartışmalarda zaten gösterilmiştir.
printf
çok özeldir, çünkü değişkenler hepsi bitene kadar biçim dizesine sığdırmaya devam eder. Yani printf "%d, %d, %d \n" $a $b $c
printf yaptığınızda biçim dizesini 3 ondalık sayı ile görür, ancak 3'ten daha fazla argüman vardır (çünkü değişkenleriniz aslında tek tek 1,2,3,4,5,6'ya genişler). Bu kafa karıştırıcı gelebilir, ancak bir nedenden ötürü, gerçek printf()
işlevin C dilinde yaptıklarından geliştirilmiş davranış olarak vardır .
Burada da çıktıyı etkileyen, değişkenlerinizin alıntılanmamasıdır, bu da kabuğun (değil printf
) değişkenleri 6 ayrı öğeye ayırmasına izin verir . Bunu alıntı ile karşılaştırın:
bash-4.3$ read a b c <<< "1 2 3 4"
bash-4.3$ printf "%d %d %d\n" "$a" "$b" "$c"
bash: printf: 3 4: invalid number
1 2 3
Tam olarak $c
değişken alıntılandığından, artık tek bir dize olarak tanınır 3 4
ve %d
sadece tek bir tam sayı olan biçime uymaz
Şimdi aynı şeyi alıntı yapmadan yapın:
bash-4.3$ printf "%d %d %d\n" $a $b $c
1 2 3
4 0 0
printf
tekrar söylüyor: "Tamam, orada 6 öğe var ama biçim sadece 3 gösterir, bu yüzden bir şeyler sığdırmaya devam edeceğim ve kullanıcıdan gerçek girdi ile eşleşemediğim her şeyi boş bırakacağım".
Ve tüm bu durumlarda benim sözümü almak zorunda değilsiniz. Sadece çalıştırın strace -e trace=execve
ve kendiniz görün, komut aslında neyi "gör"?
bash-4.3$ strace -e trace=execve printf "%d %d %d\n" $a $b $c
execve("/usr/bin/printf", ["printf", "%d %d %d\\n", "1", "2", "3", "4"], [/* 80 vars */]) = 0
1 2 3
4 0 0
+++ exited with 0 +++
bash-4.3$ strace -e trace=execve printf "%d %d %d\n" "$a" "$b" "$c"
execve("/usr/bin/printf", ["printf", "%d %d %d\\n", "1", "2", "3 4"], [/* 80 vars */]) = 0
1 2 printf: ‘3 4’: value not completely converted
3
+++ exited with 1 +++
Ek Notlar
Charles Duffy'nin yorumlarda düzgün bir şekilde işaret ettiği gibi, komutunuzda kullandığınız bash
kendi yerleşik printf
olanı strace
, aslında /usr/bin/printf
kabuğun sürümünü değil sürümü çağırır . Küçük farkların yanı sıra, bu özel soruya olan ilgimiz için standart format belirteçleri aynıdır ve davranış aynıdır.
Ayrıca akılda tutulması gereken , sözdiziminin C veya sözdiziminde işlevi olan herhangi bir C benzeri dile daha aşina olduğunu belirtmekten printf
daha çok taşınabilir (ve bu nedenle tercih edilir) echo
olduğudur printf()
. Bu Bkz Terdon tarafından mükemmel cevabı konusunda printf
vs echo
. Çıkışı, Ubuntu'nun belirli sürümünüzde özel kabuğunuza göre düzenleyebilirsiniz, ancak farklı sistemlerde komut dosyalarını taşıyacaksanız, muhtemelen printf
yankı yerine tercih etmelisiniz . Belki de Ubuntu ve CentOS makineleriyle çalışan yeni başlayan bir sistem yöneticisisiniz, hatta belki de bildiğiniz FreeBSD bile - bu gibi durumlarda seçimler yapmanız gerekecektir.
Bash el kitabından alıntı, SHELL BUILTIN KOMUTLARI bölümü
[-ers] [-a aname] [-d ayırma] [-i metin] [-n nchars] [-N nchars] [-p istemi] [-t zaman aşımı] [-u fd] [ad ... ]
Bir satır, standart girişten veya -u seçeneğine bağımsız değişken olarak sağlanan dosya tanımlayıcı fd'den okunur ve ilk sözcük, ilk ada, ikinci ada ikinci kelimeye, vb. kelimeleri ve soyadına atanan araya giren ayırıcıları. Giriş akışından adlardan daha az okunan kelime varsa, kalan adlara boş değerler atanır. IFS'deki karakterler, kabuğun genişletme için kullandığı kuralların aynısını kullanarak satırı kelimelere bölmek için kullanılır (yukarıda Word Bölme altında açıklanmıştır).
strace
Dava ve diğeri arasındaki kayda değer bir fark -strace printf
kullanırken/usr/bin/printf
,printf
doğrudan bash'da aynı adla yerleşik kabuk kullanıyor. Her zaman aynı olmazlar - örneğin, bash örneğinde biçim belirleyicileri%q
ve yeni sürümlerde$()T
zaman biçimlendirmesi bulunur.