suatu saat mulai sekarang lakukan sesuatu (dan mungkin juga tampilkan hasil di konsol)

12

Saya menggunakan server Ubuntu 16.04 dan saya ingin menggunakan utilitas atdalam sesi saya saat ini untuk melakukan sesuatu 1 menit dari sekarang (katakanlah, an echo), tanpa memberikan tanggal dan waktu tertentu - hanya 1 menit lebih cepat dari waktu sekarang.

Ini gagal:

echo 'hi' | at 1m

Alasan saya memilih atlebih sleepadalah karena sleep cacat sesi saat ini dan karenanya lebih cocok untuk menunda perintah di sesi lain, daripada yang kami bekerja dengan sebagian besar waktu. AFAIR, attidak berperilaku seperti ini dan tidak akan mengganggu sesi saya.

Perbarui_1

Dengan jawaban Pied Piper, saya sudah mencoba:

(sleep 1m; echo 'hi')  &

Saya memiliki masalah dengan metode ini: Aliran "hi" dicetak di dalam prompt utama saya dan juga menambahkan prompt sekunder kosong ( _) tepat di bawah prompt utama yang berisi itu, lihat:

USER@:~# (sleep 1m; echo 'hi')  &
[1] 22731
USER@:~# hi
^C
[1]+  Done

Perbarui_2

Dengan jawaban Peter Corde saya mencoba:

(sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)

Ini berfungsi dengan baik di Bash 4.4, tetapi tidak di beberapa versi yang lebih lama, tampaknya (lihat komentar dalam jawabannya). Saya pribadi menggunakan Bash 4.3 di lingkungan saya sendiri.

Arcticooling
sumber
2
Saya berharap bendera kill adalah GNU-style dan bisa disusun ulang sehingga tidak terdengar seperti "kill the wench"!
NH.

Jawaban:

6

Aliran "hi" dicetak di prompt saya (di stdin) dan bukan di sayastdout

Tidak, ini bukan "dicetak di Anda stdin".

Jika Anda menekan kembali, itu tidak akan mencoba dijalankan hisebagai perintah. Hanya saja latar belakang echodicetak hisaat kursor mengikuti permintaan Anda.


Mungkin Anda ingin mencetak baris baru , seperti (sleep 1m && echo -e '\nhi' &). (Sesuaikan seperlunya untuk echodi shell Anda. Atau gunakan printfuntuk portabilitas.)

Saya menempatkan bagian &dalam subkulit untuk "menolak" pekerjaan latar belakang, sehingga Anda juga menghindari "suara" dari [1]+ Done. Dengan di &&antara perintah, &berlaku untuk seluruh rantai majemuk-perintah, sehingga kembali ke prompt shell segera daripada menunggu untuk sleepkeluar seperti yang Anda dapatkan dengan(sleep 1; echo ... &)

Inilah yang terjadi (dengan penundaan 1 detik):

peter@volta:~$ (sleep 1 && echo -e '\nhi' &)
peter@volta:~$ 
hi
       # cursor is at the start of this empty line.

Bash tidak tahu bahwa echomencetak sesuatu, jadi tidak tahu itu perlu mencetak ulang promptnya atau membersihkan layar. Anda dapat melakukannya secara manual dengan control-L.

Anda bisa menulis fungsi shell yang mendapat bash untuk mencetak ulang promptnya setelah echoselesai . Hanya melakukan echo "$PS1"tidak akan mereproduksi karakter yang sudah diketik. kill -WINCH $$pada sistem saya mendapat bash untuk mencetak ulang baris prompt tanpa membersihkan layar, meninggalkan hibaris sendiri sebelum prompt. SIGWINCH dikirim secara otomatis ketika WINdow size Mengubah, dan respons bash terhadapnya melakukan apa yang kita inginkan.

Mungkin bekerja dengan cangkang lain, tapi saya tidak berusaha membuat 100% portable / POSIX ini. ( Sayangnya, ini hanya bekerja pada bash4.4, bukan bash4.3, atau tergantung pada beberapa pengaturan yang saya tidak sadari )

  # bash4.4
peter@volta:~$ (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)
peter@volta:~$ ljlksjksajflasdf dfas      # typed in 2 seconds
hi
peter@volta:~$ ljlksjksajflasdf dfas      # reprinted by Bash because of `kill -WINCH`

