Log keluar kode perintah, mirip dengan perintah waktu

10

menggunakan

time sleep 1

hasil:

$ time sleep 1

real    0m1.005s
user    0m0.001s
sys     0m0.001s

apakah ada perintah yang bisa saya gunakan untuk mencetak kode keluar sleepatau perintah apa pun yang ingin saya jalankan?

Sesuatu suka:

$ log-exit-code sleep 1

mungkin ini cukup?

sleep 1 && echo "$?"
Alexander Mills
sumber
3
Tidak, sleep 1 && echo $?akan mencetak kode sel tidur hanya ketika itu nol ...
Satō Katsura
oh lol kau benar
Alexander Mills
1
sleep 1 && echo "$?" || echo "$?"
jesse_b
2
Saya menambahkan jawaban karena semua yang lain sejauh ini membuang nilai pengembalian asli, yang imo buruk (setelah jawaban itu, $? Selalu 0 karena hal terakhir yang mereka lakukan adalah disply, yang selalu berfungsi. Mereka semua lupa untuk menjaga nilai pengembalian asli, dan mengembalikannya setelah menampilkannya. Oleh karena itu mereka hanya menunjukkannya kepada pengamat, tetapi 'sembunyikan' untuk sisa skrip, yang tidak dapat digunakan sekarang "$?" untuk melihat apakah perintah penting itu kembali 0 atau yang lainnya ...)
Olivier Dulac
1
Juga semua solusi di bawah ini dapat dengan mudah mengimplementasikan fungsi ini. Kusalananda misalnya hanya akan:EXIT_CODE=$(tellexit command)
jesse_b

Jawaban:

4

Dalam pengujian saya sejauh ini ini berhasil:

command && echo "$?" || echo "$?"

Katakan saja untuk mengulang kode keluar jika berhasil atau gagal.

Seperti yang ditunjukkan Sato di bawah ini pada dasarnya sama dengan:

command; echo "$?"

Satu hal yang bisa membuat perintah dan / atau berharga adalah sesuatu seperti:

command && echo "Success! Exit Code: $?" || echo "Failure! Exit Code: $?"

Jika Anda memerlukan skrip Anda untuk bertindak berdasarkan kode keluar seperti yang menjadi perhatian Olivier, itu bukan masalah. Skrip Anda dapat terlihat seperti:

command
case "$?" in; 
    0) echo "Command exited with: 0"
       <command if good>
       ;;
    1) echo "Command exited with: 1"
        <command if bad>
        ;;
    255) echo "Command exited with: 255"  # for ssh maybe?
         <command if 255>
         ;;
    *) echo "Command exited with: >2"
        <command for other exit code>
        ;;
esac
jesse_b
sumber
13
Err, bagaimana itu lebih baik dari sekadar command; echo $??
Satō Katsura
4
Saya kira tidak. command; echo $?adalah jawaban yang jauh lebih baik.
jesse_b
uhhh ini sepertinya itu pilihan terbaik, jadi saya memilih itu sebagai jawaban, kalau ada yang tidak setuju, lmk
Alexander Mills
@AlexanderMills Hanya Anda yang tahu jawaban mana yang paling tepat untuk pertanyaan Anda :-)
Kusalananda
1
Saya tidak suka ini: ini mengubah kode keluar setelah command ; echo $?karena echo $?selalu berhasil dan karenanya setelah itu, nilai baru $? sekarang 0. Ada cara untuk menyimpan kode pengembalian asli bahkan setelah menampilkannya, jika kode pengembalian asli ini berguna nanti (Misalnya: dalam sebuah skrip, memeriksa kode pengembalian itu penting sebelum memutuskan bagaimana untuk melanjutkan). Lihat jawaban saya untuk satu dari beberapa solusi (sebagian besar jawaban di sini dapat diubah dengan cara yang sama)
Olivier Dulac
11

cmd && echo "$?"tidak akan bekerja karena hanya akan mencetak angka nol ( echohanya akan mengeksekusi setelah berhasil menyelesaikan perintah sebelumnya).

Inilah fungsi shell pendek untuk Anda:

tellexit () {
    "$@"

    local err="$?"
    printf 'exit code\t%d\n' "$err" >/dev/tty
    return "$err"
}

Ini mencetak kode keluar dari perintah yang diberikan dengan cara yang sama seperti timeperintah.

$ tellexit echo "hello world"
hello world
exit code       0

$ tellexit false
exit code       1

Dengan mengarahkan ulang printfke /dev/ttydalam fungsi, kita masih dapat menggunakan tellexitpengalihan tanpa mendapatkan sampah di output standar atau aliran kesalahan:

$ tellexit bash -c 'echo hello; echo world >&2' >out 2>err
exit code       0
$ cat out
hello
$ cat err
world

