Saya telah mendengar bahwa printf
lebih baik dari echo
. Saya hanya dapat mengingat satu contoh dari pengalaman saya di mana saya harus menggunakan printf
karena echo
tidak berfungsi untuk memasukkan beberapa teks ke dalam beberapa program di RHEL 5.8 tetapi printf
telah melakukan. Tapi ternyata, ada perbedaan lain, dan saya ingin menanyakan apa perbedaannya serta apakah ada kasus tertentu saat menggunakan satu vs yang lain.
Jawaban yang Diterima:
Pada dasarnya, ini adalah masalah portabilitas (dan keandalan).
Awalnya, echo
tidak menerima opsi apa pun dan tidak memperluas apa pun. Yang dilakukannya hanyalah mengeluarkan argumennya yang dipisahkan oleh karakter spasi dan diakhiri oleh karakter baris baru.
Sekarang, seseorang berpikir akan lebih baik jika kita bisa melakukan hal-hal seperti echo "nt"
untuk menampilkan karakter baris atau tab baru, atau memiliki opsi untuk tidak menampilkan karakter baris baru tambahan.
Mereka kemudian berpikir lebih keras tetapi alih-alih menambahkan fungsionalitas itu ke shell (seperti perl
di mana di dalam tanda kutip ganda, t
sebenarnya berarti karakter tab), mereka menambahkannya ke echo
.
David Korn menyadari kesalahan tersebut dan memperkenalkan bentuk baru dari kutipan shell:$'...'
yang kemudian disalin oleh bash
dan zsh
tapi saat itu sudah sangat terlambat.
Sekarang ketika echo
UNIX standar menerima argumen yang berisi dua karakter dan
t
, alih-alih mengeluarkannya, itu menghasilkan karakter tab. Dan segera setelah ia melihat c
dalam sebuah argumen, ia berhenti mengeluarkan (jadi baris baru yang tertinggal juga tidak keluar).
Shell/vendor/versi Unix lainnya memilih untuk melakukannya secara berbeda:mereka menambahkan -e
opsi untuk memperluas urutan pelarian, dan -n
opsi untuk tidak menampilkan baris baru yang tertinggal. Beberapa memiliki -E
untuk menonaktifkan urutan pelarian, beberapa memiliki -n
tapi bukan -e
, daftar urutan pelarian yang didukung oleh satu echo
implementasi belum tentu sama dengan yang didukung oleh yang lain.
Sven Mascheck memiliki halaman bagus yang menunjukkan sejauh mana masalahnya.
Pada echo
itu implementasi yang mendukung opsi, umumnya tidak ada dukungan --
untuk menandai akhir opsi (echo
bawaan dari beberapa shell non-Bourne, dan zsh mendukung -
untuk itu), jadi misalnya, sulit untuk menampilkan "-n"
dengan echo
dalam banyak cangkang.
Pada beberapa shell seperti bash
atau ksh93
² atau yash
($ECHO_STYLE
variabel), perilaku bahkan tergantung pada bagaimana shell dikompilasi atau lingkungan (GNU echo
perilaku juga akan berubah jika $POSIXLY_CORRECT
ada di lingkungan dan dengan versi, zsh
dengan bsd_echo
-nya opsi, beberapa berbasis pdksh dengan posix
their opsi atau apakah mereka disebut sebagai sh
atau tidak). Jadi dua bash
echo
s, bahkan dari versi bash
yang sama tidak dijamin akan berperilaku sama.
POSIX mengatakan:jika argumen pertama adalah -n
atau argumen apa pun berisi garis miring terbalik, maka perilakunya tidak ditentukan . bash
echo dalam hal itu bukan POSIX dalam hal itu misalnya echo -e
tidak mengeluarkan -e<newline>
seperti yang dibutuhkan POSIX. Spesifikasi UNIX lebih ketat, melarang -n
dan membutuhkan perluasan beberapa urutan pelarian termasuk c
satu untuk berhenti mengeluarkan.
Spesifikasi tersebut tidak benar-benar datang untuk menyelamatkan di sini karena banyak implementasi yang tidak sesuai. Bahkan beberapa bersertifikat sistem seperti macOS tidak sesuai.
Untuk benar-benar mewakili kenyataan saat ini, POSIX harus benar-benar mengatakan:jika argumen pertama cocok dengan ^-([eEn]*|-help|-version)$
diperpanjang regexp atau argumen apa pun berisi garis miring terbalik (atau karakter yang pengkodeannya berisi pengkodean karakter garis miring terbalik seperti α
di lokal menggunakan charset BIG5), maka perilakunya tidak ditentukan.
Secara keseluruhan, Anda tidak tahu apa echo "$var"
akan ditampilkan kecuali Anda dapat memastikan bahwa $var
tidak mengandung karakter garis miring terbalik dan tidak dimulai dengan -
. Spesifikasi POSIX sebenarnya memberitahu kita untuk menggunakan printf
sebagai gantinya.
Jadi maksudnya tidak bisa menggunakan echo
untuk menampilkan data yang tidak terkontrol. Dengan kata lain, jika Anda sedang menulis skrip dan mengambil input eksternal (dari pengguna sebagai argumen, atau nama file dari sistem file...), Anda tidak dapat menggunakan echo
untuk menampilkannya.
Tidak apa-apa:
echo >&2 Invalid file.
Ini bukan:
echo >&2 "Invalid file: $file"
(Meskipun itu akan berfungsi dengan baik dengan beberapa (tidak sesuai dengan UNIX) echo
implementasi seperti bash
saat xpg_echo
opsi belum diaktifkan dengan satu atau lain cara seperti pada waktu kompilasi atau melalui lingkungan).
file=$(echo "$var" | tr ' ' _)
tidak OK di sebagian besar implementasi (pengecualian adalah yash
dengan ECHO_STYLE=raw
(dengan peringatan bahwa yash
Variabel 's tidak dapat menampung urutan byte yang sewenang-wenang, jadi bukan nama file yang sewenang-wenang) dan zsh
's echo -E - "$var"
).
printf
, di sisi lain lebih dapat diandalkan, setidaknya jika terbatas pada penggunaan dasar echo
.
printf '%sn' "$var"
Akan menampilkan konten $var
diikuti oleh karakter baris baru terlepas dari karakter apa yang mungkin ada di dalamnya.
printf '%s' "$var"
Akan menampilkannya tanpa karakter baris baru tambahan.
Sekarang, ada juga perbedaan antara printf
implementasi. Ada inti fitur yang ditentukan oleh POSIX, tetapi kemudian ada banyak ekstensi. Misalnya, beberapa mendukung %q
mengutip argumen tetapi cara melakukannya bervariasi dari satu shell ke shell lainnya, beberapa mendukung uxxxx
untuk karakter unicode. Perilaku bervariasi untuk printf '%10sn' "$var"
di lokal multi-byte, setidaknya ada tiga hasil berbeda untuk printf %b '123'
Tetapi pada akhirnya, jika Anda tetap menggunakan set fitur POSIX dari printf
dan jangan mencoba melakukan sesuatu yang terlalu mewah dengannya, Anda keluar dari masalah.
Tapi ingat argumen pertama adalah formatnya, jadi tidak boleh berisi data variabel/tidak terkontrol.
echo
yang lebih andal dapat diimplementasikan menggunakan printf
, seperti:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%sn' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%bn' "$*"
)
Subkulit (yang menyiratkan pemijahan proses tambahan di sebagian besar implementasi shell) dapat dihindari menggunakan local IFS
dengan banyak cangkang, atau dengan menuliskannya seperti:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
if [ "$#" -gt 0 ]; then
printf ' %s' "[email protected]"
fi
fi
printf 'n'
}
Catatan
1. bagaimana bash
echo
perilaku dapat diubah.
Dengan bash
, pada saat dijalankan, ada dua hal yang mengontrol perilaku echo
(di samping enable -n echo
atau mendefinisikan ulang echo
sebagai fungsi atau alias):
xpg_echo
bash
opsi dan apakah bash
dalam mode posix. posix
mode dapat diaktifkan jika bash
disebut sebagai sh
atau jika POSIXLY_CORRECT
ada di lingkungan atau dengan posix
pilihan:
Perilaku default pada sebagian besar sistem:
$ bash -c 'echo -n "
Implikasi Keamanan dari Lupa Mengutip Variabel Dalam Bash/posix Shells?
Mengapa *tidak* Mengurai `ls` (dan Apa yang Harus Dilakukan)?
Linux