mach irgendwann mal was (und zeige vielleicht auch das ergebnis in der konsole)

12

Ich verwende Ubuntu Server 16.04 und atmöchte das Dienstprogramm in meiner aktuellen Sitzung verwenden, um in einer Minute etwas zu tun (sagen wir eine echo), ohne ein bestimmtes Datum und eine bestimmte Uhrzeit anzugeben - nur eine Minute vor der aktuellen Uhrzeit.

Dies ist fehlgeschlagen:

echo 'hi' | at 1m

Der Grund , warum ich wähle atüber sleep, weil Schlaf Handicaps aktuelle Sitzung und ist in einer anderen Sitzung, anstatt die, die wir Arbeit mit den meisten Zeit zu Verzögerungsbefehlen dafür besser geeignet. AFAIR atverhält sich nicht so und behindert meine Sitzung nicht.

Update_1

Nach der Antwort von Pied Piper habe ich versucht:

(sleep 1m; echo 'hi')  &

Ich habe ein Problem mit dieser Methode: Der Stream "hi" wird in meiner primären Eingabeaufforderung gedruckt und fügt außerdem eine leere sekundäre Eingabeaufforderung ( _) direkt unter der primären Eingabeaufforderung hinzu, die ihn enthält. Siehe:

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

Update_2

Nach der Antwort von Peter Corde habe ich versucht:

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

Dies funktioniert anscheinend in Bash 4.4 einwandfrei, in einigen älteren Versionen jedoch nicht (siehe Kommentare in der Antwort). Ich persönlich benutze Bash 4.3 in meinen eigenen Umgebungen.

Arcticooling
quelle
2
Ich wünschte, die Kill-Flags wären GNU-artig und könnten neu angeordnet werden, damit es nicht wie "Kill the wench" klingt!
NH.

Antworten:

6

Der "Hallo" -Stream wird in meiner Eingabeaufforderung (in der stdin) und nicht in meiner gedrucktstdout

Nein, es ist nicht "gedruckt in Ihrem stdin".

Wenn Sie die Eingabetaste drücken, wird nicht versucht, den hiBefehl auszuführen . Es ist nur so, dass der Hintergrund echogedruckt wurde, hiwährend sich der Cursor hinter Ihrer Eingabeaufforderung befand.


Vielleicht möchten Sie eine führende Zeile drucken , wie z (sleep 1m && echo -e '\nhi' &). (Passen Sie nach Bedarf die echoin Ihrer Shell an. Oder verwenden Sie sie printffür die Portabilität.)

Ich habe die &Innenseite der Unterschale so eingestellt, dass der Hintergrundjob "verleugnet" wird, damit Sie auch das "Rauschen" von vermeiden [1]+ Done. Wird &&zwischen den Befehlen &für die gesamte zusammengesetzte Befehlskette angewendet, sodass sofort zur Shell-Eingabeaufforderung zurückgekehrt wird, anstatt auf sleepdas Beenden zu warten , wie Sie es gewohnt sind(sleep 1; echo ... &)

Folgendes passiert (mit einer Verzögerung von 1 Sekunde):

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

Bash weiß nicht, dass echoetwas gedruckt wurde, daher muss die Eingabeaufforderung nicht erneut gedruckt oder der Bildschirm aufgeräumt werden. Sie können das manuell mit tun control-L.

Sie können eine Shell-Funktion schreiben, die bash abruft, um die Eingabeaufforderung nach echoBeendigung erneut zu drucken . Wenn Sie dies tun, echo "$PS1"werden keine bereits eingegebenen Zeichen reproduziert. kill -WINCH $$Auf meinem System wird Bash angewiesen, die Eingabeaufforderungszeile erneut zu drucken, ohne den Bildschirm zu hilöschen. Vor der Eingabeaufforderung wird eine eigene Zeile angezeigt. SIGWINCH wird automatisch gesendet, wenn sich die Größe von WINdow ändert, und die Antwort von bash darauf geschieht, was wir wollen.

Es könnte mit anderen Shells funktionieren, aber ich versuche nicht, diese zu 100% portabel / POSIX zu machen. ( Leider funktioniert dies nur mit bash4.4, nicht mit bash4.3, oder hängt von einer Einstellung ab, die mir nicht bekannt ist. )

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