Dengan menyimpan kode keluar dalam suatu variabel, kami dapat mengembalikannya ke pemanggil:

$ tellexit false || echo 'failed'
exit code       1
failed

Versi yang lebih bagus dari fungsi yang sama juga mencetak sinyal yang mematikan perintah jika kode keluar lebih besar dari 128 (yang berarti diakhiri karena suatu sinyal):

tellexit () {
    "$@"

    local err="$?"

    if [ "$err" -gt 128 ]; then
        printf 'exit code\t%d (%s)\n' "$err" "$(kill -l "$err")" >/dev/tty
    else
        printf 'exit code\t%d\n' "$err" >/dev/tty
    fi

    return "$err"
}

Pengujian:

$ tellexit sh -c 'kill $$'
exit code       143 (TERM)

$ tellexit sh -c 'kill -9 $$'
Killed
exit code       137 (KILL)

( localMasalahnya memerlukan ash/ pdksh/ bash/ zsh, atau Anda dapat mengubahnya typesetyang beberapa shell lainnya juga mengerti.)

Kusalananda
sumber
keren, bagaimana Anda menggunakan tellexit, tunjukkan dalam aksi, tolong
Alexander Mills
1
@AlexanderMills Lihat pembaruan.
Kusalananda
2
Seharusnya menyimpan nilai pengembalian asli dan mengembalikannya pada akhirnya.
Olivier Dulac
@OlivierDulac Pengamatan yang sangat bagus! Saya akan mengubah.
Kusalananda
7

Gunakan fungsi pembungkus shell thingy. Mungkin dengan nama yang berbeda.

$ exito() { "$@"; echo $?; }
$ exito true
0
$ exito false
1
$ exito echo "test test"      
test test
0
$ 

(Ini tentu saja akan merusak output standar, jadi gunakan yang ttyditunjukkan oleh @Kusalananda atau jangan gunakan di luar konteks interaktif.)

Menyingkir ke wilayah yang tidak dapat ditonton, beberapa shell dapat melaporkan status semua perintah dalam sebuah pipa, bukan hanya yang terakhir, misalnya di ZSH jika Anda ingin kegagalan dilaporkan dari seluruh pipa:

% TRAPZERR() { print >/dev/tty $pipestatus }
% perl -e 'exit 42' | perl -e 'exit 99'
42 99
% false | perl -e 'exit 42' | perl -e 'exit 99'
1 42 99
% perl -e 'exit 42' | perl -e 'exit 99' | true
% 

TRAPZERR jika tidak, tidak akan menyala ketika tidak ada kesalahan (pada prinsip "tidak ada berita adalah kabar baik").

thrig
sumber
1
Seharusnya menyimpan nilai pengembalian asli dan mengembalikannya pada akhirnya. Menampilkan (printf, gema) hal sederhana (tidak dimulai dengan '-', dll) selalu berfungsi, dan setelah itu menampilkan "$?" kini memiliki nilai "0" saat layar bekerja
Olivier Dulac
7

GNU timememiliki opsi untuk ini:

time -f %x sleep 1
0

Melewati kode keluar 1 , kecuali dibunuh oleh sinyal 2 :

$ /usr/bin/time -f %x sleep 1; echo $?
0
0

$ /usr/bin/time -f %x sleep 2x; echo $?
sleep: invalid time interval ‘2x’
Try 'sleep --help' for more information.
1
1

$ /usr/bin/time -f %x sleep 60s; echo $? # Press ^C before a minute elapses
0
2

Jika Anda ingin mengetahui / menangani situasi signal kill, berikan -vdan ambil stderr untuk string Command terminated by signal.


1 Terima kasih kepada Olivier Dulac karena mencatat kode keluar melewati.
2 Juga, terima kasih Stéphane Chazelas karena menunjukkan kode keluar sinyal mati tidak melewati.

