Bash'te ekrana yazı basmanın iki yerleşik yolu vardır: echo argümanlarını aralarına boşluk koyup sonuna yeni satır ekleyerek yazar; printf ise bir biçim dizgesine göre çıktı üretir ve kendiliğinden yeni satır eklemez. Kısa ve sabit mesajlar için echo yeterlidir; değişken içerik, hizalı tablo, sayı biçimlendirme veya taşınabilir davranış gerektiğinde printf kullanmak daha güvenlidir.

Bu yazıda echo komutunun -n, -e ve -E seçeneklerini, printf biçim belirteçlerini, Bash'e özgü %b, %q ve %(...)T biçimlerini ve çıktıyı bir değişkene yazan printf -v seçeneğini örneklerle anlatıyoruz. Serinin diğer konuları için Bash öğrenme rehberi sayfasına bakabilirsiniz.

echo: argümanları yaz, satırı bitir

Kılavuza göre echo, argümanlarını aralarına birer boşluk koyarak yazar ve sonuna yeni satır ekler; yazma hatası olmadıkça çıkış durumu 0'dır. Argümanları kelimelere ayıran kabuk olduğu için tırnaksız birden fazla boşluk tek boşluğa iner. -n seçeneği sondaki yeni satırı kaldırır:

bash
echo Merhaba    dünya
# çıktı: Merhaba dünya
echo "Merhaba    dünya"
# çıktı: Merhaba    dünya
echo -n "satır sonu yok"; echo " | devam"
# çıktı: satır sonu yok | devam

-e seçeneği ters eğik çizgili kaçış dizilerini yorumlar; -E bu yorumu, varsayılan olarak yorumlayan sistemlerde bile kapatır. Bash'te varsayılan durum kaçışların yorumlanmamasıdır, bu yüzden -e vermezseniz \n iki karakter olarak yazılır. \c kaçışı ise kendisinden sonraki bütün çıktıyı, sondaki yeni satır dahil, bastırır:

bash
echo 'bir\niki'
# çıktı: bir\niki
echo -e 'bir\niki'
# çıktı:
#   bir
#   iki
echo -e 'görünür\cgörünmez'; echo
# çıktı: görünür

echo neden bazen sürpriz yapar?

echo komutunun iki zayıf noktası vardır. Birincisi, kılavuzun belirttiği gibi echo, -- argümanını seçeneklerin sonu olarak yorumlamaz. İkincisi, yazdırmak istediğiniz değer -n veya -e gibi bir seçeneğe benziyorsa echo onu seçenek olarak alır. Bir değişkenin değerini bilmiyorsanız echo "$v" beklediğinizi yazmayabilir:

bash
v="-n"
echo "$v"; echo "(önceki komut hiçbir şey yazmadı)"
# çıktı: (önceki komut hiçbir şey yazmadı)
echo -- "$v"
# çıktı: -- -n
printf '%s\n' "$v"
# çıktı: -n

Üçüncü bir ayrıntı, xpg_echo kabuk seçeneğidir. Kılavuza göre bu seçenek echo komutunun seçenekleri yorumlayıp yorumlamayacağını ve kaçış dizilerini genişletip genişletmeyeceğini belirler. Açıldığında -e yazmadan da kaçışlar çözülür; POSIX modunda ise echo hiçbir argümanı seçenek saymaz. Yani aynı echo satırı, kabuğun ayarlarına göre farklı çıktı verebilir:

bash
shopt -s xpg_echo
echo 'bir\niki'
# çıktı:
#   bir
#   iki

printf: biçim dizgesi ve argümanlar

printf komutunun biçimi printf [-v değişken] biçim [argümanlar] şeklindedir. Kılavuza göre biçim dizgesi üç tür öğe içerir: olduğu gibi kopyalanan düz karakterler, dönüştürülüp yazılan kaçış dizileri ve her biri sıradaki argümanı yazdıran biçim belirteçleri. printf sona yeni satır eklemez; satırı bitirmek için biçimin sonuna \n yazarsınız.

