через некоторое время сделайте что-нибудь (и, возможно, покажите результат в консоли)

12

Я использую Ubuntu Server 16.04, и я хочу использовать утилиту atв моем текущем сеансе, чтобы сделать что-то через 1 минуту (скажем, an echo), без указания конкретной даты и времени - всего на 1 минуту вперед от текущего времени.

Это не удалось:

echo 'hi' | at 1m

Причина я выбираю atболее sleepпотому , что недостатки сна текущего сеанса и для них больше подходит для задержки команд в другой сессии, а не один мы работаем с большой частью времени. AFAIR, atне ведет себя так и не будет препятствовать моей сессии.

Update_1

По ответу Пьед Пайпер, я попробовал:

(sleep 1m; echo 'hi')  &

У меня проблема с этим методом: поток «hi» печатается внутри моей основной подсказки, а также добавляет пустую вторичную подсказку ( _) прямо под первичной подсказкой, которая его содержит, см .:

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

Update_2

По ответу Питера Корде я попытался:

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

Это работает должным образом в Bash 4.4, но, по-видимому, не в некоторых старых версиях (см. Комментарии в ответе). Я лично использую Bash 4.3 в своей среде.

Arcticooling
источник
2
Мне бы хотелось, чтобы флаги уничтожения были в стиле GNU и могли быть переставлены так, чтобы это не звучало как «убей девку»!
НХ.

Ответы:

6

Поток "hi" напечатан в моем приглашении (в stdin), а не в моемstdout

Нет, это не "напечатано в вашем stdin".

Если вы нажмете return, он не будет запускаться hiкак команда. Просто фон echoпечатается, hiпока курсор был после вашего приглашения.


Возможно, вы хотите напечатать ведущий символ новой строки , как (sleep 1m && echo -e '\nhi' &). (Отрегулируйте по мере необходимости echoв вашей оболочке. Или используйтеprintf для переносимости.)

Я поместил &внутреннюю часть подоболочки, чтобы «отречься» от фоновой работы, чтобы вы также избежали «шума» [1]+ Done. С &&между командами, &относится ко всему соединение-командной цепи, поэтому он возвращается к оболочке приглашение сразу , а не ждать , sleepчтобы выйти , как вы получите с(sleep 1; echo ... &)

Вот что происходит (с задержкой в ​​1 секунду):

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

Bash не знает, что echoнапечатал что-то, поэтому он не знает, что нужно перепечатать его подсказку или очистить экран. Вы можете сделать это вручную с помощью control-L.

Вы можете написать функцию оболочки, которая получает bash для повторной печати своего приглашения после echoзавершения . Просто делать echo "$PS1"не будет воспроизводить какие-либо уже напечатанные символы. kill -WINCH $$в моей системе получает bash для повторной печати строки подсказки без очистки экрана, оставляяhi строку перед самой подсказкой. SIGWINCH отправляется автоматически, когда размер окна изменяется, и ответ bash на него делает то, что мы хотим.

Это может работать с другими оболочками, но я не пытаюсь сделать это на 100% портативным / POSIX. ( К сожалению, это работает только на bash4.4, а не на bash4.3 или зависит от некоторых настроек, о которых я не знаю )

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

Вы можете легко обернуть это в функцию оболочки, которая принимает спящий аргумент и сообщение.

bash 4.3 не перепечатывает свою подсказку после SIGWINCH, поэтому там это не работает. у меня естьshopt -s checkwinsize включил в обеих системах, но он работает только в системе Bash 4.4 (Arch Linux).

Если это не работает, вы можете попробовать (sleep 2 && echo -e '\nhi' && echo "$PS1" &), что «работает», если командная строка пуста. (Это также игнорирует возможность, которая PROMPT_COMMANDустановлена.)


Нет действительно чистого способа заставить вывод терминала смешиваться с тем, что вы могли бы делать на своем терминале позже, когда срабатывает таймер. Если вы находитесь в процессе прокрутки чего-то less, вы можете легко пропустить это.

Если вы ищете что-то с интерактивными результатами, я предлагаю воспроизвести аудиофайл. например, с помощью проигрывателя командной строки mpv(хороший форк MPlayer2). Или выберите видеофайл, чтобы открылось новое окно.

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

Возможно, вы захотите echoоткрыть новый терминал xterm / konsole / gnome. Используйте опцию, которая сохраняет терминал открытым после выхода из команды.

