Ausführen von Bash-Befehlen im Hintergrund ohne Druckauftrags- und Prozess-IDs

83

Es ist ziemlich einfach, einen Prozess im Hintergrund in Bash auszuführen.

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

Die Ausgabe ist jedoch ausführlich. In der ersten Zeile werden die Job-ID und die Prozess-ID der Hintergrundaufgabe gedruckt, dann haben wir die Ausgabe des Befehls, schließlich haben wir die Job-ID, ihren Status und den Befehl, der den Job ausgelöst hat.

Gibt es eine Möglichkeit, die Ausgabe einer Hintergrundaufgabe so zu unterdrücken, dass die Ausgabe genauso aussieht wie ohne das kaufmännische Und am Ende? Dh:

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

Der Grund, den ich frage, ist, dass ich einen Hintergrundprozess als Teil eines Tab-Vervollständigungsbefehls ausführen möchte, sodass die Ausgabe dieses Befehls nicht unterbrochen werden muss, um einen Sinn zu ergeben.

Alex Spurling
quelle
Ich nehme an, Sie sollten 2> / dev / null zu allem hinzufügen, was Sie in Bash-Completion-Skripten tun
siehe

Antworten:

98

Nicht im Zusammenhang mit der Fertigstellung, aber Sie können diese Ausgabe unterdrücken, indem Sie den Aufruf in eine Subshell einfügen:

(echo "Hello I'm a background task" &)
Dimitre Radoulov
quelle
3
Danke, das funktioniert definitiv. Wenn Sie ein Skript wie dieses über eine Bash-Vervollständigungsfunktion ausführen, scheint es leider so, als würde Bash warten, bis der Unterprozess abgeschlossen ist, bevor die Vervollständigungsergebnisse angezeigt werden. Dies ist unglücklich, da es nicht möglich zu sein scheint, Hintergrundarbeiter durch Bash-Vervollständigung auszulösen.
Alex Spurling
13
Sie können dies nicht verwenden, wenn waitder Hintergrundjob abgeschlossen werden soll.
Arekolek
13

Aufbauend auf der Antwort von @ shellter funktionierte dies für mich:

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:~$

Ich kenne die Gründe dafür nicht, aber ich erinnerte mich an einen alten Beitrag, der durch Verleugnung verhindert, dass Bash die Prozess-IDs ausgibt.

Tyzoid
quelle
@ Tyzoid. Ich habe Ihre Lösung in einer Bash-Shell (Version 4.2.37) ausprobiert und interessante Ergebnisse erzielt. 1. Ich habe die gleichen Ergebnisse nur ;am Ende hinzugefügt . (ohne Schlaf) . 2. ABER bemerkt, dass, als ich echo STDERR 2>&1in der "nächsten" Zeile hinzufügte , das [1]+ Done ...Ding erschien! . 3. Meine Lösung (wie ich jetzt bemerkt habe) funktioniert in kshund echo STDERR 2>&1erzeugt nur die Ausgabe STDERR. Nun, lebe und lerne für uns beide. Allen viel Glück.
Shellter
12

In einigen neueren Versionen von bash und in ksh93 können Sie es mit einer Sub-Shell oder einer Prozessgruppe (dh { ... }) umgeben.

/home/shellter $ { echo "Hello I'm a background task" & } 2>/dev/null
Hello I'm a background task
/home/shellter $
Shellter
quelle
5
Wenn ich geschweifte Klammern verwende, wird die letzte Ausgabezeile weiterhin angezeigt (ich bin mir nicht sicher, warum Sie sie nicht sehen).
Alex Spurling
Ich habe das genau kopiert, aber es funktioniert bei mir nicht. Es zeigt immer noch die Ausgabe, wie @AlexSpurling sagt. Vielleicht mache ich etwas falsch, seit andere es positiv bewertet haben, aber ich fürchte, ich muss es -1 geben.
Mark
1
@ Mark: Ich habe gerade einige schnelle Tests damit gemacht und ich sehe das Problem darin, dass ich weiterhin ksh93+als meine primäre Shell verwende. Dieser Code funktioniert wie in ksh versprochen. aber da das OP mit markiert ist, bashverstehe ich Ihren dv. Die Ergebnisse der Bash-Tests finden Sie in der Antwort von @ Tyzoid. Allen viel Glück!
Shellter
Da Sie klargestellt haben, dass dies in Bash nicht funktioniert, entferne ich die -1.
Mark
1
Für mich funktioniert dies hervorragend in einer Bash-Funktion, auch auf den Prozess kann gewartet werden, wait $!was bei der akzeptierten Antwort nicht der Fall zu sein scheint.
Jonatan Öström
6

Aufbauend auf der obigen Antwort, wenn Sie zulassen möchten, dass stderr vom Befehl durchkommt:

f() { echo "Hello I'm a background task" >&2; }
{ f 2>&3 &} 3>&2 2>/dev/null
pajamian
quelle
Schöne Verwendung von Weiterleitungen. Trotzdem benötigen Sie unter disownbestimmten Umständen eine .
Tom Hale
6

Basierend auf dieser Antwort habe ich mir das prägnantere und korrektere ausgedacht:

silent_background() {
    { 2>&3 "$@"& } 3>&2 2>/dev/null
    disown &>/dev/null  # Prevent whine if job has already completed
}
silent_background date
Tom Hale
quelle
perfekt! und wiederverwendbar für andere Sachen
user230910
5

Versuchen:

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

Und Sie haben sowohl den Ausgang als auch die PID ausgeblendet . Beachten Sie, dass Sie die PID weiterhin von $ REPLY abrufen können

mark_infinite
quelle
1
Dies ist perfekt! Vielen Dank
Adham Atta
4

Entschuldigen Sie die Antwort auf einen alten Beitrag, aber ich denke, dies ist für andere nützlich und es ist die erste Antwort bei Google.

Ich hatte ein Problem mit dieser Methode (Subshells) und der Verwendung von 'wait'. Da ich es jedoch in einer Funktion ausführte, konnte ich Folgendes tun:

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

Und wenn ich es laufen lasse:

$ 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

Und es gibt eine Verzögerung von 5 Sekunden, bevor ich meine Aufforderung zurück bekomme.

Der Tomahawk
quelle
1

Die Subshell-Lösung funktioniert, aber ich wollte auch auf die Hintergrundjobs warten können (und am Ende nicht die Meldung "Fertig" haben). $!von einer Subshell ist in der aktuellen interaktiven Shell nicht "wartbar". Die einzige Lösung, die für mich funktioniert hat, war die Verwendung meiner eigenen Wartefunktion, die sehr einfach ist:

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[*]}

Leicht genug.

ExpertNoob1
quelle