İki kural printf komutunu çok kullanışlı yapar. Biçim, bütün argümanlar tüketilene kadar yeniden kullanılır; böylece tek bir biçimle bir listenin her elemanını yazdırabilirsiniz. Biçimin istediğinden az argüman verilirse, eksik belirteçler sıfır veya boş dizge verilmiş gibi davranır:

bash
printf '%s yaşında\n' 30
# çıktı: 30 yaşında
printf '%s\n' elma armut kiraz
# çıktı:
#   elma
#   armut
#   kiraz
printf '%s=%s\n' renk mavi boyut 42
# çıktı:
#   renk=mavi
#   boyut=42
printf '[%s-%d]\n' x
# çıktı: [x-0]

Sık kullanılan biçim belirteçleri

Sık kullanılan biçim belirteçleri: dizge, %d, %i, %x, %X, %o, %f, %e

printf, C dilindeki printf(3) belirteçlerini (cCsSndiouxXeEfFgGaA) destekler. Belirtecin önüne bayrak, genişlik ve duyarlık ekleyebilirsiniz: %-8s sola dayalı 8 genişlikte bir dizge, %5d sağa dayalı 5 genişlikte bir tamsayı, %05d başı sıfırla doldurulmuş bir tamsayı, %.2f virgülden sonra iki basamaklı bir ondalık sayı yazar.

bash
printf '|%-8s|%5d|\n' ad 42
# çıktı: |ad      |   42|
printf '%05d %x %X %o\n' 42 255 255 8
# çıktı: 00042 ff FF 10
printf '%.2f %e\n' 3.14159 12345.678
# çıktı: 3.14 1.234568e+04
printf '%+d %+d %.3s\n' 5 -5 abcdef
# çıktı: +5 -5 abc
printf '%%%d tamamlandı\n' 80
# çıktı: %80 tamamlandı
BelirteçNe yazarÖrnek çıktı
%sdizgeelma
%d, %iişaretli tamsayı42
%x, %X, %oonaltılık, büyük harfli onaltılık, sekizlikff, FF, 10
%f, %eondalık ve üstel gösterim3.14, 1.234568e+04
%cargümanın ilk karakteria
%bargümandaki kaçışları çözerek dizge (Bash)satır sonu üretir
%qkabuğa yeniden verilebilir biçimde tırnaklı dizge (Bash)dosya\ adı
%(biçim)Ttarih ve saat (Bash)1970-01-01
%%yüzde işareti%

Sayısal belirteçlere verilen argümanlar C dili sabitleri gibi yorumlanır; başta artı veya eksi işaretine izin verilir. Kılavuzdaki ilginç bir kurala göre argüman tek veya çift tırnakla başlıyorsa, değer tırnaktan sonraki karakterin sayısal kodudur. Böylece bir karakterin kodunu dış araç kullanmadan öğrenebilirsiniz. Sayı olmayan bir argüman ise hata verir; printf o belirteç için 0 yazar ve sıfırdan farklı bir durumla döner:

bash
printf '%d %d\n' "'A" "'a"
# çıktı: 65 97
printf '%d\n' 0x1F 010
# çıktı:
#   31
#   8
printf '%d\n' abc
# çıktı:
#   ornek.sh: line 7: printf: abc: invalid number
#   0
# $? = 1

İkinci satırda 0x1F onaltılık, 010 ise başındaki sıfır yüzünden sekizlik olarak okundu ve 8 oldu. Kullanıcıdan gelen "010" gibi değerleri sayıya çevirirken bu davranışı hesaba katın; Bash aritmetiğindeki taban kuralları aritmetik işlemler yazısında anlatılıyor.

Türkçe karakterler ve genişlik

Türkçe karakterler ve genişlik