Питер Кордес
источник
Благодарю тебя, дорогой Дэвид, и я поднял голову. Это действительно ближе всего к тому, что я ищу. Есть ли способ обновить следующую команду, чтобы эмулировать событие Enterнажатия клавиши, сразу после появления эха, чтобы я автоматически возвращался в основное приглашение консоли ? (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &),
Arcticooling
@Arcticooling: kill -WINCH $$ действительно обновить приглашение оболочки , работающей в терминале. Вот и весь смысл его использования. Нажатие клавиши ввода приведет к вводу частично набранной команды, а WINCH - нет, как я показал. Если вы ничего не набрали, то вы получите пустое приглашение.
Питер Кордес
1
Если бы вы начали печатать rm -rf ~/some/directory, потенциально не запустил бы Enter rm -rf ~/. (Совет по безопасности: закончите вводить пути, а затем управляйте «а» и переходите lsна « rm -rfжирный ввод» в любое время, что не испортит ваш день.)
Питер Кордес
Зашитый, я выполнил, (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)и после того, как эхо появилось, я получил пустое приглашение, только после нажатия Enterя вернулся к основному приглашению ... Извините, если я вас неправильно понял, я совсем новичок в Bash.
Arcticooling
@Arcticooling: я использую Bash 4.4 в Arch Linux, в эмуляторе терминала Konsole. Я надеялся, что моя команда будет переносимой, по крайней мере, к другим версиям bash, но, возможно, нет :( Да, только что протестировал в Bash 4.3 внутри screenсеанса (и только через SSH) в старой системе Ubuntu, и я получил поведение, которое вы описываете.
Питер Кордес
13

Правильным при использовании является at <timespec>, где <timespec>указывается время. Вы также можете использовать -fопцию, чтобы указать файл, содержащий команды для выполнения. Если -fи перенаправление не используется, at будет читать команды для выполнения из стандартного ввода (стандартный ввод).

В вашем примере, чтобы выполнить «некоторую команду» (я выбрал «некоторую команду» как «echo hi» в приведенном ниже примере) через одну минуту сразу после того, как вы нажали клавишу ввода (сейчас), предполагается <timespec>использовать ее now + 1 minute. Итак, чтобы получить желаемый результат с помощью однострочного решения, используйте команду ниже:

echo 'echo hi' | at now + 1 minute

Это предполагается (и будет делать), чтобы запустить echo hiкоманду через одну минуту. Проблема здесь заключается в том, что echo hiкоманда будет выполнена командой at в фиктивном tty, а выходные данные будут отправлены в почтовый ящик пользователя (если он настроен правильно - что требует более подробного объяснения о том, как настроить почтовый ящик в Unix машина и не входит в сферу этого вопроса). Суть в том, что вы не сможете напрямую увидеть результат echo hiв том же терминале, в котором вы выполнили команду. Самый простой вариант - перенаправить его в «какой-нибудь файл», например:

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

В приведенном выше примере «некоторый файл» есть, /tmp/at.outи ожидаемый результат состоит в том, что файл at.outв / tmp создается через одну минуту и ​​содержит текст «привет».

В дополнение к now + 1 minuteприведенному выше примеру, могут использоваться многие другие временные спецификации, даже некоторые сложные. Команда at достаточно умна, чтобы интерпретировать довольно удобочитаемые, как в примерах ниже:

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

Пожалуйста, обратитесь к справочной странице для получения дополнительной информации о возможных спецификациях времени, поскольку возможные вариации довольно обширны и могут включать даты, относительное или абсолютное время и т. Д.

Marcelo
источник
2
atпо умолчанию отправляет вывод по почте, но это должно быть правильно настроено для работы.
Саймон Рихтер
Теперь я предлагаю 100 повторений за ответ. Пожалуйста, прочитайте мое сообщение о вознаграждении ниже и отредактируйте, чтобы объяснить больше о том, что at <timespec>и какой флаг вы использовали и подходит ли он для Ubuntu 16.04 xenial.
Arcticooling
Хорошо, я увеличил уровень детализации в ответе, чтобы он стал более дидактичным. Для некоторых это может показаться немного скучным, но я считаю, что это не оставляет никаких оснований для сомнений, поскольку в нем четко приводятся примеры, которые работают и как они работают.
Марсело
7

Запустите sleepв подоболочке:

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

Примечание: в Linux вы можете задать аргумент для сна в секундах или с суффиксом s / m / h / d (для секунд, минут, часов, дней). На BSD аргумент должен быть в секундах

Крысолов
источник
У меня небольшая проблема с этим методом - поток «hi» печатается в моем приглашении (в stdin), а не в моем stdout(как это было бы с обычным echo).
Arcticooling
1
Если вы не хотите, чтобы вывод в вашем терминале смешивался с запросами и выводом других команд, вам придется куда-то перенаправить его
PiedPiper
Это перенаправление не удалось @PiedPiper (sleep 10s; echo 'hi') & 1>. Я теперь читаю больше об этом.
Arcticooling
2
Чтение документации всегда хорошая идея :)
PiedPiper
1
@Arcticooling Похоже, вы не понимаете значения stdinи stdout. Они не имеют ничего общего с тем, где вещи появляются на вашем терминале; вместо этого вы должны интерпретировать их относительно определенного процесса (в основном, программы). Стандартный ввод ("stdin") для процесса - это источник, из которого процесс может читать данные, а стандартный вывод процесса ("stdout") - это место назначения, в которое он может записывать данные. Этими источниками и получателями могут быть другие программы, или файлы, или сам терминал (набираемые вами данные отправляются в stdin, и отображается все, что приходит в stdout).
Дэвид З
3

