Katakanlah saya memiliki skrip seperti berikut:
tidak berguna
echo "This Is Error" 1>&2
echo "This Is Output"
Dan saya punya skrip shell lain:
alsoUseless.sh
./useless.sh | sed 's/Output/Useless/'
Saya ingin menangkap "This Is Error", atau stderr lainnya dari useless.sh, menjadi variabel. Sebut saja ERROR.
Perhatikan bahwa saya menggunakan stdout untuk sesuatu. Saya ingin terus menggunakan stdout, jadi mengarahkan stderr ke stdout tidak membantu, dalam hal ini.
Jadi, pada dasarnya, saya ingin melakukannya
./useless.sh 2> $ERROR | ...
tapi itu jelas tidak berhasil.
Saya juga tahu bahwa saya bisa melakukannya
./useless.sh 2> /tmp/Error
ERROR=`cat /tmp/Error`
tapi itu jelek dan tidak perlu.
Sayangnya, jika tidak ada jawaban yang muncul di sini, itulah yang harus saya lakukan.
Saya berharap ada cara lain.
Adakah yang punya ide yang lebih baik?
ERROR=$(./useless.sh | sed 's/Output/Useless/' 2>&1 1>/dev/ttyX)
Jawaban:
Akan lebih rapi untuk menangkap file kesalahan sebagai berikut:
Shell mengenali ini dan tidak harus menjalankan '
cat
' untuk mendapatkan data.Pertanyaan yang lebih besar sulit. Saya tidak berpikir ada cara mudah untuk melakukannya. Anda harus membangun seluruh pipa ke dalam sub-shell, akhirnya mengirim output standar akhir ke file, sehingga Anda dapat mengarahkan kesalahan ke output standar.
Perhatikan bahwa semi-colon diperlukan (dalam shell klasik - Bourne, Korn - pasti; mungkin di Bash juga). '
{}
' Apakah saya / O pengalihan atas perintah terlampir. Seperti yang ditulis, itu akan menangkap kesalahansed
juga.sumber
/dev/null
alih-alihoutfile
(Jika Anda seperti saya, Anda menemukan pertanyaan ini melalui Google, dan tidak memiliki persyaratan yang sama dengan OP)stdout
danstderr
maju dan mundur. Tetapi berhati-hatilah , seperti yang dikatakan di sini : Dalam bash, akan lebih baik untuk tidak berasumsi bahwa file deskriptor 3 tidak digunakan " .alsoUseless.sh
Ini akan memungkinkan Anda untuk mem-pipe output
useless.sh
skrip Anda melalui perintah sepertised
dan menyimpanstderr
dalam variabel bernamaerror
. Hasil dari pipa dikirim kestdout
untuk ditampilkan atau disalurkan ke perintah lain.Ini mengatur beberapa deskriptor file tambahan untuk mengelola pengalihan yang diperlukan untuk melakukan ini.
sumber
stderr
danstdout
dalam variabel?dry_run
fungsi yang andal dapat memilih antara menggemakan argumennya dan menjalankannya, terlepas dari apakah perintah yang sedang dijalankan sedang disalurkan ke file lain.read
tidak menerima input dari pipa. Anda dapat menggunakan teknik lain untuk mencapai apa yang Anda coba peragakan.Pengarah stderr ke stdout, stdout ke / dev / null, dan kemudian gunakan backticks atau
$()
untuk menangkap stderr yang diarahkan:sumber
PY_VERSION="$(python --version 2>&1)"
Ada banyak duplikat untuk pertanyaan ini, banyak di antaranya memiliki skenario penggunaan yang sedikit lebih sederhana di mana Anda tidak ingin menangkap stderr dan stdout dan kode keluar semuanya secara bersamaan.
berfungsi untuk skenario umum di mana Anda mengharapkan output yang tepat jika berhasil, atau pesan diagnostik pada stderr jika gagal.
Perhatikan bahwa pernyataan kontrol shell sudah memeriksa di
$?
bawah tenda; jadi apa pun yang terlihat sepertihanyalah cara kikuk, tanpa kata-kata
sumber
sumber
command
adalah pilihan yang buruk di sini, karena sebenarnya ada builtin dengan nama itu. Mungkin membuatnyayourCommand
atau semacamnya, agar lebih eksplisit.Untuk kepentingan pembaca, resep ini ada di sini
Jika Anda ingin menangkap
stderr
beberapacommand
ke dalamvar
Anda dapat melakukannyaSetelah itu Anda memiliki semuanya:
Jika
command
sederhana (bukan sesuatu sepertia | b
) Anda dapat meninggalkan batin{}
:Dibungkus menjadi
bash
fungsi -mudah yang dapat digunakan kembali (mungkin membutuhkan versi 3 dan di atas untuklocal -n
):Dijelaskan:
local -n
alias "$ 1" (yang merupakan variabel untukcatch-stderr
)3>&1
menggunakan file descriptor 3 untuk menyimpan poin stdout di sana{ command; }
(atau "$ @") kemudian mengeksekusi perintah dalam output capturing$(..)
2>&1
pengalihanstderr
ke penangkapan output$(..)
1>&3
redirectstdout
dari output menangkap$(..)
kembali ke "luar"stdout
yang disimpan dalam file descriptor 3. Perhatikan bahwastderr
masih merujuk ke tempat FD 1 menunjuk sebelumnya: Ke output menangkap$(..)
3>&-
kemudian menutup file deskriptor 3 karena tidak diperlukan lagi, sehinggacommand
tidak tiba-tiba muncul beberapa deskriptor file terbuka yang tidak dikenal. Perhatikan bahwa cangkang luar masih memiliki FD 3 terbuka, tetapicommand
tidak akan melihatnya.lvm
mengeluh tentang deskriptor file yang tidak terduga. Danlvm
mengeluhstderr
- hanya apa yang akan kita tangkap!Anda dapat menangkap deskriptor file lain dengan resep ini, jika Anda menyesuaikannya. Kecuali deskriptor file 1 tentu saja (di sini logika redirection akan salah, tetapi untuk deskriptor file 1 Anda dapat menggunakan
var=$(command)
seperti biasa).Perhatikan bahwa ini deskriptor file pengorbanan 3. Jika Anda membutuhkan deskriptor file itu, jangan ragu untuk mengubah nomornya. Namun perlu diperhatikan, bahwa beberapa kerang (dari tahun 1980-an) dapat dipahami
99>&1
sebagai argumen yang9
diikuti9>&1
(ini bukan masalah bagibash
).Perhatikan juga bahwa tidak mudah untuk membuat FD 3 ini dapat dikonfigurasi melalui variabel. Ini membuat banyak hal menjadi tidak terbaca:
Catatan:
catch-var-from-fd-by-fd var 2 3 cmd..
sama dengancatch-stderr var cmd..
shift || return
hanyalah beberapa cara untuk mencegah kesalahan jelek jika Anda lupa untuk memberikan jumlah argumen yang benar. Mungkin menghentikan shell akan menjadi cara lain (tetapi ini membuatnya sulit untuk menguji dari commandline).exec
, tetapi kemudian menjadi sangat jelek.bash
-juga sehingga tidak perlulocal -n
. Namun kemudian Anda tidak dapat menggunakan variabel lokal dan itu menjadi sangat jelek!eval
s digunakan dengan cara yang aman. Biasanyaeval
dianggap berbahaya. Namun dalam hal ini tidak lebih jahat daripada menggunakan"$@"
(untuk menjalankan perintah sewenang-wenang). Namun tolong pastikan untuk menggunakan kutipan yang tepat dan benar seperti yang ditunjukkan di sini (kalau tidak menjadi sangat berbahaya ).sumber
Begini cara saya melakukannya:
Contoh penggunaan:
Ini tidak menggunakan file sementara. Tapi setidaknya barang jelek dibungkus dalam suatu fungsi.
sumber
eval
. Misalnya,printf -v "$1" '%s' "$(<tmpFile)"
jangan mengambil risiko menjalankan kode arbitrer jikaTMPDIR
variabel Anda telah disetel ke nilai berbahaya (atau nama variabel tujuan Anda berisi nilai seperti itu).rm -- "$tmpFile"
lebih kuat daripadarm $tmpFile
.Ini adalah masalah menarik yang saya harap ada solusi yang elegan. Sayangnya, saya berakhir dengan solusi yang mirip dengan Mr. Leffler, tetapi saya akan menambahkan bahwa Anda dapat memanggil tidak berguna dari dalam fungsi Bash untuk meningkatkan keterbacaan:
Semua jenis pengalihan output lainnya harus didukung oleh file sementara.
sumber
POSIX
STDERR dapat ditangkap dengan beberapa sihir redirection:
Perhatikan bahwa pemipaan STDOUT dari perintah (di sini
ls
) dilakukan di bagian terdalam{
}
. Jika Anda menjalankan perintah sederhana (mis., Bukan pipa), Anda bisa menghapus kawat gigi bagian dalam ini.Anda tidak bisa mem-pipe di luar perintah karena pemipaan membuat subshell di
bash
danzsh
, dan penugasan ke variabel dalam subshell tidak akan tersedia untuk shell saat ini.pesta
Di
bash
, akan lebih baik untuk tidak menganggap bahwa file deskriptor 3 tidak digunakan:Perhatikan bahwa ini tidak berhasil
zsh
.Berkat jawaban ini untuk ide umum.
sumber
Posting ini membantu saya menghasilkan solusi serupa untuk tujuan saya sendiri:
Kemudian selama PESAN kami bukan string kosong, kami meneruskannya ke hal-hal lain. Ini akan memberi tahu kami jika format_logs.py kami gagal dengan semacam pengecualian python.
sumber
Ambil dan cetak stderr
Kerusakan
Anda dapat menggunakan
$()
untuk menangkap stdout, tetapi Anda ingin menangkap stderr. Jadi, Anda menukar stdout dan stderr. Menggunakan fd 3 sebagai penyimpanan sementara dalam algoritma swap standar.Jika Anda ingin menangkap DAN mencetak gunakan
tee
untuk membuat duplikat. Dalam hal ini outputtee
akan ditangkap$()
daripada pergi ke konsol, tetapi stderr (daritee
) masih akan pergi ke konsol sehingga kami menggunakannya sebagai output kedua untuktee
melalui file khusus/dev/fd/2
karenatee
mengharapkan path file daripada fd jumlah.CATATAN: Itu adalah banyak sekali pengalihan dalam satu baris dan urutan penting.
$()
adalah meraih stdouttee
pada akhir pipa dan pipa itu sendiri mengarahkan stdout./useless.sh
ke stdintee
SETELAH kami bertukar stdin dan stdout untuk./useless.sh
.Menggunakan stdout dari ./useless.sh
OP mengatakan dia masih ingin menggunakan (bukan hanya mencetak) stdout, seperti
./useless.sh | sed 's/Output/Useless/'
.Tidak masalah lakukan saja SEBELUM menukar stdout dan stderr. Saya sarankan untuk memindahkannya ke fungsi atau file (juga-useless.sh) dan memanggilnya sebagai ganti ./useless.sh pada baris di atas.
Namun, jika Anda ingin MENGAMBIL stdout DAN stderr, maka saya pikir Anda harus kembali pada file sementara karena
$()
hanya akan melakukan satu per satu dan itu membuat subkulit dari mana Anda tidak dapat mengembalikan variabel.sumber
Mengulang sedikit pada jawaban Tom Hale, saya merasa mungkin untuk membungkus yoga redirection ke dalam suatu fungsi agar lebih mudah digunakan kembali. Sebagai contoh:
Hampir bisa dipastikan untuk menyederhanakan ini lebih lanjut. Belum diuji secara menyeluruh-menyeluruh, tetapi tampaknya berhasil dengan bash dan ksh.
sumber
Jika Anda ingin memotong penggunaan file sementara Anda mungkin dapat menggunakan substitusi proses. Saya belum membuatnya bekerja. Ini adalah upaya pertama saya:
Lalu saya mencoba
Namun
Jadi proses substitusi secara umum melakukan hal yang benar ... sayangnya, setiap kali saya membungkus STDIN
>( )
dengan sesuatu dalam$()
upaya untuk menangkap itu ke suatu variabel, saya kehilangan konten$()
. Saya pikir ini karena$()
meluncurkan sub proses yang tidak lagi memiliki akses ke file descriptor di / dev / fd yang dimiliki oleh proses induk.Substitusi proses telah memberi saya kemampuan untuk bekerja dengan aliran data yang tidak lagi di STDERR, sayangnya saya sepertinya tidak dapat memanipulasinya seperti yang saya inginkan.
sumber
./useless.sh 2> >( ERROR=$( cat <() ); echo "$ERROR" )
maka Anda akan melihat output dariERROR
. Masalahnya adalah bahwa proses substitusi dijalankan dalam sub-shell, sehingga nilai yang ditetapkan dalam sub-shell tidak mempengaruhi shell induk.sumber
a=> b=>stderr
a
dievaluasi dan ditetapkan dalam sub-shell, dan penugasan dalam sub-shell tidak mempengaruhi shell induk. (Diuji pada Ubuntu 14.04 LTS dan juga Mac OS X 10.10.1.)GNU bash, version 4.4.12(1)-release (x86_64-pc-msys)
)SLE 11.4
baik dan menghasilkan efek yang dijelaskan oleh @JonathanLefflerDalam zsh:
sumber
Untuk kesalahan membuktikan perintah Anda:
Terinspirasi dalam Lean manufacturing:
sumber
if
. Biarkan saya memposting solusi terpisah.Solusi sederhana
Akan menghasilkan:
sumber
Memperbaiki jawaban YellowApple :
Ini adalah fungsi Bash untuk menangkap stderr ke dalam variabel apa pun
stderr_capture_example.sh
:Pengujian:
Keluaran:
Fungsi ini dapat digunakan untuk menangkap pilihan
dialog
perintah yang dikembalikan .sumber