%-8s gibi bir genişlik verdiğinizde dolgunun karakter mi bayt mı hesaplandığı önemlidir. Kılavuza göre %b, %q ve %T belirteçleri genişlik ve duyarlığı bayt olarak uygular. Aynı bölüm Bash 5.3 için %s ve %c belirteçlerine l (long) değiştiricisinin eklenebildiğini, %ls biçiminin genişliği bayt yerine karakter olarak hesapladığını anlatır. 5.2.37 ile denediğimizde hem %-6s hem %-6ls genişliği bayt olarak hesapladı; Türkçe harfler UTF-8'de iki bayt tuttuğu için sütun kaydı:

bash
printf '|%-6s|\n' ab ğü
# çıktı:
#   |ab    |
#   |ğü  |

İkinci satırda "ğü" dört bayt olduğundan yalnızca iki boşlukla dolduruldu. Türkçe metinleri hizalamanız gerekiyorsa dolguyu kendiniz hesaplayabilirsiniz: UTF-8 yerel ayarında ${#metin} karakter sayısını verir, eksik kısmı printf '%*s' "$eksik" '' ile boşlukla tamamlayabilirsiniz. Kendi sisteminizdeki Bash sürümü 5.3 ise %ls biçimini deneyebilirsiniz.

%b ile %s farkı

%s argümanı olduğu gibi yazar. %b ise argümandaki ters eğik çizgili kaçışları, echo -e ile aynı şekilde çözer. Kaçış dizileri biçim dizgesinin kendisinde her zaman çözülür; %b, bu işlemi bir argümanın, örneğin bir değişkenin değeri üzerinde yapmanızı sağlar:

bash
metin='bir\tiki\nüç'
printf '%s\n' "$metin"
# çıktı: bir\tiki\nüç
printf '%b\n' 'satır1\nsatır2'
# çıktı:
#   satır1
#   satır2

%q: kabuğa yeniden verilebilir çıktı

%q belirteci argümanı, kabuğa girdi olarak verildiğinde aynı değeri üretecek biçimde tırnaklar. Kılavuza göre argüman gerektiriyorsa ANSI-C tırnaklama ($'...'), gerekmiyorsa ters eğik çizgi kullanılır. Bu, boşluk veya özel karakter içeren bir değeri günlüğe yazarken, başka bir kabukta çalışacak bir komut satırı oluştururken ya da değerin içinde görünmeyen karakter olup olmadığını anlamaya çalışırken çok işe yarar:

bash
printf '%q\n' "dosya adı's" $'a\nb' 'düz'
# çıktı:
#   dosya\ adı\'s
#   $'a\nb'
#   düz
deger=$'satır sonu\r'
printf 'görünen: [%s]\n' "${deger%$'\r'}"
# çıktı: görünen: [satır sonu]
printf 'gerçek: %q\n' "$deger"
# çıktı: gerçek: $'satır sonu\r'

Son örnek, Windows'tan gelen bir dosyadan okunan değerlerin sonundaki görünmez satır başı karakterini %q ile nasıl fark edebileceğinizi gösteriyor. Bu karakterin yol açtığı hatalar sık hata mesajları yazısında ele alınıyor.

printf -v: çıktıyı değişkene yazmak

-v değişken seçeneği çıktıyı ekrana yazmak yerine verilen değişkene atar. Biçimlendirilmiş bir değeri bir değişkende saklamak için komut ikamesi ($(printf ...)) kullanmak yerine bu seçeneği tercih edebilirsiniz; ayrı bir alt kabuk açılmaz ve sondaki yeni satırlar silinmez:

bash
printf -v no '%03d' 7
echo "dosya_$no.log"
# çıktı: dosya_007.log
printf -v cizgi '%*s' 10 ''
echo "[${cizgi// /-}]"
# çıktı: [----------]

İkinci örnekte %*s genişliği argümandan alır: 10 karakterlik boşluk üretilir ve ardından parametre genişletmesiyle boşluklar tireye çevrilir. ${degisken// /-} biçimi string işlemleri yazısında anlatılıyor.

%(biçim)T: tarih ve saat

Bash'in printf komutu tarih ve saati harici bir program çağırmadan biçimlendirebilir. %(biçim)T belirtecindeki biçim, C kütüphanesindeki strftime biçimidir; argüman, Unix zamanının başlangıcından beri geçen saniye sayısıdır. Kılavuza göre -1 şimdiki zamanı, -2 kabuğun başlatıldığı zamanı temsil eder; argüman verilmezse şimdiki zaman kullanılır. Aşağıdaki örnek, sabit bir değerle sonucu görebilmeniz için 0 saniyeyi UTC saat diliminde biçimlendiriyor:

bash
TZ=UTC printf '%(%Y-%m-%d %H:%M)T\n' 0
# çıktı: 1970-01-01 00:00

Günlük satırlarının başına zaman damgası eklemek için printf '%(%F %T)T %s\n' -1 "mesaj" kalıbını kullanabilirsiniz. Bu durumda çıktı, çalıştırdığınız ana göre değişir.

Biçim dizgesindeki kaçış dizileri

Biçim dizgesindeki ters eğik çizgili kaçışlar printf tarafından her zaman çözülür; bu yüzden biçimi tek tırnakla yazmanız yeterlidir. \n, \t, \\ gibi yaygın kaçışların yanında onaltılık (\xHH), sekizlik ve Unicode (\uHHHH) kaçışlar da kullanılabilir. echo -e ile küçük bir fark vardır: kılavuz echo için sekizlik kaçışı \0nnn biçiminde, yani başında bir sıfırla tanımlar ve tanımadığı kaçışları olduğu gibi yazdığını belirtir:

bash
printf '\x41\101ü\n'
# çıktı: AAü
echo -e '\0101 \x41'
# çıktı: A A
echo -e '\q bilinmeyen kaçış'
# çıktı: \q bilinmeyen kaçış

Bir argümanın içindeki kaçışların çözülmesini istiyorsanız yukarıda anlatılan %b belirtecini kullanın; %s argümanı olduğu gibi bırakır. Bu ayrım, kullanıcıdan gelen bir metni yazdırırken önemlidir: metinde \c gibi bir dizi varsa %b çıktının geri kalanını keser, %s ise metni harfi harfine yazar.

Kılavuzun POSIX modu listesinde printf ile ilgili bir madde daha vardır: POSIX modunda ondalık sayı belirteçlerine karşılık gelen argümanlar, varsa long double yerine double türüyle dönüştürülür; L uzunluk değiştiricisi long double kullanımını zorlar. Günlük betiklerde bu fark nadiren görünür, ama çok büyük veya çok hassas ondalık sayıları biçimlendirirken sonuçlar modlar arasında ayrışabilir. printf komutunun çıkış durumu başarıda sıfırdır; geçersiz bir seçenek, bir yazma hatası veya bir atama hatası olduğunda sıfırdan farklıdır.

Biçim dizgesine veri koymayın

printf komutunun ilk argümanı bir biçim dizgesidir ve içindeki % karakterleri belirteç olarak yorumlanır. Kullanıcıdan veya bir dosyadan gelen değeri doğrudan biçim yerine koyarsanız, değer yüzde işareti içerdiği anda çıktı bozulur:

bash
veri='%s indirim %d'
printf "$veri\n"
# çıktı:  indirim 0
printf '%s\n' "$veri"
# çıktı: %s indirim %d

Birinci çağrıda %s boş dizgeye, %d sıfıra dönüştü ve çıktının başında bir boşluk kaldı. Doğru kullanım, biçimi sabit tutmak ve veriyi her zaman argüman olarak vermektir.

Uygulama: hizalı bir rapor

Biçimin yeniden kullanılması kuralı sayesinde, döngü yazmadan bir tablo üretebilirsiniz. Her satır için iki argüman tüketilir ve biçim baştan uygulanır. Başlık satırında da aynı genişlikleri kullanmak sütunları hizalı tutar:

bash
printf '%-8s %6s\n' Meyve Adet
printf '%-8s %6d\n' elma 12 armut 7 kiraz 130
# çıktı:
#   Meyve      Adet
#   elma         12
#   armut         7
#   kiraz       130

echo mu printf mi?

echo mu printf mi? Sabit, kısa bir mesaj, Değeri bilinmeyen bir değişkeni yazmak, Sonunda yeni satır istemiyorsanız, Kaçış dizileri (sekme, yeni satır)
DurumÖnerilen
Sabit, kısa bir mesajecho
Değeri bilinmeyen bir değişkeni yazmakprintf '%s\n'
Sonunda yeni satır istemiyorsanızprintf veya echo -n
Kaçış dizileri (sekme, yeni satır)printf
Hizalı sütunlar, sayı biçimiprintf
Sonucu bir değişkene yazmakprintf -v
Değeri kabuk için güvenli tırnaklamakprintf %q

Sık yapılan hatalar

  • printf sonrası yeni satırı unutmak: printf satırı kendiliğinden bitirmez; bir sonraki çıktı aynı satıra yazılır.
  • Değişkeni biçim dizgesi yapmak: printf "$veri" yerine printf '%s' "$veri" yazın.
  • echo ile seçenek benzeri değer yazmak: -n, -e gibi değerler echo tarafından seçenek sayılır.
  • echo -e sonucunu her yerde aynı sanmak: xpg_echo ve POSIX modu echo davranışını değiştirir.
  • Başında sıfır olan sayılar: printf '%d' 010 sekizlik okunur ve 8 yazar; 08 ve 09 ise "invalid octal number" hatası verir.
  • Genişliğin karakter sayıldığını varsaymak: 5.2.37'de %-10s dolguyu bayt olarak hesaplar ve Türkçe harfler sütunları kaydırır.

Sık Sorulan Sorular

echo ile printf arasındaki temel fark nedir?

echo argümanları boşlukla birleştirip yeni satır ekler ve seçenek yorumu kabuk ayarlarına bağlıdır. printf bir biçim dizgesine göre çalışır, yeni satırı yalnızca siz isterseniz ekler, sayıları biçimlendirir ve değerleri tutarlı biçimde yazar.

printf ile ondalık sayı hesaplanabilir mi?

printf ondalık sayıları biçimlendirebilir (%.2f), ama hesap yapmaz. Bash aritmetiği yalnızca tamsayılarla çalışır; ondalıklı hesap için dış bir araç gerekir. Sayıyı yalnızca yuvarlayıp göstermek için printf '%.1f' 2.345 yeterlidir.

printf ile bir sayının başına sıfır nasıl eklenir?

Genişlikten önce 0 bayrağı yazın: printf '%03d\n' 7 çıktısı 007 olur. Dosya adlarını sıralı numaralandırırken bu kalıbı printf -v ile birleştirip sonucu doğrudan bir değişkene alabilirsiniz.

printf neden biçimi birden fazla kez uyguluyor?

Kılavuza göre biçim, bütün argümanlar tüketilene kadar yeniden kullanılır. printf '%s\n' a b c üç satır yazar. Bu davranış istemediğiniz bir tekrar üretiyorsa, argümanları tek bir argümanda birleştirin ("$*") veya biçimdeki belirteç sayısını argüman sayısına eşitleyin.

printf çıktısı bir değişkene nasıl atanır?

printf -v hedef 'biçim' argümanlar yazın. Bu yöntem komut ikamesine göre alt kabuk açmaz ve çıktının sonundaki yeni satırları korur.

Bir değerde görünmeyen karakter olup olmadığını nasıl anlarım?

printf '%q\n' "$deger" ile yazdırın. Değer sekme, satır başı veya başka kontrol karakterleri içeriyorsa çıktı $'...' biçiminde gelir ve bu karakterler kaçış dizisi olarak görünür.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Bash öğrenme rehberi · Tırnak farkı · read ile girdi · String işlemleri · stdout ve stderr