Uruchamianie poleceń bash w tle bez drukowania identyfikatorów zadań i procesów


83

Uruchamianie procesu w tle w bash jest dość łatwe.

$ echo "Hello I'm a background task" &
[1] 2076
Hello I'm a background task
[1]+  Done                    echo "Hello I'm a background task"

Jednak dane wyjściowe są szczegółowe. W pierwszym wierszu wypisywany jest identyfikator zadania i identyfikator procesu zadania w tle, następnie mamy wyjście polecenia, na końcu mamy identyfikator zadania, jego status i polecenie, które wyzwoliło zadanie.

Czy istnieje sposób na pominięcie wyniku działania zadania w tle w taki sposób, aby dane wyjściowe wyglądały dokładnie tak, jak bez znaku ampersand na końcu? To znaczy:

$ echo "Hello I'm a background task" &
Hello I'm a background task

Powodem, o który pytam, jest to, że chcę uruchomić proces w tle jako część polecenia uzupełniania tabulacji, więc wyjście tego polecenia musi być nieprzerwane, aby miało jakikolwiek sens.


Przypuszczam, należy dodać 2> / dev / null, aby cokolwiek zrobić wewnątrz skryptów bash-wykończeniowymi
sehe

Odpowiedzi:


98

Nie ma związku z dokończeniem, ale możesz ukryć to wyjście, umieszczając wywołanie w podpowłoce:

(echo "Hello I'm a background task" &)

3
Dzięki, to zdecydowanie działa. Niestety, podczas uruchamiania takiego skryptu z funkcji uzupełniania bash wydaje się, że bash czeka, aż podproces zostanie ukończony, zanim poda wyniki ukończenia. Jest to niefortunne, ponieważ oznacza to, że nie wydaje się możliwe wywołanie procesów roboczych w tle przez zakończenie bash.
Alex Spurling

13
Nie możesz tego użyć, jeśli chcesz, aby waitpraca w tle zakończyła się.
arekolek

13

Opierając się na odpowiedzi @ shellter, zadziałało to dla mnie:

tyler@Tyler-Linux:~$ { echo "Hello I'm a background task" & disown; } 2>/dev/null; sleep .1;
Hello I'm a background task
tyler@Tyler-Linux:~$

Nie znam powodu tego, ale przypomniałem sobie ze starego postu, w którym wyparcie się uniemożliwia bashowi wyprowadzanie identyfikatorów procesów.


@Tyzoid. Wypróbowałem twoje rozwiązanie w powłoce bash (v 4.2.37) i uzyskałem interesujące wyniki. 1. Otrzymałem te same wyniki po prostu dodając ;na końcu. (bez snu) . 2. ALE zauważyłem, że kiedy dodałem echo STDERR 2>&1w "następnej" linii, to się [1]+ Done ...pojawiło! . 3. Moje rozwiązanie (jak już zauważyłem) działa w kshi echo STDERR 2>&1generuje tylko wyjściowy STDERR. Cóż, żyj i ucz się dla nas obu. Powodzenia wszystkim.
łowca

12

W niektórych nowszych wersjach basha i ksh93 można otoczyć go podpowłoką lub grupą procesów (np { ... }.).

/home/shellter $ { echo "Hello I'm a background task" & } 2>/dev/null
Hello I'm a background task
/home/shellter $

5
Kiedy używam nawiasów klamrowych, ostatni wiersz danych wyjściowych nadal jest wyświetlany (nie jestem pewien, dlaczego go nie widzisz).
Alex Spurling

Skopiowałem to dokładnie, ale nie działa dla mnie, nadal pokazuje dane wyjściowe, jak mówi @AlexSpurling. Może robię coś złego, skoro inni to popierali, ale obawiam się, że muszę to przyznać -1.
Mark

1
@Mark: Właśnie wykonałem kilka szybkich testów z tym i widzę, że problem polega na tym, że nadal używam ksh93+jako mojej podstawowej powłoki. Ten kod działa zgodnie z obietnicą w ksh. ale ponieważ OP jest oznaczony tagiem bash, rozumiem twój dv. Zobacz wyniki testów bash w odpowiedzi @ Tyzoid. Powodzenia wszystkim!
łowca

Ponieważ wyjaśniłeś, że to nie działa w bash, usunę -1.
Mark

1
Dla mnie działa to świetnie w funkcji Bash, również można poczekać na proces, z wait $!którym nie wydaje się, aby tak było w przypadku zaakceptowanej odpowiedzi.
Jonatan Öström


6

Na podstawie tej odpowiedzi wymyśliłem bardziej zwięzłe i poprawne:

silent_background() {
    { 2>&3 "$@"& } 3>&2 2>/dev/null
    disown &>/dev/null  # Prevent whine if job has already completed
}
silent_background date

doskonały! i wielokrotnego użytku do innych rzeczy
user230910

5

Próbować:

user@host:~$ read < <( echo "Hello I'm a background task" & echo $! )
user@host:~$ echo $REPLY
28677

I ukryłeś zarówno wyjście, jak i PID . Zauważ, że nadal możesz pobrać PID z $ REPLY


1
To jest doskonałe! Dziękuję
Adham Atta,

4

Przepraszam za odpowiedź na stary post, ale myślę, że jest to przydatne dla innych i jest to pierwsza odpowiedź w Google.

Miałem problem z tą metodą (podpowłoki) i użyłem „czekaj”. Jednak ponieważ uruchamiałem go w funkcji, mogłem to zrobić:

function a {
    echo "I'm background task $1"
    sleep 5
}

function b {
    for i in {1..10}; do
        a $i &
    done
    wait
} 2>/dev/null

A kiedy to uruchomię:

$ b
I'm background task 1
I'm background task 3
I'm background task 2
I'm background task 4
I'm background task 6
I'm background task 7
I'm background task 5
I'm background task 9
I'm background task 8
I'm background task 10

I zanim otrzymam zwrotny monit, upływa 5 sekund.


1

Rozwiązanie podpowłoki działa, ale chciałem również móc czekać na zadania w tle (i nie mieć na końcu komunikatu „Gotowe”). $!z podpowłoki nie „czeka” w bieżącej powłoce interaktywnej. Jedynym rozwiązaniem, które zadziałało, było użycie własnej funkcji czekania, która jest bardzo prosta:

myWait() {
  while true; do
    sleep 1; STOP=1
    for p in $*; do
      ps -p $p >/dev/null && STOP=0 && break
    done
    ((STOP==1)) && return 0
  done
}

i=0
((i++)); p[$i]=$(do_whatever1 & echo $!)
((i++)); p[$i]=$(do_whatever2 & echo $!)
..
myWait ${p[*]}

Wystarczająco łatwe.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.