Это не удалось , потому что вы перенаправив вывод echo "hi", который только hiк at, но atожидает , что это вход может быть выполнена оболочкой системы по умолчанию (обычно Баш, но иногда что - то другое в зависимости от системы).

Эта:

at 1m << EOF
echo "hi"
EOF

достигнет того, что вы, кажется, пытаетесь сделать. Он использует конструкцию оболочки, называемую «здесь документ», которая, как правило, является общепринятым способом для такого рода вещей (поскольку он обрабатывает несколько строк лучше, чем использование echoкоманды, и не требует создания нового файла, как это было бы необходимо с помощью cat) и переводит в несколько более сложный эквивалент, используя echo:

echo 'echo "hi"' | at 1m

Вероятно, стоит отметить, что atлюбые выходные команды отправляются пользователю, который вызвал atкоманду, вместо того, чтобы выводить вывод на терминал, как команда, отложенная при использовании sleepв подоболочке. В частности, это означает, что если вы не выполнили некоторые настройки системы или не собираетесь читать локальную почтовую папку, вы не сможете получить вывод команд, запущенных через at.

Остин Хеммелгарн
источник
По какой-то причине оба приведенных вами примера дают мне ошибку syntax error. Last token seen: m Garbled time . Понятия не имею почему. Я использую Ubuntu 16.04, Bash 4.0. Может быть, вы сделали это в другой системе. Даже когда я печатаю atбез каких-либо дополнительных аргументов, я все равно получаю ту же ошибку. Больше данных здесь .
Arcticooling
1
at 1mне работает для posix-совместимых at. at now + 1 minute
Команду
@PiedPiper верен, 1m несовместим с POSIX-совместимыми версиями at, я просто предполагал, что у вас есть версия, которая принимает этот синтаксис.
Остин Хеммельгарн
@PiedPiper at 1mне является POSIX, но POSIX-совместимые реализации atмогут свободно интерпретировать его так, как они хотят, это не делает atреализацию менее совместимой, чтобы интерпретировать ее как значение, аналогичное now + 1 minuteвозврату ошибки или значения месяц назад . IOW, это at 1mкод, который не POSIX.
Стефан
2

Объедините ответ @Peter Cordes и этот ответ /programming/23125527/how-to-return-to-bash-prompt-after-printing-output-from-backgounded-function/23125687#23125687

Код ниже взят из последнего ответа, с небольшим изменением моего. Скомпилируйте его в файл a.out (как вам угодно)

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

Затем выполните команду ниже. (Я помещаю a.out в dir / tmp /, возможно, вам придется перейти на ваш)

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

или

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

Кстати, kill -WINCH $$у меня отлично работает с Bash 4.3 на Gentoo.

Брюс
источник
1

Основываясь на ответах выше, вы можете сделать так, чтобы встроенная команда направляла свой вывод на ваш терминал, например так:

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

Команда ttyвозвращает путь к устройству вашего текущего терминала, заключив его в $(...)bash, выполнив его в под-оболочке, и команда kill отправляет сигнал текущему процессу, который bash перепечатает приглашение $ PS1.

user1404316
источник