à un moment donné, faire quelque chose (et peut-être aussi afficher le résultat dans la console)

12

J'utilise le serveur Ubuntu 16.04 et je souhaite utiliser l'utilitaire atdans ma session actuelle pour faire quelque chose dans 1 minute (disons, an echo), sans donner de date et d'heure spécifiques - à seulement 1 minute d'avance sur l'heure actuelle.

Cela a échoué:

echo 'hi' | at 1m

La raison pour laquelle je choisis atplus sleepest parce que les handicaps du sommeil session et est plus adapté à cet effet aux commandes de retard dans une autre session, plutôt que le travail d' un nous avec la plupart du temps. AFAIR, atne se comporte pas ainsi et ne handicapera pas ma séance.

Update_1

Par la réponse de Pied Piper, j'ai essayé:

(sleep 1m; echo 'hi')  &

J'ai un problème avec cette méthode: le flux "hi" est imprimé dans mon invite principale et ajoute également une invite secondaire vide ( _) juste sous l'invite principale qui le contient, voir:

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

Update_2

Par la réponse de Peter Corde, j'ai essayé:

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

Cela fonctionne correctement dans Bash 4.4, mais pas dans certaines anciennes versions, apparemment (voir les commentaires dans la réponse). J'utilise personnellement Bash 4.3 dans mes propres environnements.

Arcticooling
la source
2
Je souhaite que les drapeaux de mise à mort soient de style GNU et puissent être réorganisés afin que cela ne ressemble pas à "tuer la garce"!
NH.

Réponses:

6

Le flux "salut" est imprimé dans mon invite (dans le stdin) et non dans monstdout

Non, ce n'est pas "imprimé dans votre stdin".

Si vous appuyez sur retour, il n'essaiera pas de s'exécuter en hitant que commande. C'est juste que l'arrière-plan echoimprimé hitandis que le curseur était après votre invite.


Vous souhaitez peut-être imprimer une nouvelle ligne de tête , comme (sleep 1m && echo -e '\nhi' &). (Ajustez si nécessaire pour le echodans votre coque. Ou utilisez printfpour la portabilité.)

J'ai mis l' &intérieur du sous-shell pour "renier" le travail en arrière-plan, donc vous évitez également le "bruit" de [1]+ Done. Avec &&entre les commandes, &s'applique à l'ensemble de la chaîne de commandes composées, de sorte qu'il revient immédiatement à l'invite du shell au lieu d'attendre la sleepsortie comme vous le feriez avec(sleep 1; echo ... &)

Voici ce qui se passe (avec 1 seconde de retard):

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

Bash ne sait pas que echoquelque chose a été imprimé, il ne sait donc pas qu'il doit réimprimer son invite ou nettoyer l'écran. Vous pouvez le faire manuellement avec control-L.

Vous pouvez écrire une fonction shell qui obtient bash pour réimprimer son invite une fois echoterminé . Faire juste echo "$PS1"ne reproduira aucun caractère déjà tapé. kill -WINCH $$sur mon système obtient bash pour réimprimer sa ligne d'invite sans effacer l'écran, en laissant hiune ligne seule avant l'invite. SIGWINCH est envoyé automatiquement lorsque la taille de WINdow CHANGE, et la réponse de bash se produit pour faire ce que nous voulons.

