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.