Anda dapat dengan mudah menyelesaikannya dalam fungsi shell yang membutuhkan argumen sleep dan sebuah pesan.

bash 4.3 tidak mencetak ulang promptnya setelah SIGWINCH, jadi ini tidak berfungsi di sana. Saya telah shopt -s checkwinsizemengaktifkan kedua sistem, tetapi hanya bekerja pada sistem Bash 4.4 (Arch Linux).

Jika tidak berhasil, Anda dapat mencoba (sleep 2 && echo -e '\nhi' && echo "$PS1" &), yang "berfungsi" jika baris perintah kosong. (Ini juga mengabaikan kemungkinan yang PROMPT_COMMANDdiatur.)


Tidak ada cara yang benar-benar bersih untuk mendapatkan output terminal untuk bercampur dengan apa pun yang Anda lakukan di terminal nanti, ketika timer menyala. Jika Anda sedang menggulirkan sesuatu less, Anda dapat dengan mudah melewatkannya.

Jika Anda mencari sesuatu dengan hasil interaktif, saya sarankan memutar file audio. misalnya dengan pemain command-line like mpv(fork of MPlayer2). Atau pilih file video sehingga jendela baru terbuka.

(sleep 1 && mpv /f/share/music/.../foo.ogg </dev/null &>/dev/null &)

Anda mungkin ingin memiliki echoxterm / konsole / gnome-terminal baru Anda. Gunakan opsi yang membuat terminal tetap terbuka setelah perintah keluar.

