đôi khi từ bây giờ hãy làm gì đó (và cũng có thể hiển thị kết quả trong bảng điều khiển)


12

Tôi sử dụng máy chủ Ubuntu 16.04 và tôi muốn sử dụng tiện ích attrong phiên hiện tại của mình để thực hiện điều gì đó 1 phút kể từ bây giờ (giả sử echo), mà không đưa ra ngày giờ cụ thể - chỉ trước 1 phút so với thời gian hiện tại.

Điều này thất bại:

echo 'hi' | at 1m

Lý do tôi chọn atqua sleeplà vì tật ngủ phiên hiện tại và là do phù hợp hơn để lệnh chậm trễ trong phiên khác, chứ không phải là công việc mà chúng ta với hầu hết thời gian. AFAIR, atkhông hành xử theo cách này và sẽ không chấp nhận phiên của tôi.

Cập nhật_1

Theo câu trả lời của Pi Piper, tôi đã thử:

(sleep 1m; echo 'hi')  &

Tôi có một vấn đề với phương pháp này: Luồng "hi" được in bên trong dấu nhắc chính của tôi và cũng thêm một dấu nhắc phụ trống ( _) ngay dưới dấu nhắc chính có chứa nó, xem:

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

Cập nhật_2

Bởi câu trả lời của Peter Corde tôi đã thử:

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

Điều này hoạt động đúng trong Bash 4.4, nhưng dường như không phải trong một số phiên bản cũ hơn (xem bình luận trong câu trả lời). Cá nhân tôi sử dụng Bash 4.3 trong môi trường của riêng tôi.


2
Tôi ước các cờ kill là kiểu GNU và có thể được sắp xếp lại để nó không giống như "kill the wench"!
NH.

Câu trả lời:


6

Luồng "hi" được in trong lời nhắc của tôi (trong phần stdin ) và không phải trongstdout

Không, nó không phải là "in trong của bạn stdin".

Nếu bạn nhấn return, nó sẽ không chạy hinhư một lệnh. Chỉ là nền tảngecho được in hitrong khi con trỏ theo dấu nhắc của bạn.


Có lẽ bạn muốn in một dòng mới hàng đầu , như(sleep 1m && echo -e '\nhi' &) . (Điều chỉnh khi cần thiết cho echovỏ của bạn. Hoặc sử dụng printfcho tính di động.)

Tôi đặt phần &bên trong lớp con để "từ chối" công việc nền, vì vậy bạn cũng tránh được "tiếng ồn" [1]+ Done. Với &&giữa các lệnh, &áp dụng cho toàn bộ chuỗi lệnh ghép, do đó, nó sẽ quay trở lại dấu nhắc shell ngay lập tức thay vì chờ sleepthoát như bạn nhận được với(sleep 1; echo ... &)

Đây là những gì xảy ra (với độ trễ 1 giây):

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

Bash không biết rằng đã echoin một cái gì đó, vì vậy nó không biết nó cần in lại lời nhắc của nó hoặc làm sạch màn hình. Bạn có thể làm điều đó bằng tay với control-L.

Bạn có thể viết một hàm shell được bash để in lại dấu nhắc của nó sau khi echokết thúc . Chỉ cần làm echo "$PS1"sẽ không tái tạo bất kỳ nhân vật đã gõ. kill -WINCH $$trên hệ thống của tôi được bash để in lại dòng nhắc của nó mà không xóa màn hình, để lại himột dòng trước dấu nhắc. SIGWINCH được gửi tự động khi kích thước WINdow thay đổi và phản ứng của bash đối với nó xảy ra để làm những gì chúng ta muốn.

Nó có thể hoạt động với các shell khác, nhưng tôi không cố gắng thực hiện 100% xách tay / POSIX này. ( Thật không may, điều này chỉ hoạt động trên bash4.4, không phải bash4.3 hoặc phụ thuộc vào một số cài đặt mà tôi không biết )

  # 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`

Bạn có thể dễ dàng gói nó trong một hàm shell có chức năng ngủ và tin nhắn.