uskup
sumber
1
Interresting. Hanya GNU tetapi interresting, karena yang satu akan (mungkin) menyimpan kode pengembalian asli untuk sisa kode yang akan diikuti (yaitu, itu tidak menggantikannya dengan "0" seperti jawaban yang lain lakukan)
Olivier Dulac
2
Itu kembali 0meskipun jika perintah itu terbunuh.
Stéphane Chazelas
2
@ bishop, saya setuju 0 dalam kasus itu kurang ideal. Tetapi perhatikan bahwa, tidak ada kesepakatan umum tentang apa yang harus dilaporkan ketika suatu perintah dibunuh oleh sinyal. Mengembalikan 128 + nomor sinyal digunakan oleh beberapa shell. Anda juga melihat hal-hal seperti 256+signumatau 384+signumatau bahkan signum/256atau signum/256+0.5jika suatu inti dihasilkan (sebenarnya status dikembalikan oleh waitpid()dibagi 256). Beberapa akan memberikan representasi tekstual (suka sigintatau sigquit+core). Masih mungkin layak dibahas di newsgroup gnu.coreutils.bugs saya setuju.
Stéphane Chazelas
3
(sebenarnya timebukan bagian dari coreutils, itu adalah paket dengan daftar mailing bug sendiri ([email protected]))
Stéphane Chazelas
1
@bishop, yang system()dari awks tradisional seperti yang masih dipertahankan oleh Brian Kernighan (yang kdi awk) atau nawkdari Solaris, yang awkdari FreeBSD yang berasal dari itu. awk 'BEGIN{print system("kill -s ABRT $$")}'output 0.523438ketika ukuran dump inti tidak terbatas. Perilaku gawktelah berubah baru-baru ini, dengan perilaku yang berbeda-beda dengan --traditionalatau --posix(lihat juga nilai pengembalian close(cmd)untuk variasi yang lebih banyak)
Stéphane Chazelas
2

Saya tidak menyukai semua jawaban lain (walaupun saya sangat suka kepintarannya): mereka menampilkan kode keluar tetapi mereka juga MENGUBAHNYA. (bagian yang ditampilkan adalah benar, maka setelah itu kembalinya adalah 0)

Ini adalah versi yang dimodifikasi:

do_and_tell () {
   "$@"
   returncode="$?"
   printf "Execution of : \n%s\n yelded a return code of: \n%s\n" "$*" "$returncode"
   return $returncode  # the interresting addition... keeps the commands return value.
}

## usage:

prompt$ do_and_tell true
Execution of : 
true
 yelded a return code of: 
0

prompt$ echo $?
0

prompt$ do_and_tell false
Execution of : 
false
 yelded a return code of: 
1

prompt$ echo $?
1
Olivier Dulac
sumber
sebenarnya, jawaban @bishop adalah satu-satunya yang mempertahankan nilai ini, tetapi bergantung pada versi GNU timeyang tidak selalu tersedia.
Olivier Dulac
2

Agar sedikit lebih kuat, kirim status keluar ke FD terpisah sehingga dapat ditangani secara independen dari stdout / stderr:

exit_status() {
    "$@"
    rv=$?
    echo >&3 "$rv"
    return "$rv"
}

# exit status and stdout to stdout:
> exit_status echo "hello" 3>&1
hello
0

# exit_status to stdout, stdout to /dev/null
> exit_status echo "hello" 3>&1 1>/dev/null
0
> exit_status ehco "hello" 3>&1 1>/dev/null
ehco: command not found
127

# exit_status to stdout, stdout and stderr to /dev/null
> exit_status ehco "hello" 3>&1 &>/dev/null
127

Catatan Anda harus melakukan sesuatu dengan FD3 atau akan mengatakan Bad file descriptor.

Ini dapat dikonfigurasi lebih lanjut untuk memberikan output ini ke input dari program lain dengan meminta program lain mendengarkan lebih dari satu FD; Anda bisa menggunakan ini sebagai semacam awal 90-an Kibana :)

onwsk8r
sumber
Seharusnya menyimpan nilai pengembalian asli dan mengembalikannya pada akhirnya. Menampilkan (printf, gema) hal sederhana (tidak dimulai dengan '-', dll) selalu berfungsi, dan setelah itu menampilkan "$?" kini memiliki nilai "0" saat layar bekerja
Olivier Dulac
1

Sementara semua jawaban (sangat menarik) lainnya menjawab pertanyaan persis yang diajukan OP, dalam praktiknya, hal-hal biasanya lebih sederhana. Anda cukup menyimpan status keluar ke variabel (segera mengikuti perintah) dan melaporkan atau mencatatnya dengan cara apa pun yang Anda inginkan. Misalnya mencetak ke / dev / stderr atau menulis / menambahkannya sebagai pesan teks ke dalam file log. Dan, itu juga dipertahankan untuk digunakan dalam pengambilan keputusan / kontrol aliran dalam kode selanjutnya.

Jika dalam suatu fungsi:

function some-function () {
    local rc ## to avoid side effects
    ...
    some-command ## run any command
    rc=$?  ## save the return status in a variable
    echo "some-command returned $rc" ## optionally tell the user about it
    ...
    return $rc  ## at the end, if appropriate
}

Jika langsung di skrip:

some-command ## run any command
rc=$?  ## save the return status in a variable
echo "some-command returned $rc" ## optionally tell the user about it
...
exit $rc    ## at the end to tell any calling script about it, if appropriate

(Menunduk dan menutupi karena ini tidak menjawab pertanyaan yang tepat .)

Joe
sumber