Sie können dies leicht in eine Shell-Funktion einschließen, die ein Schlafargument und eine Nachricht entgegennimmt.

bash 4.3 druckt seine Eingabeaufforderung nach SIGWINCH nicht erneut, daher funktioniert dies dort nicht. Ich habe shopt -s checkwinsizeauf beiden Systemen aktiviert, aber es funktioniert nur auf dem Bash 4.4-System (Arch Linux).

Wenn es nicht funktioniert, können Sie versuchen (sleep 2 && echo -e '\nhi' && echo "$PS1" &), was "funktioniert", wenn die Befehlszeile leer ist. (Es ignoriert auch die Möglichkeit, die festgelegt PROMPT_COMMANDist.)


Es gibt keine wirklich saubere Möglichkeit, die Terminalausgabe mit dem zu mischen, was Sie später auf Ihrem Terminal tun, wenn der Timer ausgelöst wird. Wenn Sie gerade einen Bildlauf durchführen less, können Sie ihn leicht übersehen.

Wenn Sie nach etwas mit interaktiven Ergebnissen suchen, empfehle ich Ihnen, eine Audiodatei abzuspielen. zB mit einem Kommandozeilen-Player wie mpv(nette Abzweigung von MPlayer2). Oder wählen Sie eine Videodatei aus, um ein neues Fenster zu öffnen.

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

Vielleicht möchten Sie echoein neues xterm / konsole / gnome-terminal öffnen. Verwenden Sie eine Option, mit der das Terminal nach dem Beenden des Befehls geöffnet bleibt.