Peter Cordes
sumber
Saya berterima kasih kepada Anda, David, dan saya telah mengacungkan jempol. Ini benar-benar yang paling dekat yang saya dapatkan tentang apa yang saya cari. Apakah ada cara untuk memutakhirkan perintah berikut untuk meniru Enteracara tekan tombol, tepat setelah gema muncul, jadi saya secara otomatis akan dibawa kembali ke prompt utama konsol ? (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &).
Arcticooling
@Arcticooling: kill -WINCH $$ melakukan refresh prompt shell yang berjalan di terminal. Itulah inti dari menggunakannya. Menekan enter akan mengirimkan perintah yang diketik sebagian, tetapi WINCH tidak, seperti yang saya tunjukkan. Jika Anda belum mengetik apa pun, maka Anda mendapatkan prompt kosong.
Peter Cordes
1
Jika Anda mulai mengetik rm -rf ~/some/directory, Enter pada waktu yang salah akan berpotensi berjalan rm -rf ~/. (Tip keamanan: selesaikan jalur pengetikan lalu kontrol-a dan ubah lsmenjadi rm -rf, jadi masukkan jari-jari kapan saja tidak akan merusak hari Anda.)
Peter Cordes
Dengan kabel, saya mengeksekusi (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)dan setelah gema muncul saya mendapat prompt kosong, hanya setelah memukul Entersaya kembali ke prompt utama ... Maaf jika saya salah paham dengan Anda, saya cukup baru untuk Bash.
Arcticooling
@Arcticooling: Saya menggunakan Bash 4.4 di Arch Linux, di emulator terminal Konsole. Saya berharap perintah saya akan portabel setidaknya untuk versi lain dari bash, tapi mungkin tidak :( Ya, baru saja diuji di Bash 4.3 dalam satu screensesi (dan hanya melalui SSH) pada sistem Ubuntu yang lebih lama, dan saya mendapatkan perilaku yang Anda jelaskan.
Peter Cordes
13

Yang benar pada penggunaan adalah at <timespec>, di mana <timespec>spesifikasi waktu. Anda juga dapat menggunakan -fopsi untuk menentukan file yang berisi perintah untuk dieksekusi. Jika -fdan tidak ada redirection digunakan, at akan membaca perintah untuk mengeksekusi dari stdin (input standar).

Dalam contoh Anda, untuk mengeksekusi "beberapa perintah" (saya memilih "beberapa perintah" untuk menjadi "echo hi" dalam contoh di bawah) satu menit setelah Anda menekan enter (sekarang), yang dimaksudkan <timespec>untuk digunakan adalah now + 1 minute. Jadi, untuk mendapatkan hasil yang Anda inginkan dengan solusi satu-liner, silakan gunakan perintah di bawah ini:

echo 'echo hi' | at now + 1 minute

Ini seharusnya (dan akan dilakukan) untuk menjalankan echo hiperintah setelah satu menit. Masalahnya di sini adalah bahwa echo hiperintah akan dieksekusi oleh perintah at dalam dummy tty dan hasilnya akan dikirim ke kotak surat pengguna (jika dikonfigurasi dengan benar - yang memerlukan penjelasan yang lebih luas tentang cara mengkonfigurasi kotak surat di mesin unix dan bukan bagian dari ruang lingkup pertanyaan ini). Intinya adalah bahwa Anda tidak akan dapat langsung melihat hasil echo hidi terminal yang sama dengan yang Anda berikan perintah. Alternatif termudah adalah Anda mengarahkannya ke "beberapa file", misalnya:

echo 'echo hi > /tmp/at.out' | at now + 1 minute

Dalam contoh di atas, "some file" adalah /tmp/at.outdan hasil yang diharapkan adalah bahwa file di at.outbawah / tmp dibuat setelah satu menit dan contais teks 'hai'.

Selain now + 1 minutecontoh di atas, banyak spesifikasi waktu lainnya dapat digunakan, bahkan beberapa yang kompleks. Perintah at cukup pintar untuk mengartikan yang dapat dibaca oleh manusia seperti contoh di bawah ini:

now + 1 day, 13:25, mid‐night, noon, ...

Silakan merujuk ke halaman manual di untuk lebih lanjut tentang spesifikasi waktu yang mungkin karena variasi yang mungkin cukup luas dan dapat mencakup tanggal, waktu relatif atau absolut, dll.

Marcelo
sumber
2
atsecara default mengirimkan output melalui surat, tetapi ini harus dikonfigurasi dengan benar agar berfungsi.
Simon Richter
Saya sekarang menawarkan 100 rep hadiah untuk jawaban. Silakan baca pesan hadiah saya di bawah ini dan edit untuk menjelaskan lebih lanjut tentang apa at <timespec>dan bendera yang Anda gunakan dan apakah itu sesuai dengan Ubuntu 16.04 xenial.
Arcticooling
Ok, saya meningkatkan level detail dalam jawaban sehingga lebih didaktik sekarang. Mungkin terdengar agak bertele-tele untuk beberapa orang, tetapi saya percaya itu tidak meninggalkan keraguan untuk keraguan karena jelas mengutip contoh yang bekerja dan bagaimana mereka bekerja juga.
Marcelo
7

Jalankan sleepdalam subkulit:

(sleep 60; echo -e '\nhi')  &

Catatan: di Linux Anda bisa memberikan argumen untuk tidur dalam hitungan detik atau dengan sufiks s / m / h / d (untuk detik, menit, jam, hari). Pada BSD argumen harus dalam hitungan detik

PiedPiper
sumber
Saya punya masalah kecil dengan metode ini --- aliran "hi" dicetak di prompt saya (di stdin) dan bukan di saya stdout(seperti akan dengan biasa echo).
Arcticooling
1
Jika Anda tidak ingin output di terminal Anda tercampur dengan prompt dan output dari perintah lain, Anda harus mengarahkannya kembali ke suatu tempat
PiedPiper
Pengalihan ini gagal @PiedPiper (sleep 10s; echo 'hi') & 1>. Saya sekarang membaca lebih lanjut tentang ini.
Arcticooling
2
Membaca dokumentasi selalu merupakan ide yang bagus :)
PiedPiper
1
@Arcticooling Sepertinya Anda bingung tentang arti dari stdindan stdout. Mereka tidak ada hubungannya dengan apa yang tampak di terminal Anda; sebaliknya, Anda harus menafsirkannya sehubungan dengan proses tertentu (pada dasarnya, sebuah program). Input standar ("stdin") untuk suatu proses adalah sumber dari mana proses dapat membaca data, dan output standar proses ("stdout") adalah tujuan di mana ia dapat menulis data. Sumber dan tujuan ini bisa berupa program lain, atau file, atau terminal itu sendiri (barang yang Anda ketikkan masuk ke stdin, dan apa pun yang datang ke stdout ditampilkan).
David Z
3

