Pada dasarnya, ini 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 "\n\t"
menampilkan karakter baris baru atau tab, atau memiliki opsi untuk tidak menampilkan karakter baris baru yang tertinggal.
Mereka kemudian berpikir lebih keras tetapi alih-alih menambahkan fungsionalitas itu ke shell (seperti di perl
mana di dalam tanda kutip ganda, \t
sebenarnya berarti karakter tab), mereka menambahkannya echo
.
David Korn menyadari kesalahan itu dan memperkenalkan bentuk baru dari kutipan shell: $'...'
yang kemudian disalin oleh bash
dan zsh
tapi itu sudah sangat terlambat pada saat itu.
Sekarang ketika UNIX standar echo
menerima argumen yang berisi dua karakter \
dan t
, bukannya menghasilkan mereka, itu menghasilkan karakter tab. Dan begitu ia melihat \c
dalam argumen, ia berhenti mengeluarkan (jadi baris baru yang tertinggal juga bukan keluaran).
Kerang / vendor Unix lain / versi memilih untuk melakukannya secara berbeda: mereka menambahkan -e
opsi untuk memperluas urutan melarikan diri, dan -n
opsi untuk tidak menampilkan baris baru. Beberapa memiliki -E
untuk menonaktifkan urutan melarikan diri, beberapa memiliki -n
tetapi tidak -e
, daftar urutan melarikan diri yang didukung oleh satu echo
implementasi tidak harus sama dengan yang didukung oleh yang lain.
Sven Mascheck memiliki halaman yang bagus yang menunjukkan tingkat masalah .
Pada echo
implementasi yang mendukung opsi, umumnya tidak ada dukungan dari --
untuk menandai akhir dari opsi ( echo
builtin dari beberapa shell yang tidak seperti Bourne lakukan, dan zsh mendukung -
untuk itu), jadi misalnya, sulit untuk menghasilkan "-n"
dengan echo
di banyak kerang.
Pada beberapa shell seperti bash
¹ atau ksh93
² atau yash
( $ECHO_STYLE
variabel), perilaku bahkan tergantung pada bagaimana shell dikompilasi atau lingkungan ( echo
Perilaku GNU juga akan berubah jika $POSIXLY_CORRECT
ada di lingkungan dan dengan versi 4 , zsh
dengan bsd_echo
pilihannya, beberapa berbasis pdksh dengan posix
opsi mereka atau apakah mereka dipanggil sh
atau tidak). Jadi dua bash
echo
s, bahkan dari versi yang sama bash
tidak dijamin berperilaku sama.
POSIX mengatakan: jika argumen pertama adalah -n
atau argumen apa pun mengandung garis miring terbalik, maka perilaku tersebut tidak ditentukan . bash
gema dalam hal itu bukanlah POSIX dalam hal itu misalnya echo -e
tidak menghasilkan -e<newline>
seperti yang diperlukan POSIX. Spesifikasi UNIX lebih ketat, melarang -n
dan membutuhkan perluasan beberapa urutan pelarian termasuk yang \c
berhenti mengeluarkan.
Spesifikasi itu tidak benar-benar datang ke penyelamatan di sini mengingat bahwa banyak implementasi tidak sesuai. Bahkan beberapa sistem bersertifikat seperti macOS 5 tidak sesuai.
Untuk benar-benar mewakili kenyataan saat ini, POSIX harus benar-benar mengatakan : jika argumen pertama cocok dengan ^-([eEn]*|-help|-version)$
regexp yang diperluas 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 adalah tidak ditentukan.
Secara keseluruhan, Anda tidak tahu apa yang echo "$var"
akan dihasilkan kecuali Anda dapat memastikan bahwa $var
itu tidak mengandung karakter backslash dan tidak dimulai dengan -
. Spesifikasi POSIX sebenarnya memberitahu kita untuk menggunakannya printf
sebagai gantinya.
Jadi itu artinya Anda tidak dapat menggunakan echo
untuk menampilkan data yang tidak terkontrol. Dengan kata lain, jika Anda menulis skrip dan sedang mengambil input eksternal (dari pengguna sebagai argumen, atau nama file dari sistem file ...), Anda tidak dapat menggunakannya echo
untuk menampilkannya.
Ini bagus:
echo >&2 Invalid file.
Ini bukan:
echo >&2 "Invalid file: $file"
(Meskipun itu akan bekerja OK dengan beberapa echo
implementasi (non-UNIX compliant) seperti bash
ketika xpg_echo
opsi tidak diaktifkan dengan satu atau lain cara seperti pada waktu kompilasi atau melalui lingkungan).
file=$(echo "$var" | tr ' ' _)
tidak OK di sebagian besar implementasi (pengecualian menjadi yash
dengan ECHO_STYLE=raw
(dengan peringatan bahwa yash
's variabel tidak bisa menahan urutan sewenang-wenang dari byte sehingga tidak nama file sewenang-wenang) dan zsh
' s echo -E - "$var"
6 ).
printf
, di sisi lain lebih dapat diandalkan, setidaknya ketika itu terbatas pada penggunaan dasar echo
.
printf '%s\n' "$var"
Akan menampilkan konten $var
diikuti oleh karakter baris baru terlepas dari karakter apa yang dikandungnya.
printf '%s' "$var"
Akan menampilkannya tanpa karakter baris baru.
Sekarang, ada juga perbedaan antara printf
implementasi. Ada inti fitur yang ditentukan oleh POSIX, tetapi kemudian ada banyak ekstensi. Sebagai contoh, beberapa dukungan %q
untuk mengutip argumen tetapi bagaimana itu dilakukan bervariasi dari shell ke shell, beberapa dukungan \uxxxx
untuk karakter unicode. Perilaku bervariasi untuk printf '%10s\n' "$var"
di multi-byte locales, setidaknya ada tiga hasil yang berbeda untukprintf %b '\123'
Tetapi pada akhirnya, jika Anda tetap menggunakan set fitur POSIX printf
dan tidak mencoba melakukan sesuatu yang terlalu mewah, Anda keluar dari masalah.
Tapi ingat argumen pertama adalah format, jadi tidak boleh berisi data variabel / tidak terkendali.
Yang lebih andal echo
dapat diimplementasikan menggunakan printf
, seperti:
echo() ( # subshell for local scope for $IFS
IFS=" " # needed for "$*"
printf '%s\n' "$*"
)
echo_n() (
IFS=" "
printf %s "$*"
)
echo_e() (
IFS=" "
printf '%b\n' "$*"
)
Subkulit (yang menyiratkan pemijahan proses ekstra di sebagian besar implementasi shell) dapat dihindari menggunakan local IFS
dengan banyak shell, atau dengan menuliskannya seperti:
echo() {
if [ "$#" -gt 0 ]; then
printf %s "$1"
shift
fi
if [ "$#" -gt 0 ]; then
printf ' %s' "$@"
fi
printf '\n'
}
Catatan
1. bagaimana bash
's echo
perilaku bisa diubah.
Dengan bash
, pada saat dijalankan, ada dua hal yang mengontrol perilaku echo
(di samping enable -n echo
atau mendefinisikan kembali echo
sebagai fungsi atau alias): xpg_echo
bash
opsi dan apakah bash
dalam mode posix. posix
mode dapat diaktifkan jika bash
dipanggil sebagai sh
atau jika POSIXLY_CORRECT
ada di lingkungan atau dengan posix
opsi:
Perilaku default pada kebanyakan sistem:
$ bash -c 'echo -n "\0101"'
\0101% # the % here denotes the absence of newline character
xpg_echo
perluas urutan sesuai kebutuhan UNIX:
$ BASHOPTS=xpg_echo bash -c 'echo "\0101"'
A
Itu masih menghormati -n
dan -e
(dan -E
):
$ BASHOPTS=xpg_echo bash -c 'echo -n "\0101"'
A%
Dengan xpg_echo
dan mode POSIX:
$ env BASHOPTS=xpg_echo POSIXLY_CORRECT=1 bash -c 'echo -n "\0101"'
-n A
$ env BASHOPTS=xpg_echo sh -c 'echo -n "\0101"' # (where sh is a symlink to bash)
-n A
$ env BASHOPTS=xpg_echo SHELLOPTS=posix bash -c 'echo -n "\0101"'
-n A
Kali ini, bash
adalah POSIX dan UNIX yang sesuai. Perhatikan bahwa dalam mode POSIX, bash
masih belum memenuhi POSIX karena tidak menghasilkan -e
:
$ env SHELLOPTS=posix bash -c 'echo -e'
$
Nilai default untuk xpg_echo dan posix dapat didefinisikan pada waktu kompilasi dengan opsi --enable-xpg-echo-default
dan --enable-strict-posix-default
ke configure
skrip. Biasanya itulah yang dilakukan versi OS / X terbaru untuk membuatnya /bin/sh
. Tidak ada implementasi / distribusi Unix / Linux yang waras biasanya akan melakukannya untuk /bin/bash
saat ini . Sebenarnya, itu tidak benar, /bin/bash
bahwa Oracle dikirimkan bersama Solaris 11 (dalam paket opsional) tampaknya dibangun dengan --enable-xpg-echo-default
(itu tidak terjadi di Solaris 10).
2. Bagaimana ksh93
's echo
perilaku bisa diubah.
Dalam ksh93
, apakah echo
memperluas urutan lolos atau tidak dan mengenali opsi tergantung pada konten variabel $PATH
dan / atau $_AST_FEATURES
lingkungan.
Jika $PATH
berisi komponen yang mengandung /5bin
atau /xpg
sebelum /bin
atau /usr/bin
komponen maka itu berperilaku cara SysV / UNIX (memperluas urutan, tidak menerima opsi). Jika ia menemukan /ucb
atau /bsd
pertama atau jika $_AST_FEATURES
7 berisi UNIVERSE = ucb
, maka ia berperilaku dengan cara BSD 3 ( -e
untuk memungkinkan ekspansi, mengenali -n
).
Standarnya tergantung pada sistem, BSD pada Debian (lihat output builtin getconf; getconf UNIVERSE
dalam versi terbaru dari ksh93):
$ ksh93 -c 'echo -n' # default -> BSD (on Debian)
$ PATH=/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /xpg before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH ksh93 -c 'echo -n' # /5bin before /bin or /usr/bin -> XPG
-n
$ PATH=/5binary:$PATH _AST_FEATURES='UNIVERSE = ucb' ksh93 -c 'echo -n' # -> BSD
$ PATH=/ucb:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /ucb first -> BSD
$ PATH=/bin:/foo/xpgbar:$PATH ksh93 -c 'echo -n' # /bin before /xpg -> default -> BSD
3. BSD untuk echo -e?
Referensi ke BSD untuk penanganan -e
opsi agak menyesatkan di sini. Sebagian besar echo
perilaku yang berbeda dan tidak kompatibel itu semua diperkenalkan di AT&T:
\n
, \0ooo
, \c
Di Programmer Kerja Bench UNIX (berdasarkan Unix V6), dan sisanya ( \b
, \r
...) di Unix System III Ref .
-n
dalam Unix V7 (oleh Dennis Ritchie Ref )
-e
dalam Unix V8 (oleh Dennis Ritchie Ref )
-E
sendiri mungkin awalnya berasal dari bash
(CWRU / CWRU.chlog dalam versi 1.13.5 menyebutkan Brian Fox menambahkannya pada 1992-10-18, GNU echo
menyalinnya segera setelah di sh-utils-1.8 dirilis 10 hari kemudian)
Sementara echo
builtin dari sh
atau BSD telah mendukung -e
sejak hari mereka mulai menggunakan shell Almquist untuk itu di awal 90-an, echo
utilitas mandiri hingga hari ini tidak mendukungnya di sana ( FreeBSDecho
masih tidak mendukung -e
, meskipun ia mendukung -n
seperti Unix V7 (dan juga \c
tetapi hanya pada akhir argumen terakhir)).
Penanganan -e
ditambahkan ke ksh93
's echo
ketika di BSD alam semesta dalam versi ksh93r dirilis pada tahun 2006 dan dapat dinonaktifkan pada saat kompilasi.
4. GNU gema perubahan perilaku di 8.31
Sejak coreutils 8.31 (dan ini komit ), GNU echo
sekarang memperluas escape sequence secara default ketika POSIXLY_CORRECT adalah di lingkungan, untuk mencocokkan perilaku bash -o posix -O xpg_echo
's echo
builtin (lihat laporan bug ).
5. macOS echo
Sebagian besar versi macOS telah menerima sertifikasi UNIX dari OpenGroup .
sh
Builtin mereka echo
kompatibel karena bash
(versi yang sangat lama) dibangun dengan xpg_echo
diaktifkan secara default, tetapi echo
utilitas yang berdiri sendiri tidak. env echo -n
tidak menghasilkan apa-apa -n<newline>
, env echo '\n'
output \n<newline>
bukan <newline><newline>
.
Itu /bin/echo
adalah salah satu dari FreeBSD yang menekan output baris baru jika argumen pertama adalah -n
atau (sejak 1995) jika argumen terakhir berakhir \c
, tetapi tidak mendukung urutan backslash lainnya yang diperlukan oleh UNIX, bahkan tidak \\
.
6. echo
implementasi yang dapat menghasilkan data sembarang kata demi kata
Sebenarnya, Anda juga dapat menghitung bahwa FreeBSD / macOS di /bin/echo
atas (bukan bawaan shell mereka echo
) di mana zsh
's echo -E - "$var"
atau yash
' ECHO_STYLE=raw echo "$var"
( printf '%s\n' "$var"
) dapat ditulis:
/bin/echo "$var
\c"
Implementasi yang mendukung -E
dan -n
(atau dapat dikonfigurasi untuk) juga dapat melakukan:
echo -nE "$var
"
Dan zsh
's echo -nE - "$var"
( printf %s "$var"
) dapat ditulis
/bin/echo "$var\c"
7. _AST_FEATURES
dan ASTUNIVERSE
Itu _AST_FEATURES
tidak dimaksudkan untuk dimanipulasi secara langsung, itu digunakan untuk menyebarkan pengaturan konfigurasi AST di seluruh eksekusi perintah. Konfigurasi ini dimaksudkan untuk dilakukan melalui astgetconf()
API (tidak berdokumen) . Di dalam ksh93
, getconf
builtin (diaktifkan dengan builtin getconf
atau dengan memanggil command /opt/ast/bin/getconf
) adalah antarmuka untukastgetconf()
Misalnya, Anda harus builtin getconf; getconf UNIVERSE = att
mengubah UNIVERSE
pengaturan ke att
(menyebabkan echo
berperilaku SysV antara lain). Setelah melakukan itu, Anda akan melihat $_AST_FEATURES
variabel lingkungan berisi UNIVERSE = att
.
echo -e
?