Peter Cordes
quelle
Ich danke dir, lieber David, und ich bin durchgedreht. Das ist wirklich das Nächste, was ich in Bezug auf das, was ich suche, erreicht habe. Gibt es eine Möglichkeit, den folgenden Befehl zu aktualisieren, um ein EnterTastendruckereignis zu emulieren , direkt nachdem das Echo angezeigt wurde, sodass ich automatisch zur primären Eingabeaufforderung der Konsole zurückkehren würde ? (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &).
Arcticooling
@Arcticooling: kill -WINCH $$ nicht aktualisieren , um die prompte der Schale in dem Endgerät ausgeführt wird . Das war der ganze Sinn, es zu benutzen. Wenn Sie die Eingabetaste drücken, wird ein teilweise eingegebener Befehl gesendet, WINCH jedoch nicht, wie ich gezeigt habe. Wenn Sie nichts eingegeben haben, wird eine leere Eingabeaufforderung angezeigt.
Peter Cordes
1
Wenn Sie mit der rm -rf ~/some/directoryEingabe begonnen hätten, würde die Eingabe möglicherweise zur falschen Zeit ausgeführt rm -rf ~/. (Sicherheits - Tipp: Wegen Finish eingeben und dann steuern-a und Änderung lszu rm -rf, so fett fingern jederzeit Geben Sie nicht den Tag verderben.)
Peter Cordes
Wired, ich habe ausgeführt (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)und nachdem das Echo erschienen ist, habe ich eine leere Eingabeaufforderung erhalten, erst nachdem Enterich auf die primäre Eingabeaufforderung zurückgekehrt bin ... Entschuldigung, wenn ich Sie falsch verstehe, bin ich ziemlich neu bei Bash.
Arcticooling
@Arcticooling: Ich verwende Bash 4.4 unter Arch Linux in einem Konsole-Terminal-Emulator. Ich hoffte, dass mein Befehl zumindest für andere Versionen von bash portierbar sein würde, aber vielleicht nicht :( Ja, ich habe ihn gerade in Bash 4.3 in einer screenSitzung (und nur über SSH) auf einem älteren Ubuntu-System getestet , und ich habe das von Ihnen beschriebene Verhalten erhalten.
Peter Cordes
13

Die korrekte Verwendung ist at <timespec>, wo <timespec>ist die Zeitangabe. Sie können die -fOption auch verwenden , um die Datei anzugeben, die die auszuführenden Befehle enthält. Wenn -fund keine Umleitung verwendet wird, liest at die auszuführenden Befehle aus der Standardeingabe.

Um in Ihrem Beispiel "some command" auszuführen (im folgenden Beispiel habe ich "some command" als "echo hi" ausgewählt), ist eine Minute nach dem Drücken von enter (now) die beabsichtigte <timespec>Verwendung now + 1 minute. Verwenden Sie den folgenden Befehl, um das gewünschte Ergebnis mit einer Ein-Linien-Lösung zu erzielen:

echo 'echo hi' | at now + 1 minute

Dies soll (und wird) den echo hiBefehl nach einer Minute ausführen . Das Problem hierbei ist, dass der echo hiBefehl vom Befehl at in einem Dummy-Tty ausgeführt wird und die Ausgabe an die Mailbox des Benutzers gesendet wird (sofern diese korrekt konfiguriert ist - was eine ausführlichere Erläuterung zum Konfigurieren einer Mailbox in erfordert eine Unix-Maschine und gehört nicht zum Umfang dieser Frage). Die Quintessenz ist, dass Sie das Ergebnis von nicht direkt echo hiin demselben Terminal sehen können, in dem Sie den Befehl ausgegeben haben. Die einfachste Alternative ist, Sie leiten es zu "irgendeiner Datei" um, zum Beispiel:

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

Im obigen Beispiel ist "some file" /tmp/at.outund das erwartete Ergebnis ist, dass die Datei at.outunter / tmp nach einer Minute erstellt wird und den Text "hi" enthält.

Zusätzlich zu dem now + 1 minuteobigen Beispiel können viele andere Zeitangaben verwendet werden, auch einige komplexe. Der Befehl at ist intelligent genug, um für Menschen lesbare Befehle wie die folgenden zu interpretieren:

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

Weitere Informationen zu den möglichen Zeitangaben finden Sie auf der Manpage von at, da die möglichen Abweichungen sehr umfangreich sind und Daten, relative oder absolute Zeiten usw. enthalten können.

Marcelo
quelle
2
atStandardmäßig wird die Ausgabe per E-Mail gesendet, dies muss jedoch ordnungsgemäß konfiguriert sein, um zu funktionieren.
Simon Richter
Ich biete jetzt 100 Wiederholungsprämie für eine Antwort an. Bitte lies meine Kopfgeldnachricht unten und bearbeite sie, um mehr darüber zu at <timespec>erfahren, was ist und welches Flag du benutzt hast und ob es zu Ubuntu 16.04 xenial passt.
Arcticooling
Ok, ich habe den Detaillierungsgrad in der Antwort erhöht, damit es jetzt didaktischer ist. Es klingt für manche vielleicht etwas langatmig, aber ich glaube, es lässt keinen Raum für Zweifel, da es klar Beispiele nennt, die funktionieren und wie sie auch funktionieren.
Marcelo
7

sleepFühre das in einer Subshell aus:

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

Hinweis: Unter Linux können Sie das Argument für den Ruhezustand in Sekunden oder mit dem Suffix s / m / h / d (für Sekunden, Minuten, Stunden, Tage) angeben. Bei BSD muss das Argument in Sekunden angegeben werden

Rattenfänger
quelle
Ich habe ein kleines Problem mit dieser Methode - der "hi" -Stream wird in meiner Eingabeaufforderung (in der stdin) und nicht in meiner stdout(wie bei einer regulären echo) ausgegeben .
Arcticooling
1
Wenn Sie nicht möchten, dass die Ausgabe in Ihrem Terminal mit Eingabeaufforderungen und der Ausgabe anderer Befehle
verwechselt
Diese Umleitung ist bei @PiedPiper fehlgeschlagen (sleep 10s; echo 'hi') & 1>. Ich lese jetzt mehr dazu.
Arcticooling
2
Das Lesen der Dokumentation ist immer eine gute Idee :)
PiedPiper
1
@Arcticooling Sie scheinen verwirrt über die Bedeutung von stdinund zu sein stdout. Sie haben nichts damit zu tun, wo Dinge auf Ihrem Terminal erscheinen. Stattdessen sollten Sie sie in Bezug auf einen bestimmten Prozess (im Grunde genommen ein Programm) interpretieren . Die Standardeingabe ("stdin") für einen Prozess ist eine Quelle, von der der Prozess Daten lesen kann, und die Standardausgabe des Prozesses ("stdout") ist ein Ziel, in das Daten geschrieben werden können. Diese Quellen und Ziele können andere Programme oder Dateien oder das Terminal selbst sein (von Ihnen eingegebenes Material wird in stdin gespeichert und alles, was in stdout enthalten ist, wird angezeigt).
David Z
3