Itu gagal karena Anda mem-pipkan output echo "hi", yang hanya hiuntuk at, tetapi atmengharapkan input itu dapat dieksekusi oleh shell sistem default (biasanya bash, tetapi kadang-kadang sesuatu yang berbeda tergantung pada sistem).

Ini:

at 1m << EOF
echo "hi"
EOF

akan mencapai apa yang Anda coba lakukan. Ini menggunakan shell konstruksi yang disebut 'di sini dokumen', yang umumnya merupakan cara yang diterima untuk melakukan hal semacam ini (karena menangani beberapa baris lebih baik daripada menggunakan echoperintah dan tidak perlu membuat file baru seperti yang Anda perlukan dengan cat), dan menerjemahkan ke padanan yang agak lebih rumit menggunakan echo:

echo 'echo "hi"' | at 1m

Mungkin perlu dicatat bahwa atakan mengirimkan perintah-output apa pun kepada pengguna yang memanggil atperintah, alih-alih memutar-mutar output ke terminal seperti yang ditunda perintah sleepdalam subkulit. Secara khusus, ini berarti bahwa kecuali Anda telah melakukan kustomisasi sistem, atau bermaksud membaca spool mail lokal Anda, Anda tidak akan bisa mendapatkan output dari perintah yang dijalankan at.

Austin Hemmelgarn
sumber
Untuk beberapa alasan, kedua contoh yang Anda berikan memberi saya kesalahan syntax error. Last token seen: m Garbled time . Saya tidak tahu kenapa. Saya menggunakan Ubuntu 16.04, Bash 4.0. Mungkin Anda melakukannya di sistem lain. Bahkan ketika saya mengetik attanpa argumen lebih lanjut --- saya masih mendapatkan kesalahan yang sama. Lebih banyak data di sini .
Arcticooling
1
at 1mtidak berfungsi untuk POSIX yang kompatibel at. Perintahnya adalahat now + 1 minute
PiedPiper
@ PiedPiper benar, 1m tidak kompatibel dengan versi yang sesuai dengan POSIX at, saya hanya berasumsi bahwa Anda memiliki versi yang menerima sintaks itu.
Austin Hemmelgarn
@PiedPiper, at 1mbukan POSIX, tetapi implementasi yang kompatibel dengan POSIX atbebas untuk menafsirkannya seperti yang mereka inginkan, itu tidak membuat atimplementasi kurang patuh untuk menafsirkannya sebagai makna yang sama dengan now + 1 minutemengembalikan kesalahan atau makna satu bulan yang lalu . TKI, itu at 1mkode yang bukan POSIX.
Stéphane Chazelas
2

Kombinasikan jawaban @Peter Cordes dan jawaban ini /programming/23125527/how-to-return-to-bash-prompt-after-printing-output-from-backgrounded-function/23125687#23125687

Kode di bawah ini dari jawaban yang terakhir, dengan sedikit perubahan milik saya. Kompilasi untuk mengajukan a.out (nama apa pun yang Anda suka)

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
        char *tty = "/dev/tty";
        if (argc > 1)
                tty = argv[1];
        int fd = open(tty, O_WRONLY);

        int i;
        char buf[] = "\n";
        for (i = 0; i < sizeof buf - 1; i++)
                if (ioctl(fd, TIOCSTI, &buf[i]))
                        perror("ioctl");
        close(fd);
        return 0;
}

Kemudian jalankan perintah di bawah ini. (Saya meletakkan a.out di dir / tmp /, Anda mungkin perlu mengubah milik Anda)

(sleep 2 && echo -e '\nhi' && /tmp/a.out &)

atau

(sleep 2 && echo -e '\nhi' && /tmp/a.out /proc/$$/fd/0 &)

BTW, kill -WINCH $$bekerja dengan baik untuk saya dengan Bash 4.3 di Gentoo.

Bruce
sumber
1

Membangun berdasarkan jawaban di atas, Anda dapat memiliki perintah tertanam langsung outputnya ke terminal Anda, seperti:

echo "echo hi >$(tty); kill -WINCH $$" | at now + 1 minute

The ttyperintah mengembalikan jalur perangkat terminal Anda saat ini, melampirkan dalam $(...)memiliki pesta melaksanakannya dalam sub-shell, dan perintah kill mengirimkan sinyal ke proses saat ini yang akan memiliki pesta ulang mencetak $ PS1 nya cepat.

pengguna1404316
sumber