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.

Alex Spurling
źródło
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" &)
Dimitre Radoulov
źródło
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
źródło
@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 $
łuskarz
źródło
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

Opierając się na powyższej odpowiedzi, jeśli chcesz zezwolić na przechodzenie stderr z polecenia:

f() { echo "Hello I'm a background task" >&2; }
{ f 2>&3 &} 3>&2 2>/dev/null
pajamian
źródło
Piękne wykorzystanie przekierowań. Mimo to disownw niektórych okolicznościach będziesz potrzebować .
Tom Hale
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
Tom Hale
źródło
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

mark_infinite
źródło
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.

Tomahawk
źródło
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.

ExpertNoob1
źródło