Powiadomienie na pulpicie po zakończeniu długotrwałych poleceń

59

Chcę otrzymywać powiadomienie na pulpicie za każdym razem, gdy polecenie uruchamiane przez ponad, powiedzmy 15 sekund, kończy się w interaktywnej powłoce.

Innymi słowy, chciałbym, aby wszystkie polecenia były zapakowane w coś takiego

start=$(date +%s);
ORIGINAL_COMMAND;
[ $(($(date +%s) - start)) -le 15 ] || notify-send "Long running command finished"

Jaki jest najlepszy sposób na osiągnięcie tego w bash?

aioobe
źródło
1
Możliwe to samo na Superuser: superuser.com/questions/31917/…
Ciro Santilli 27 改造 中心 法轮功 六四 事件
Dlaczego właśnie dodałeś „w powłoce interaktywnej”? Może to unieważnić niektóre z istniejących odpowiedzi, które istniały w kontekście twojego oryginalnego postu przez ponad rok. Jeśli potrzebujesz rozwiązania specjalnie dla interaktywnej powłoki, zastanów się nad zadaniem osobnego pytania dotyczącego tego.
Sergiy Kolodyazhnyy
Część „powłoki interaktywnej” została wyjaśniona w pierwszym komentarzu przed edycją. Po prostu zredagowałem pytanie i usunąłem komentarz. Poza tym 3 wiersze zamieszczone w pytaniach działają dobrze dla skryptu.
aioobe

Odpowiedzi:

24

Chcesz https://launchpad.net/undistract-me (instalowalny z archiwów Ubuntu za pomocą sudo apt-get install undistract-me), który robi dokładnie to, o co prosisz, w tym działa automatycznie (to znaczy, bez konieczności dodawania czegoś dodatkowego do potencjalnie długich- uruchamianie poleceń).

sil
źródło
1
Wygląda na to, że nie działa. Zainstalowałem go, ponownie uruchomiłem system i testowałem sleep 30, ale bez powiadomienia.
Galgalesh
4
Trzeba dodać następujące dwa wiersze .bashrc: . /usr/share/undistract-me/long-running.bash notify_when_long_running_commands_finish_install. Wydaje się, że to błąd: github.com/jml/undistract-me/issues/23
Ludenticus
32

O ile rozumiem, chcesz opakowanie . I chcesz użyć polecenia za jego pośrednictwem, aby dało pożądane powiadomienie, jeśli czas działania polecenia jest dłuższy niż 15 sekund. Więc oto to.

wrapper(){
    start=$(date +%s)
    "$@"
    [ $(($(date +%s) - start)) -le 15 ] || notify-send "Notification" "Long\
 running command \"$(echo $@)\" took $(($(date +%s) - start)) seconds to finish"
}

Skopiuj tę funkcję do swojego ~/.bashrci źródła ~/.bashrcjako,

. ~/.bashrc

Wykorzystanie

wrapper <your_command>

Jeśli zajmie to więcej niż 15 sekund, otrzymasz powiadomienie na pulpicie opisujące polecenie i czas jego wykonania.

Przykład

wrapper sudo apt-get update

zrzut ekranu nitryfikacji pulpitu

souravc
źródło
1
"$@"Nie $@. Duża różnica.
geirha
4
Chciałbym jednak unikać pisania wrapperprzed każdym poleceniem, które wpisuję.
aioobe
2
@ aioobe możesz użyć krótszej nazwy funkcji, nawet może to być litera. ale opakowanie działa w ten sposób.
souravc
3
command; alertjest w rzeczywistości krótszy niż wrapper command:-)
aioobe
1
Jeśli nie chcesz, notify-sendaby wygasł przez długi czas, podaj powiadomienie-wyślij niestandardowy limit czasu (w milisekundach). np.notify-send --expire-time 999999999 ...
Bryce Guinta
26

W ~/.bashrcjest alias alertokreślono jako:

alias alert='notify-send --urgency=low -i "$([ $? = 0 ] && echo terminal || echo error)" "$(history|tail -n1|sed -e '\''s/^\s*[0-9]\+\s*//;s/[;&|]\s*alert$//'\'')"'