Das ist fehlgeschlagen, weil Sie die Ausgabe von weiterleiten echo "hi", was nur hidazu dient at, aber aterwartet, dass die Eingabe von der Standard-System-Shell ausgeführt werden kann (normalerweise bash, aber manchmal etwas anderes, je nach System).

Dies:

at 1m << EOF
echo "hi"
EOF

wird erreichen, was Sie versuchen zu tun scheinen. Es wird ein Shell-Konstrukt namens "here document" verwendet, was im Allgemeinen die akzeptierte Methode ist, um diese Art von Dingen auszuführen (da es mehrere Zeilen besser verarbeitet als das Verwenden des echoBefehls und keine neue Datei erstellt werden muss, wie Sie es benötigen würden) cat) und übersetzt in das etwas kompliziertere Äquivalent mit echo:

echo 'echo "hi"' | at 1m

Es ist wahrscheinlich erwähnenswert, dass ateine Befehlsausgabe per E-Mail an den Benutzer atgesendet wird, der den Befehl aufgerufen hat , anstatt die Ausgabe an das Terminal als einen Befehl zu senden, dessen Verwendung sleepin einer Subshell verzögert wird . Dies bedeutet insbesondere, dass Sie die Ausgabe von Befehlen nur über ausführen können, wenn Sie das System angepasst haben oder beabsichtigen, Ihre lokale Mail-Spool zu lesen at.

Austin Hemmelgarn
quelle
Aus irgendeinem Grund haben mir beide von Ihnen angegebenen Beispiele einen Fehler gemeldet syntax error. Last token seen: m Garbled time . Ich habe keine Idee warum. Ich benutze Ubuntu 16.04, Bash 4.0. Vielleicht haben Sie das auf einem anderen System gemacht. Selbst wenn ich nur atohne weitere Argumente eingebe, erhalte ich immer noch den gleichen Fehler. Weitere Daten hier .
Arcticooling
1
at 1mfunktioniert nicht für eine posix-kompatible at. Der Befehl müsste seinat now + 1 minute
PiedPiper
@PiedPiper ist korrekt, 1m ist nicht kompatibel mit POSIX-kompatiblen Versionen von at. Ich habe nur angenommen, dass Sie eine Version haben, die diese Syntax akzeptiert.
Austin Hemmelgarn
@PiedPiper at 1mist kein POSIX- System, aber es steht POSIX-kompatiblen Implementierungen atfrei, sie so zu interpretieren, wie sie möchten. Es macht eine atImplementierung nicht weniger konform, wenn sie so interpretiert wird, now + 1 minuteals würde sie einen Fehler zurückgeben oder vor einem Monat bedeuten . IOW, es ist der at 1mCode, der nicht POSIX ist.
Stéphane Chazelas
2

Kombinieren Sie die Antwort von @Peter Cordes und diese Antwort /programming/23125527/how-to-return-to-bash-prompt-after-printing-output-from-backgrounded-function/23125687#23125687

Der folgende Code stammt aus der letzten Antwort, mit einer kleinen Änderung von mir. Kompiliere es, um a.out abzulegen (welcher Name auch immer du willst)

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

Führen Sie dann den folgenden Befehl aus. (Ich stelle a.out in dir / tmp /, Sie müssen möglicherweise zu Ihrem wechseln.)

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

oder

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

BTW, kill -WINCH $$funktioniert gut für mich mit Bash 4.3 auf Gentoo.

Bruce
quelle
1

Aufbauend auf den obigen Antworten können Sie den eingebetteten Befehl so auf Ihr Terminal ausgeben lassen:

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

Der ttyBefehl gibt den Gerätepfad Ihres aktuellen Terminals zurück. Wenn Sie ihn in " $(...)Bash" einschließen , wird er in einer Sub-Shell ausgeführt. Der Befehl "Kill" sendet ein Signal an den aktuellen Prozess, bei dem Bash die Aufforderung "$ PS1" erneut ausgibt.

user1404316
quelle