bash 4.3 không in lại lời nhắc của nó sau SIGWINCH, vì vậy điều này không hoạt động ở đó. Tôi cóshopt -s checkwinsize kích hoạt trên cả hai hệ thống, nhưng nó chỉ hoạt động trên hệ thống Bash 4.4 (Arch Linux).

Nếu nó không hoạt động, bạn có thể thử (sleep 2 && echo -e '\nhi' && echo "$PS1" &), "hoạt động" nếu dòng lệnh trống. (Nó cũng bỏ qua khả năng PROMPT_COMMANDđược đặt.)


Không có cách nào thực sự sạch để có được đầu ra đầu cuối để trộn với bất cứ điều gì bạn có thể làm trên thiết bị đầu cuối của mình sau đó, khi bộ hẹn giờ kích hoạt. Nếu bạn đang ở giữa cuộn một cái gì đó trongless , bạn có thể dễ dàng bỏ lỡ nó.

Nếu bạn đang tìm kiếm thứ gì đó có kết quả tương tác, tôi khuyên bạn nên phát tệp âm thanh. ví dụ với một trình phát dòng lệnh như mpv(fork đẹp của MPlayer2). Hoặc chọn một tệp video để một cửa sổ mới mở ra.

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

Bạn có thể muốn echomở một xterm / konsole / gnome-terminal mới. Sử dụng một tùy chọn giữ cho thiết bị đầu cuối mở sau khi thoát lệnh.


Tôi cảm ơn David thân mến, và tôi đã đưa ngón tay cái lên. Đây thực sự là gần nhất tôi có được về những gì tôi tìm kiếm. Có cách nào để nâng cấp lệnh sau để mô phỏng Entersự kiện nhấn phím, ngay sau khi tiếng vang xuất hiện, vì vậy tôi sẽ tự động được đưa trở lại vào dấu nhắc chính của giao diện điều khiển ? (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &).
Bắc cực

@Arcticooling: kill -WINCH $$ không làm mới dấu nhắc của shell đang chạy trong terminal. Đó là toàn bộ điểm của việc sử dụng nó. Nhấn enter sẽ gửi lệnh được gõ một phần, nhưng WINCH không, như tôi đã trình bày. Nếu bạn chưa gõ gì, thì bạn sẽ nhận được một dấu nhắc trống.
Peter Cordes

1
Nếu bạn đã bắt đầu nhập rm -rf ~/some/directory, Enter không đúng lúc sẽ có khả năng chạy rm -rf ~/. (Mẹo an toàn: kết thúc việc nhập các đường dẫn và sau đó kiểm soát-a và thay đổi lsthành rm -rf, vì vậy, ngón tay mập vào bất cứ lúc nào sẽ không làm hỏng ngày của bạn.)
Peter Cordes

Có dây, tôi đã thực thi (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)và sau khi tiếng vang xuất hiện, tôi nhận được một dấu nhắc trống, chỉ sau khi nhấn Entertôi mới quay lại dấu nhắc chính ... Xin lỗi nếu tôi hiểu nhầm bạn, tôi khá mới với Bash.
Bắc cực