które mogą być użyte do powiadomienia o zakończeniu wykonywania polecenia.

Stosowanie:

$ the_command; alert

na przykład

$ sudo apt-get update; alert

Snap1

Możesz dostosować alias do swoich potrzeb i pragnień.

precyzyjny
źródło
3
Dobrze wiedzieć. Czy istnieje sposób na dołączenie alertu po każdym wpisaniu polecenia (a także powiadamianie go tylko wtedy, gdy polecenie trwało dłużej niż 15 sekund)?
aioobe
dziwna rzecz, że jest już dodana do mojego .bashrc, czy jest domyślna w Ubuntu?
Vignesh,
13

Oprócz sugerowanego opakowania takiego jak souravc, naprawdę nie ma dobrego sposobu na zrobienie tego w bashu. Możesz włamać się do niego z pułapką DEBUG i PROMPT_COMMAND. Pułapka DEBUG jest uruchamiana przy każdym uruchomieniu polecenia, a PROMPT_COMMAND jest uruchamiany tuż przed napisaniem monitu.

Rzeczy ~/.bashrcstają się czymś takim

trap '_start=$SECONDS' DEBUG
PROMPT_COMMAND='(if (( SECONDS - _start > 15 )); then notify-send "Long running command ended"; fi)'

To jest hack, więc nie zdziw się, jeśli spotkasz się z dziwnymi efektami ubocznymi.

geirha
źródło
4

EDYTOWAĆ

TL; DR : utwórz skrót do autouzupełniania .inputrci uruchom w .bashrc . Uruchom polecenie jak zwykle, wpisz, ale zamiast tego ENTERnaciśnij skrót określony w.inputrc

Osoba, która naliczyła nagrodę za to pytanie, powiedziała:

„Wszystkie istniejące odpowiedzi wymagają wpisania dodatkowego polecenia po poleceniu. Chcę odpowiedzi, która zrobi to automatycznie”.

Badając rozwiązania tego problemu, natknąłem się na to pytanie z stackexchange, które pozwala na powiązanie CtrlJz sekwencją poleceń: Ctrla(przejście do początku linii), umieść ciąg „mesure” przed wprowadzonym poleceniem, Ctrlm(wykonaj)

W ten sposób otrzymujesz funkcję automatycznego uzupełniania i osobne ENTERpolecenie do pomiaru czasu, zachowując oryginalny cel drugiej funkcji, którą zamieściłem poniżej.

Na razie oto zawartość mojego ~/.inputrcpliku:

"\C-j": "\C-a measure \C-m"

A oto zawartość .bashrc(uwaga, nie używałem bash od zawsze - używam mksh jako mojej powłoki, stąd to widać w oryginalnym poście. Funkcjonalność jest nadal taka sama)

PS1=' serg@ubuntu [$(pwd)]
================================
$ '
function measure () 
{

/usr/bin/time --output="/home/xieerqi/.timefile" -f "%e" $@ 

if [ $( cat ~/.timefile| cut -d'.' -f1 ) -gt 15 ]; then

    notify-send "Hi , $@ is done !"

fi


}

Oryginalny post

Oto mój pomysł - użyj funkcji w .bashrc. Podstawowa zasada - służy /usr/bin/timedo pomiaru czasu potrzebnego do wykonania polecenia, a jeśli jest on dłuższy niż 15 sekund, wyślij powiadomienie.

function measure () 
{

if [ $( /usr/bin/time -f "%e" $@ 2>&1 >/dev/null ) -gt 15 ]; then

    notify-send "Hi , $@ is done !"

fi


}

Tutaj przekierowuję dane wyjściowe do, /dev/nullale aby wyświetlić dane wyjściowe, można również dokonać przekierowania do pliku.

O wiele lepszym podejściem, IMHO, jest wysyłanie wyniku czasu do jakiegoś pliku w folderze domowym (tylko po to, abyś nie zanieczyszczał systemu plikami czasowymi i zawsze wiedział, gdzie szukać). Oto druga wersja

function measure () 
{

/usr/bin/time --output=~/.timefile -f "%e" $@ 

if [ $( cat ~/.timefile | cut -d'.' -f1 ) -gt 15 ]; then

    notify-send "Hi , $@ is done !"

fi


}