Cela pourrait fonctionner avec d'autres shells, mais je n'essaye pas de rendre ce 100% portable / POSIX. ( Malheureusement, cela ne fonctionne que sur bash4.4, pas bash4.3, ou dépend d'un paramètre que je ne connais pas )

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

Vous pouvez facilement envelopper cela dans une fonction shell qui prend un argument de sommeil et un message.

bash 4.3 ne réimprime pas son invite après SIGWINCH, donc cela ne fonctionne pas là-bas. J'ai shopt -s checkwinsizeactivé sur les deux systèmes, mais cela ne fonctionne que sur le système Bash 4.4 (Arch Linux).

Si cela ne fonctionne pas, vous pouvez essayer (sleep 2 && echo -e '\nhi' && echo "$PS1" &), qui "fonctionne" si la ligne de commande est vide. (Il ignore également la possibilité PROMPT_COMMANDdéfinie.)


Il n'y a pas de moyen vraiment propre de mélanger la sortie du terminal avec tout ce que vous pourriez faire sur votre terminal plus tard, lorsque le minuteur se déclenche. Si vous êtes en train de faire défiler quelque chose less, vous pouvez facilement le manquer.

Si vous cherchez quelque chose avec des résultats interactifs, je vous suggère de lire un fichier audio. par exemple avec un lecteur en ligne de commande comme mpv(nice fork de MPlayer2). Ou choisissez un fichier vidéo pour qu'une nouvelle fenêtre s'ouvre.

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

Vous voudrez peut-être echoouvrir un nouveau terminal xterm / konsole / gnome. Utilisez une option qui maintient le terminal ouvert après la fin de la commande.

Peter Cordes
la source
Je vous remercie beaucoup David, et j'ai levé le pouce. C'est vraiment le plus proche de ce que je recherche. Existe-t-il un moyen de mettre à niveau la commande suivante afin d'émuler un Enterévénement de pression de touche, juste après l'apparition de l'écho, de sorte que je serais automatiquement ramené à l' invite principale de la console ? (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &).
Arcticooling
@Arcticooling: kill -WINCH $$ ne rafraîchit l'invite du shell en cours d' exécution dans le terminal. C'était tout l'intérêt de l'utiliser. Appuyer sur Entrée soumettrait une commande partiellement tapée, mais WINCH ne le fait pas, comme je l'ai montré. Si vous n'avez rien tapé, vous obtenez une invite vide.
Peter Cordes
1
Si vous aviez commencé à taper rm -rf ~/some/directory, Entrée au mauvais moment pourrait potentiellement s'exécuter rm -rf ~/. (Conseil de sécurité: finissez de taper les chemins, puis contrôlez-le et passez lsà rm -rf, afin que le doigté gras entre à tout moment ne gâchera pas votre journée.)
Peter Cordes
Filaire, j'ai exécuté (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)et après que l'écho soit apparu, j'ai reçu une invite vide, seulement après avoir frappé, Enterje suis revenu à l'invite principale ... Désolé si je vous comprends mal, je suis assez nouveau pour Bash.
Arcticooling
@Arcticooling: J'utilise Bash 4.4 sur Arch Linux, dans un émulateur de terminal Konsole. J'espérais que ma commande serait portable au moins pour les autres versions de bash, mais peut-être pas :( Ouais, je viens de tester dans Bash 4.3 dans une screensession (et juste via SSH) sur un ancien système Ubuntu, et j'ai le comportement que vous décrivez.
Peter Cordes
13

Le correct à l'utilisation est at <timespec>, où <timespec>est la spécification de temps. Vous pouvez également utiliser l' -foption pour spécifier le fichier contenant les commandes à exécuter. Si -fet aucune redirection n'est utilisée, at va lire les commandes à exécuter depuis stdin (entrée standard).

Dans votre exemple, pour exécuter "une commande" (j'ai choisi "une commande" pour être "écho salut" dans l'exemple ci-dessous) une minute juste après avoir appuyé sur Entrée (maintenant), le but <timespec>à utiliser est now + 1 minute. Donc, afin d'obtenir le résultat souhaité avec une solution à une ligne, veuillez utiliser la commande ci-dessous:

echo 'echo hi' | at now + 1 minute

Ceci est supposé (et fera) pour exécuter la echo hicommande après une minute. Le problème ici est que la echo hicommande sera exécutée par la commande at dans un terminal factice et la sortie sera envoyée à la boîte aux lettres de l'utilisateur (si elle est correctement configurée - ce qui nécessite une explication plus détaillée sur la façon de configurer une boîte aux lettres dans une machine Unix et ne fait pas partie de la portée de cette question). L'essentiel est que vous ne pourrez pas voir directement le résultat echo hidans le même terminal que vous avez émis la commande. L'alternative la plus simple est de la rediriger vers "un fichier", par exemple:

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

Dans l'exemple ci-dessus, "certains fichiers" est /tmp/at.outet le résultat attendu est que le fichier at.outsous / tmp est créé après une minute et contient le texte "hi".

En plus de l' now + 1 minuteexemple ci-dessus, de nombreuses autres spécifications de temps peuvent être utilisées, même certaines complexes. La commande at est suffisamment intelligente pour interpréter des textes assez lisibles comme les exemples ci-dessous:

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

Veuillez vous référer à la page de manuel de pour plus d'informations sur les spécifications d'heure possibles car les variations possibles sont assez étendues et peuvent inclure des dates, des heures relatives ou absolues, etc.

Marcelo
la source
2
atenvoie par défaut la sortie par courrier, mais cela doit être correctement configuré pour fonctionner.
Simon Richter
J'offre maintenant 100 primes de répétition pour une réponse. Veuillez lire mon message de prime ci-dessous et modifier pour expliquer plus sur ce qu'est at <timespec>et le drapeau que vous avez utilisé et s'il convient à Ubuntu 16.04 xenial.
Arcticooling du
Ok, j'ai augmenté le niveau de détail dans la réponse pour que ce soit plus didactique maintenant. Cela peut sembler un peu long pour certains, mais je pense que cela ne laisse aucune marge de doute car il cite clairement des exemples qui fonctionnent et comment ils fonctionnent également.
Marcelo
7

Exécutez le sleepdans un sous-shell:

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

Remarque: sous Linux, vous pouvez donner l'argument pour dormir en quelques secondes ou avec un suffixe s / m / h / d (pour secondes, minutes, heures, jours). Sur BSD, l'argument doit être en secondes

PiedPiper
la source
J'ai un petit problème avec cette méthode --- le flux "hi" est imprimé dans mon invite (dans le stdin) et non dans mon stdout(comme il le ferait avec un régulier echo).
Arcticooling
1
Si vous ne voulez pas que la sortie de votre terminal soit mélangée avec des invites et la sortie d'autres commandes, vous devrez la rediriger quelque part
PiedPiper
Cette redirection a échoué @PiedPiper (sleep 10s; echo 'hi') & 1>. J'en lis maintenant plus à ce sujet.
Arcticooling
2
Lire la documentation est toujours une bonne idée :)
PiedPiper
1
@Arcticooling Il semble que vous soyez confus quant à la signification de stdinet stdout. Ils n'ont rien à voir avec l'endroit où les choses apparaissent sur votre terminal; au lieu de cela, vous devez les interpréter par rapport à un processus particulier (essentiellement, un programme). L'entrée standard ("stdin") d'un processus est une source à partir de laquelle le processus peut lire des données et la sortie standard du processus ("stdout") est une destination vers laquelle il peut écrire des données. Ces sources et destinations peuvent être d'autres programmes ou fichiers, ou le terminal lui-même (les choses que vous tapez vont à stdin, et tout ce qui vient à stdout est affiché).
David Z
3

Cela a échoué car vous canalisez la sortie de echo "hi", ce qui est juste hipour at, mais atattend que son entrée puisse être exécutée par le shell système par défaut (généralement bash, mais parfois quelque chose de différent selon le système).

Cette:

at 1m << EOF
echo "hi"
EOF

réalisera ce que vous semblez essayer de faire. Il utilise une construction shell appelée `` ici document '', qui est généralement la méthode acceptée pour faire ce type de chose (car il gère plusieurs lignes mieux que l'utilisation de la echocommande et ne nécessite pas de créer un nouveau fichier comme vous le feriez avec cat), et se traduit par l'équivalent un peu plus compliqué en utilisant echo:

echo 'echo "hi"' | at 1m

Il est probablement intéressant de noter que la atcommande enverra toute sortie de commande à l'utilisateur qui a invoqué la atcommande, au lieu de contourner la sortie vers le terminal comme le sleepferait une commande retardée dans un sous-shell. En particulier, cela signifie qu'à moins que vous n'ayez effectué une personnalisation du système ou que vous ayez l'intention de lire votre spool de messagerie local, vous ne pourrez pas obtenir la sortie des commandes exécutées via at.

Austin Hemmelgarn
la source
Pour une raison quelconque, les deux exemples que vous avez donnés me donnent une erreur syntax error. Last token seen: m Garbled time . Je ne sais pas pourquoi. J'utilise Ubuntu 16.04, Bash 4.0. Vous l'avez peut-être fait sur un autre système. Même lorsque je tape juste atsans autre argument --- j'obtiens toujours cette même erreur. Plus de données ici .
Arcticooling
1
at 1mne fonctionne pas pour un posix compatible at. La commande devrait êtreat now + 1 minute
PiedPiper
@PiedPiper est correct, 1m n'est pas compatible avec les versions compatibles POSIX de at, je supposais simplement que vous aviez une version qui acceptait cette syntaxe.
Austin Hemmelgarn
@PiedPiper, at 1mn'est pas POSIX, mais les implémentations compatibles POSIX atsont libres de l'interpréter comme elles le souhaitent, cela ne rend pas une atimplémentation moins conforme de l'interpréter comme signifiant la même chose que now + 1 minutede renvoyer une erreur ou signifiant il y a un mois . IOW, c'est le at 1mcode qui n'est pas POSIX.
Stéphane Chazelas
2

Combinez la réponse de @Peter Cordes et cette réponse /programming/23125527/how-to-return-to-bash-prompt-after-printing-output-from-backgrounded-function/23125687#23125687

Le code ci-dessous est de la dernière réponse, avec un petit changement de la mienne. Compilez-le pour déposer a.out (quel que soit le nom que vous aimez)

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

Exécutez ensuite la commande ci-dessous. (Je mets a.out dans dir / tmp /, vous devrez peut-être changer pour le vôtre)

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

ou

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

BTW, kill -WINCH $$fonctionne très bien pour moi avec Bash 4.3 sur Gentoo.

Bruce
la source
1

En s'appuyant sur les réponses ci-dessus, vous pouvez demander à la commande intégrée de diriger sa sortie vers votre terminal, comme suit:

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

La ttycommande renvoie le chemin du périphérique de votre terminal actuel, l'enfermant dans $(...)a bash l'exécute dans un sous-shell, et la commande kill envoie un signal au processus en cours qui aura bash réimprimer son invite $ PS1.

user1404316
la source