@Arcticooling: Tôi đang sử dụng Bash 4.4 trên Arch Linux, trong trình giả lập thiết bị đầu cuối Konsole. Tôi hy vọng lệnh của tôi sẽ có thể di động ít nhất với các phiên bản bash khác, nhưng có thể không :( Vâng, chỉ được thử nghiệm trong Bash 4.3 trong một screenphiên (và chỉ thông qua SSH) trên hệ thống Ubuntu cũ hơn và tôi đã có hành vi mà bạn mô tả.
Peter Cordes

13

Các chính xác tại sử dụng là at <timespec>, <timespec>đặc điểm kỹ thuật thời gian ở đâu. Bạn cũng có thể sử dụng -ftùy chọn để chỉ định tệp chứa các lệnh để thực thi. Nếu -fvà không sử dụng chuyển hướng, sẽ đọc các lệnh để thực thi từ stdin (đầu vào tiêu chuẩn).

Trong ví dụ của bạn, để thực thi "một số lệnh" (tôi đã chọn "một số lệnh" là "echo hi" trong ví dụ bên dưới) một phút ngay sau khi bạn nhấn enter (ngay bây giờ), dự định <timespec>sẽ được sử dụng now + 1 minute. Vì vậy, để có được kết quả bạn muốn với giải pháp một lớp, vui lòng sử dụng lệnh dưới đây:

echo 'echo hi' | at now + 1 minute

Điều này được cho là (và sẽ làm) để chạy echo hilệnh sau một phút. Vấn đề ở đây là echo hilệnh sẽ được thực thi bởi lệnh at trong một tummy giả và đầu ra sẽ được gửi đến hộp thư của người dùng (nếu nó được cấu hình đúng - yêu cầu giải thích rộng hơn về cách định cấu hình hộp thư trong một máy unix và không phải là một phần của phạm vi của câu hỏi này). Điểm mấu chốt là bạn sẽ không thể nhìn thấy trực tiếp kết quả của echo hicùng một thiết bị đầu cuối mà bạn đã ban hành lệnh. Cách thay thế đơn giản nhất là bạn chuyển hướng nó đến "một số tệp", ví dụ:

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

Trong ví dụ trên, "một số tệp" là /tmp/at.outvà kết quả mong đợi là tệp at.outdưới / tmp được tạo sau một phút và hiển thị văn bản 'hi'.

Ngoài now + 1 minuteví dụ trên, nhiều thông số kỹ thuật thời gian khác có thể được sử dụng, thậm chí một số thông số phức tạp. Lệnh at đủ thông minh để diễn giải những cái khá dễ đọc của con người như các ví dụ dưới đây:

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

Vui lòng tham khảo tại trang man của để biết thêm về các thông số kỹ thuật về thời gian có thể vì các biến thể có thể có khá rộng và có thể bao gồm ngày, thời gian tương đối hoặc thời gian tuyệt đối, v.v.


2
attheo mặc định sẽ gửi đầu ra qua thư, nhưng điều này cần phải được cấu hình đúng để hoạt động.
Simon Richter

Bây giờ tôi cung cấp 100 tiền thưởng cho một câu trả lời. Vui lòng đọc tin nhắn tiền thưởng của tôi dưới đây và chỉnh sửa để giải thích thêm về những gì at <timespec>và cờ bạn đã sử dụng và nếu nó phù hợp với xenial Ubuntu 16.04.
Bắc cực

Ok, tôi đã tăng mức độ chi tiết trong câu trả lời để bây giờ nó mang tính mô phạm hơn. Nó có vẻ hơi dài dòng đối với một số người, nhưng tôi tin rằng nó không để lại nghi ngờ vì nó trích dẫn rõ ràng các ví dụ hoạt động và cách chúng hoạt động tốt.
Marcelo

7

Chạy sleeptrong một subshell:

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

Lưu ý: trên Linux, bạn có thể cho đối số ngủ trong vài giây hoặc với hậu tố s / m / h / d (trong vài giây, phút, giờ, ngày). Trên BSD, đối số phải được tính bằng giây


Tôi có một vấn đề nhỏ với phương pháp này --- luồng "hi" được in trong lời nhắc của tôi (trong stdin) và không phải trong stdout(như với thông thường echo).
Bắc cực

1
Nếu bạn không muốn đầu ra trong thiết bị đầu cuối của mình trộn lẫn với các lời nhắc và đầu ra của các lệnh khác, bạn sẽ phải chuyển hướng nó đến một nơi nào đó
PiedPiper

Chuyển hướng này đã thất bại @PiedPiper (sleep 10s; echo 'hi') & 1>. Bây giờ tôi đọc thêm về điều này.
Bắc cực

2
Đọc tài liệu luôn là một ý tưởng hay :)
PiedPiper

1
@Arcticooling Có vẻ như bạn bối rối về ý nghĩa của stdinstdout. Họ không có gì để làm với nơi mọi thứ xuất hiện trên thiết bị đầu cuối của bạn; thay vào đó, bạn nên diễn giải chúng theo một quy trình cụ thể (về cơ bản, một chương trình). Đầu vào tiêu chuẩn ("stdin") cho một quy trình là nguồn mà quá trình có thể đọc dữ liệu và đầu ra tiêu chuẩn của quy trình ("stdout") là đích đến mà nó có thể ghi dữ liệu. Các nguồn và đích này có thể là các chương trình hoặc tệp khác hoặc chính thiết bị đầu cuối (nội dung bạn nhập vào stdin và mọi thứ xuất hiện trên thiết bị xuất chuẩn đều được hiển thị).
David Z

