Ketika Anda sampai di leher Anda dalam buaya, mudah untuk melupakan bahwa tujuannya adalah untuk mengeringkan rawa.
- Pepatah populer
Pertanyaannya adalah tentang echo
, namun sebagian besar jawaban sejauh ini berfokus pada bagaimana menyelinap set +x
perintah. Ada solusi yang lebih sederhana, lebih langsung:
{ echo "Message"; } 2> /dev/null
(Saya mengakui bahwa saya mungkin tidak memikirkan { …; } 2> /dev/null
jika saya tidak melihatnya dalam jawaban sebelumnya.)
Ini agak rumit, tetapi, jika Anda memiliki blok echo
perintah berturut-turut , Anda tidak perlu melakukannya pada masing-masing secara individual:
{
echo "The quick brown fox"
echo "jumps over the lazy dog."
} 2> /dev/null
Perhatikan bahwa Anda tidak perlu titik koma saat memiliki baris baru.
Anda dapat mengurangi beban pengetikan dengan menggunakan ide kenorb untuk
membuka /dev/null
secara permanen pada deskriptor file non-standar (misalnya, 3) dan kemudian mengatakan 2>&3
alih-alih 2> /dev/null
sepanjang waktu.
Empat jawaban pertama pada saat penulisan ini memerlukan melakukan sesuatu yang istimewa (dan, dalam kebanyakan kasus, rumit)
setiap kali Anda melakukan suatu echo
. Jika Anda benar-benar ingin semua echo
perintah untuk menekan jejak eksekusi (dan mengapa Anda tidak melakukannya?), Anda dapat melakukannya secara global, tanpa mengeluarkan banyak kode. Pertama, saya perhatikan bahwa alias tidak dilacak:
$ myfunc()
> {
> date
> }
$ alias myalias="date"
$ set -x
$ date
+ date
Mon, Oct 31, 2016 0:00:00 AM # Happy Halloween!
$ myfunc
+ myfunc # Note that function call is traced.
+ date
Mon, Oct 31, 2016 0:00:01 AM
$ myalias
+ date # Note that it doesn’t say + myalias
Mon, Oct 31, 2016 0:00:02 AM
(Perhatikan bahwa cuplikan skrip berikut berfungsi jika shebang #!/bin/sh
, bahkan jika /bin/sh
tautan ke bash. Tetapi, jika shebang adalah #!/bin/bash
, Anda perlu menambahkan shopt -s expand_aliases
perintah untuk membuat alias agar bekerja dalam skrip.)
Jadi, untuk trik pertama saya:
alias echo='{ set +x; } 2> /dev/null; builtin echo'
Sekarang, ketika kita mengatakan echo "Message"
, kita memanggil alias, yang tidak bisa dilacak. Alias mematikan opsi jejak, sementara menekan pesan jejak dari set
perintah (menggunakan teknik yang disajikan pertama dalam jawaban user5071535 ), dan kemudian menjalankan echo
perintah yang sebenarnya . Ini memungkinkan kita mendapatkan efek yang mirip dengan jawaban user5071535 tanpa perlu mengedit kode di setiap echo
perintah. Namun, mode jejak daun ini dimatikan. Kami tidak dapat memasukkan set -x
alias ke (atau setidaknya tidak mudah) karena alias hanya memungkinkan string untuk diganti dengan kata; tidak ada bagian dari string alias yang dapat disuntikkan ke perintah
setelah argumen (misalnya, "Message"
). Jadi, misalnya, jika skripnya berisi
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
date
hasilnya akan
+ date
Mon, Oct 31, 2016 0:00:03 AM
The quick brown fox
jumps over the lazy dog.
Mon, Oct 31, 2016 0:00:04 AM # Note that it doesn’t say + date
jadi Anda masih perlu mengaktifkan kembali opsi jejak setelah menampilkan pesan - tetapi hanya sekali setelah setiap blokecho
perintah berturut-turut :
date
echo "The quick brown fox"
echo "jumps over the lazy dog."
set -x
date
Akan lebih baik jika kita bisa membuat set -x
otomatis setelah echo
- dan kita bisa, dengan sedikit tipu daya. Tetapi sebelum saya sajikan itu, pertimbangkan ini. OP dimulai dengan skrip yang menggunakan #!/bin/sh -ex
shebang. Secara implisit pengguna dapat menghapus x
dari shebang dan memiliki skrip yang bekerja secara normal, tanpa penelusuran jejak. Alangkah baiknya jika kita bisa mengembangkan solusi yang mempertahankan properti itu. Beberapa jawaban pertama di sini gagal properti itu karena mereka melacak "kembali" pada setelah echo
pernyataan, tanpa syarat, tanpa memperhatikan apakah itu sudah aktif.
Jawaban ini jelas gagal mengenali masalah itu, karena ia menggantikan echo
output dengan jejak output; oleh karena itu, semua pesan hilang jika penelusuran dimatikan. Saya sekarang akan menyajikan solusi yang ternyata melacak kembali setelah echo
pernyataan bersyarat - hanya jika sudah aktif. Menurunkan versi ini menjadi solusi yang mengubah penelusuran "kembali" tanpa syarat adalah sepele dan dibiarkan sebagai latihan.
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
echo_and_restore() {
builtin echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
$-
adalah daftar opsi; gabungan huruf yang sesuai dengan semua opsi yang ditetapkan. Misalnya, jika e
dan x
opsi diatur, maka $-
akan ada serakan huruf yang mencakup e
dan x
. Alias baru saya (di atas) menyimpan nilai $-
sebelum mematikan penelusuran. Kemudian, dengan pelacakan dimatikan, itu melemparkan kontrol ke fungsi shell. Fungsi itu melakukan yang sebenarnya echo
dan kemudian memeriksa untuk melihat apakah x
opsi dihidupkan ketika alias dipanggil. Jika opsi diaktifkan, fungsi akan mengaktifkannya kembali; jika tidak aktif, fungsi akan mati.
Anda dapat memasukkan tujuh baris di atas (delapan, jika Anda menyertakan a shopt
) di awal skrip dan biarkan sisanya sendiri.
Ini akan memungkinkan Anda
- untuk menggunakan salah satu dari garis shebang berikut:
#! / bin / sh -ex
#! / bin / sh -e
#! / bin / sh –x
atau sekadar polos#! / bin / sh
dan itu harus bekerja seperti yang diharapkan.
- untuk memiliki kode seperti
(shebang)
perintah 1
perintah 2
perintah 3
set -x
perintah 4
perintah 5
perintah 6
set + x
perintah 7
perintah 8
perintah 9
dan
- Perintah 4, 5, dan 6 akan dilacak - kecuali salah satunya adalah
echo
, dalam hal ini akan dieksekusi tetapi tidak dilacak. (Tetapi meskipun perintah 5 adalah echo
, perintah 6 masih akan dilacak.)
- Perintah 7, 8, dan 9 tidak akan dilacak. Bahkan jika perintah 8 adalah
echo
, perintah 9 masih tidak akan dilacak.
- Perintah 1, 2, dan 3 akan dilacak (seperti 4, 5, dan 6) atau tidak (seperti 7, 8, dan 9) tergantung pada apakah shebang termasuk
x
.
PS Saya telah menemukan bahwa, di sistem saya, saya dapat meninggalkan builtin
kata kunci di jawaban tengah saya (yang hanya alias untuk echo
). Ini tidak mengejutkan; bash (1) mengatakan bahwa, selama ekspansi alias, ...
... kata yang identik dengan alias yang diperluas tidak diperluas untuk kedua kalinya. Ini berarti bahwa seseorang dapat alias ls
untuk ls -F
, misalnya, dan bash tidak mencoba memperluas teks pengganti secara rekursif.
Tidak terlalu mengejutkan, jawaban terakhir (yang dengan echo_and_restore
) gagal jika builtin
kata kunci dihilangkan 1 . Tapi, anehnya itu berfungsi jika saya menghapus builtin
dan mengganti urutan:
echo_and_restore() {
echo "$*"
case "$save_flags" in
(*x*) set -x
esac
}
alias echo='{ save_flags="$-"; set +x;} 2> /dev/null; echo_and_restore'
__________
1 Tampaknya menimbulkan perilaku yang tidak terdefinisi. Saya telah melihat
- loop tak terbatas (mungkin karena rekursi tak terbatas),
- sebuah
/dev/null: Bad address
pesan kesalahan, dan
- dump inti.
echo +x; echo "$*"; echo -x
dalam alias, saya ingin melihatnya.Saya menemukan solusi parsial di InformIT :
output
Sayangnya, itu masih menggema
set +x
, tapi setidaknya itu sepi setelah itu. jadi setidaknya solusi parsial untuk masalah ini.Tetapi apakah mungkin ada cara yang lebih baik untuk melakukan ini? :)
sumber
Cara ini meningkatkan solusi Anda sendiri dengan menyingkirkan
set +x
output:sumber
Taruh
set +x
di dalam tanda kurung, sehingga hanya berlaku untuk lingkup lokal.Sebagai contoh:
akan menghasilkan:
sumber
set +x
dalam subkulit (atau menggunakan subkulit sama sekali) melakukan sesuatu yang bermanfaat. Anda dapat menghapusnya dan mendapatkan hasil yang sama. Ini adalah pengarah stderr ke / dev / null yang melakukan pekerjaan sementara "menonaktifkan" pelacakan ... Sepertinyaecho foo1 2>/dev/null
, dll, akan sama efektifnya, dan lebih mudah dibaca.bash -x
) dan Anda sudah mengarahkan&2
ke nol (karena&3
dialihkan ke nol), jadi saya tidak yakin bagaimana komentar itu relevan. Mungkin kita hanya perlu contoh yang lebih baik yang mengilustrasikan poin Anda, tetapi dalam contoh yang diberikan setidaknya, sepertinya masih bisa disederhanakan tanpa kehilangan manfaat apa pun.Saya suka jawaban yang komprehensif dan dijelaskan dengan baik oleh g-man , dan menganggapnya sebagai jawaban terbaik yang disediakan sejauh ini. Itu peduli dengan konteks skrip, dan tidak memaksa konfigurasi ketika mereka tidak diperlukan. Jadi, jika Anda membaca jawaban ini pertama-tama silakan periksa yang itu, semua manfaat ada di sana.
Namun, dalam jawaban itu ada bagian penting yang hilang: metode yang diusulkan tidak akan berfungsi untuk kasus penggunaan biasa, yaitu melaporkan kesalahan:
Karena bagaimana alias dikonstruksi, ini akan berkembang menjadi
dan Anda dapat menebaknya,
echo_and_restore
dieksekusi selalu , tanpa syarat. Mengingat bahwaset +x
bagian tersebut tidak berjalan, itu berarti bahwa isi dari fungsi itu akan dicetak juga.Mengubah yang terakhir
;
menjadi&&
tidak akan berhasil, karena di Bash,||
dan&&
bersifat asosiatif kiri .Saya menemukan modifikasi yang berfungsi untuk kasus penggunaan ini:
Ia menggunakan subshell (
(...)
bagian) untuk mengelompokkan semua perintah, dan kemudian meneruskan string input melalui stdin sebagai Here String (<<<
benda) yang kemudian dicetak olehcat -
. Ini-
opsional, tetapi Anda tahu, " eksplisit lebih baik daripada implisit ".Anda juga dapat menggunakan
cat -
secara langsung tanpa echo , tapi saya suka cara ini karena memungkinkan saya untuk menambahkan string lain ke output. Sebagai contoh, saya cenderung menggunakannya seperti ini:Dan sekarang ia bekerja dengan indah:
sumber
Jejak eksekusi pergi ke
stderr
, filter dengan cara ini:Beberapa penjelasan, langkah demi langkah:
stderr
diarahkan ... -2>
>(…)
grep
adalah perintahnya ...^
+ echo
...grep
balikkan pertandingan ... --v
stdout
; kami mengarahkannya kestderr
tempat yang seharusnya. ->&2
Masalahnya adalah (saya kira) solusi ini dapat melakukan sinkronisasi sinkronisasi aliran. Karena penyaringan
stderr
mungkin sedikit terlambat dalam kaitannya denganstdout
(di manaecho
output milik default). Untuk memperbaikinya, Anda dapat bergabung dengan stream terlebih dahulu jika Anda tidak keberatan memiliki keduanya distdout
:Anda dapat membangun pemfilteran seperti itu ke dalam skrip itu sendiri tetapi pendekatan ini rentan terhadap desinkronisasi untuk memastikan (mis. Itu telah terjadi dalam pengujian saya: jejak eksekusi perintah mungkin muncul setelah output segera mengikuti
echo
).Kode terlihat seperti ini:
Jalankan tanpa trik:
Sekali lagi, gunakan
> >(grep -v "^+ echo ") 2>&1
untuk mempertahankan sinkronisasi dengan biaya bergabung dengan stream.Pendekatan lain. Anda mendapatkan "sedikit berlebihan" dan output yang tampak aneh karena terminal Anda bercampur
stdout
danstderr
. Kedua aliran ini adalah binatang yang berbeda karena suatu alasan. Periksa apakah analisisstderr
hanya sesuai dengan kebutuhan Anda; buangstdout
:Jika Anda memiliki skrip
echo
debug pesan pencetakan / kesalahan untukstderr
maka Anda dapat menghilangkan redundansi dengan cara yang dijelaskan di atas. Perintah penuh:Kali ini kami hanya bekerja dengan
stderr
, jadi desinkronisasi tidak lagi menjadi perhatian. Sayangnya dengan cara ini Anda tidak akan melihat jejak atau keluaran dariecho
cetakan itu untukstdout
(jika ada). Kita bisa mencoba untuk membangun kembali filter kami untuk mendeteksi pengalihan (>&2
) tetapi jika Anda melihatecho foobar >&2
,echo >&2 foobar
danecho "foobar >&2"
kemudian Anda mungkin akan setuju bahwa semakin banyak masalah.Banyak tergantung pada gema yang Anda miliki dalam skrip Anda. Berpikir dua kali sebelum Anda menerapkan beberapa filter kompleks, mungkin akan menjadi bumerang. Lebih baik memiliki sedikit redundansi daripada secara tidak sengaja melewatkan beberapa informasi penting.
Alih-alih membuang jejak eksekusi
echo
kita dapat membuang outputnya - dan output apa pun kecuali jejak. Untuk menganalisis jejak eksekusi saja, coba:Sangat mudah? Tidak. Pikirkan apa yang akan terjadi jika ada
echo "+ rm -rf --no-preserve-root /" >&2
dalam naskah. Seseorang mungkin terkena serangan jantung.Dan akhirnya…
Untungnya ada
BASH_XTRACEFD
variabel lingkungan. Dariman bash
:Kita bisa menggunakannya seperti ini:
Perhatikan baris pertama memunculkan subkulit. Dengan cara ini deskriptor file tidak akan tetap valid atau variabel yang ditugaskan di shell saat ini setelahnya.
Berkat
BASH_XTRACEFD
Anda dapat menganalisis jejak yang bebas dari gema dan output lainnya, apa pun itu. Ini tidak persis seperti yang Anda inginkan tetapi analisis saya membuat saya berpikir ini (secara umum) The Right Way.Tentu saja Anda dapat menggunakan metode lain, terutama saat Anda perlu menganalisis
stdout
dan / ataustderr
mengikuti jejak Anda. Anda hanya perlu mengingat ada batasan dan jebakan tertentu. Saya mencoba menunjukkan kepada mereka.sumber
Dalam Makefile Anda bisa menggunakan
@
simbol.Contoh penggunaan:
@echo 'message'
Dari dokumen GNU ini :
sumber
./test.sh: line 1: @echo: command not found
, tapi saya menggunakan bash.@
hanya berfungsi saat Anda bergaung di makefile.Diedit 29 Okt 2016 per saran moderator bahwa yang asli tidak mengandung informasi yang cukup untuk memahami apa yang terjadi.
"Trik" ini menghasilkan hanya satu baris output pesan di terminal ketika xtrace aktif:
Pertanyaan aslinya adalah Apakah ada cara untuk membiarkan -x diaktifkan, tetapi hanya menampilkan pesan alih-alih dua baris . Ini adalah sebuah kutipan tepat dari pertanyaan.
Saya mengerti pertanyaannya, bagaimana "membiarkan set -x diaktifkan DAN menghasilkan satu baris untuk sebuah pesan"?
Jadi secara ringkas, OP membutuhkan:
OP tidak mengharuskan perintah gema digunakan. Mereka mengutipnya sebagai contoh produksi pesan, menggunakan singkatan mis Yang merupakan singkatan dari Latin exempli gratia, atau "misalnya".
Berpikir "di luar kotak" dan mengabaikan penggunaan gema untuk menghasilkan pesan, saya perhatikan bahwa pernyataan tugas dapat memenuhi semua persyaratan.
Lakukan penugasan string teks (berisi pesan) ke variabel tidak aktif.
Menambahkan baris ini ke skrip tidak mengubah logika apa pun, namun menghasilkan satu baris saat tracing aktif: (Jika Anda menggunakan variabel $ echo , cukup ubah nama menjadi variabel lain yang tidak digunakan)
Apa yang akan Anda lihat di terminal hanya 1 baris:
bukan dua, karena OP tidak suka:
Berikut ini contoh skrip untuk ditunjukkan. Perhatikan bahwa penggantian variabel ke dalam pesan (nama direktori $ HOME 4 baris dari akhir) berfungsi, sehingga Anda dapat melacak variabel menggunakan metode ini.
Dan ini adalah output dari menjalankannya di root, lalu direktori home.
sumber
echo "Here are the files in this directory:" *
?