A oto zrzuty ekranu pierwszej i drugiej wersji, w tej kolejności

Pierwsza wersja, brak danych wyjściowych wprowadź opis zdjęcia tutaj

Druga wersja z wyjściem wprowadź opis zdjęcia tutaj

Sergiy Kolodyazhnyy
źródło
Nawiasem mówiąc, działa również z poleceniami sudo. Testowałem to z sudo lsofi głównie z ping -c20 google.com.
Sergiy Kolodyazhnyy
3

Niedawno zbudowałem narzędzie, które służy temu celowi. Może być uruchamiany jako opakowanie lub automatycznie z integracją powłoki. Sprawdź to tutaj: http://ntfy.rtfd.io

Aby zainstalować:

sudo pip install ntfy

Aby użyć go jako opakowania:

ntfy done sleep 3

Aby automatycznie otrzymywać powiadomienia, dodaj to do swojego .bashrclub .zshrc:

eval "$(ntfy shell-integration)"
Daniel Schep
źródło
1

Twój skrypt działa całkiem dobrze, upewnij się tylko, że dołączasz#!/bin/bash wiersz „shebang” ( ) . Inne #!/bin/bashtutaj wymienione , ale przez większość czasu #!/bin/bashdziała dobrze dla skryptów bash dla Uniksa. Jest wymagany przez interpretera skryptów, aby wiedział, jaki to jest typ skryptu.

Wydaje się, że działa to jako skrypt testowy:

#!/bin/bash
start=$(date +%s);
   echo Started
   sleep 20;
   echo Finished!
[ $(($(date +%s) - start)) -le 15 ] || notify-send -i dialog-warning-symbolic "Header" "Message"

Aby zmodyfikować ten skrypt, umieść komendę w miejscu, w którym znajdują się linie echoi sleep.

Uwaga za pomocą notify-send, możesz także użyć -iikony :-)

Upewnij się także, że jest wykonywalny, uruchamiając chmod +x /PATH/TO/FILEgo najpierw.

Wilf
źródło
1

Możesz zmodyfikować Bash, aby zaimplementować pożądaną funkcję opakowania.

Jeśli jesteś skłonny wprowadzić wiele takich modyfikacji do swojej interaktywnej powłoki, możesz rozważyć przejście na wewnętrznie hakowalną powłokę, taką jak eshell, która jest zbudowana przy użyciu elisp Emacsa i ma wszystkie swoje spersonalizowane możliwości dostosowywania:

http://www.masteringemacs.org/articles/2010/12/13/complete-guide-mastering-eshell/

Ryan Prior
źródło
-2

Możesz to również zrobić za pomocą takiej prostej instrukcji if

#!/bin/bash

START=$(date +%s)

TIME=$(($START+15))

ORIGINAL_COMMAND;

END=$(date +%s)

if [ $END -gt $TIME ]
then
    notify-send "Task Completed"
fi

Możesz użyć własnych nazw zmiennych.

Mogę potwierdzić, że to działa, przetestowałem przy użyciu polecenia, które zajmuje dużo czasu, a które nie, a powiadomienie przychodzi dla tego, które zajmuje dużo czasu

Można również zrobić to z każdym poleceniem zastępując ORIGINAL_COMMANDze $@i uruchamiając skrypt jako ./script command.

Rumesh
źródło
Dlaczego moja odpowiedź została odrzucona? Chcę wiedzieć, gdzie popełniłem błąd w odpowiedzi
Rumesh
Nie głosowałem za tym, ale powiem ci, co jest z nim nie tak: Po pierwsze, wymaga wpisania dodatkowego polecenia. Konkretnie stwierdziłem w mojej nagrody, że nie chcę tego robić. Po drugie, dlaczego calc? $((2+2))jest wbudowanym bash, nie wymaga żadnych dodatkowych programów. Po trzecie: nie działa z poleceniami zawierającymi spacje. tl; dr: jest gorzej niż w przypadku innych rocznych odpowiedzi
Galgalesh
Cóż ... czas rozpoczęcia i zakończenia zawsze będzie inny, prawda? Nawet dla krótkiego polecenia trwającego 1 sekundę
Sergiy Kolodyazhnyy