3

Đó là thất bại bởi vì bạn đang đường ống đầu ra của echo "hi", mà chỉ là hiđể at, nhưng athy vọng rằng nó đầu vào có thể được thực hiện bởi vỏ hệ thống mặc định (thường là bash, nhưng đôi khi một cái gì đó khác nhau tùy thuộc vào hệ thống).

Điều này:

at 1m << EOF
echo "hi"
EOF

sẽ đạt được những gì bạn dường như đang cố gắng làm. Nó sử dụng cấu trúc shell được gọi là 'tài liệu ở đây', nói chung là cách được chấp nhận để thực hiện loại điều này (vì nó xử lý nhiều dòng tốt hơn so với sử dụng echolệnh và không yêu cầu tạo tệp mới như bạn cần cat) và dịch sang tương đương phức tạp hơn bằng cách sử dụng echo:

echo 'echo "hi"' | at 1m

Có lẽ đáng lưu ý rằng atsẽ gửi bất kỳ đầu ra lệnh nào cho người dùng đã gọi atlệnh, thay vì kết nối đầu ra tới thiết bị đầu cuối như một lệnh bị trì hoãn sử dụng sleeptrong một mạng con. Cụ thể, điều này có nghĩa là trừ khi bạn thực hiện một số tùy chỉnh của hệ thống hoặc có ý định đọc bộ đệm thư cục bộ, bạn sẽ không thể nhận được đầu ra của các lệnh chạy qua at.


Vì một số lý do, cả hai ví dụ bạn đưa ra đều cho tôi một lỗi syntax error. Last token seen: m Garbled time . Tôi không biết tại sao. Tôi sử dụng Ubuntu 16.04, Bash 4.0. Có lẽ bạn đã làm điều đó trên một hệ thống khác. Ngay cả khi tôi gõ atmà không cần thêm bất kỳ đối số nào --- tôi vẫn gặp lỗi tương tự. Thêm dữ liệu ở đây .
Bắc cực

1
at 1mkhông hoạt động cho một posix tương thích at. Lệnh sẽ cần phải làat now + 1 minute
PiedPiper

@PiedPiper là chính xác, 1m không tương thích với các phiên bản tuân thủ POSIX at, tôi chỉ giả sử rằng bạn có phiên bản chấp nhận cú pháp đó.
Austin Hemmelgarn

@PiedPiper, at 1mkhông phải là POSIX, nhưng các triển khai tương thích POSIX atcó thể tự do diễn giải nó theo ý muốn, nó không làm cho atviệc triển khai ít tuân thủ để diễn giải nó có nghĩa giống như now + 1 minutetrả lại lỗi hoặc có nghĩa là một tháng trước . IOW, đó là at 1mmã không phải là POSIX.
Stéphane Chazelas

2

Kết hợp câu trả lời của @Peter Cordes và câu trả lời này /programming/23125527/how-to-return-to-bash-prompt-after-printing-output-from-backgrounded-feft/23125687#23125687

Mã dưới đây là từ câu trả lời sau, với một chút thay đổi của tôi. Biên dịch nó để gửi a.out (bất kỳ tên nào bạn thích)

#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;
}

Sau đó chạy lệnh dưới đây. (Tôi đặt a.out trong dir / tmp /, bạn có thể cần thay đổi thành của bạn)

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

hoặc là

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

BTW, kill -WINCH $$hoạt động tốt với tôi với Bash 4.3 trên Gentoo.


1

Dựa trên các câu trả lời ở trên, bạn có thể có lệnh nhúng hướng đầu ra của nó tới thiết bị đầu cuối của bạn, như vậy:

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

Các ttylệnh trả về đường dần đến device của thiết bị đầu cuối hiện tại của bạn, kèm theo nó trong $(...)có bash thực hiện nó trong một sub-vỏ, và lệnh kill gửi tín hiệu đến quá trình hiện tại mà sẽ có bash tái print $ PS1 của nó nhanh chóng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.