Ich möchte in ein Bash-Skript einen Code schreiben, der prüft, ob ein Programm bereits ausgeführt wird. Ich habe folgendes, um zu suchen, ob die Leiste läuft
foo=`ps -ef | grep bar | grep -v grep`
Das
grep -v grep
Teil ist sicherzustellen, dass die "Grep-Leiste" in ps-Ergebnissen nicht berücksichtigt wird
Wenn die Leiste nicht läuft, ist foo korrekt leer. Aber mein Problem liegt in der Tatsache, dass das Skript hat
set -e
Dies ist ein Flag zum Beenden des Skripts, wenn ein Befehl einen Fehler zurückgibt. Es stellt sich heraus, dass "grep -v grep" mit nichts übereinstimmt und grep einen Fehler zurückgibt, wenn der Balken nicht ausgeführt wird. Ich habe versucht, -q oder -s zu verwenden, aber ohne Erfolg.
Gibt es eine Lösung dafür? Danke
set -e
nicht bash-spezifisch ist, sondern für jede POSIX-kompatible Shell (sh
usw.) giltAntworten:
Sicher:
ps -ef | grep bar | { grep -v grep || true; }
Oder auch:
quelle
grep
1 zurückgegeben wird, weil keine Übereinstimmungen vorliegen. Was passiert jedoch, wenn 2 zurückgegeben wird (z. B. Fehler)?grep $options || true
ignoriert die Verwendung echte Fehler! Ich habe eine neue Antwort vorgeschlagen: stackoverflow.com/a/49627999/307637... | grep -v grep | cat
" verhindert NICHT, dass ein Skript fehlschlägt, wenn es vorhanden istset -o pipefail
, obwohl dies möglicherweise ausreichtset -e
. Der "... | { grep -v grep || true; }
" -Ansatz funktioniertset -o pipefail
, aber eine sauberere Lösung, die speziell auf fehlende Grep-Übereinstimmungen prüft, finden Sie in der Antwort von @ myrdd.Kurze Antwort
Schreiben
ps -ef | grep bar | { grep -v grep || test $? = 1; }
wenn Sie verwenden
set -e
.Wenn Sie die
pipefail
Option (set -o pipefail
) von bash verwenden , denken Sie daran, die Ausnahmebehandlung (||test
) auf alle Elementegrep
in der Pipeline anzuwenden :ps -ef | { grep bar || test $? = 1; } | { grep -v grep || test $? = 1; }
In Shell-Skripten empfehle ich Ihnen, die Dienstprogrammfunktion „catch-1-grep“ (c1grep) zu verwenden:
c1grep() { grep "$@" || test $? = 1; }
Erklärt
grep
Der Exit-Status ist entweder 0, 1 oder 2: [1]0
bedeutet, dass eine Linie ausgewählt ist1
bedeutet, dass keine Linien ausgewählt wurden2
bedeutet, dass ein Fehler aufgetreten istgrep
kann auch andere Codes zurückgeben, wenn diese durch ein Signal unterbrochen werden (z130
. B. für SIGINT).Da wir nur Exit - Status ignorieren wollen
1
, verwenden wir ,test
dass bestimmte Exit - Status zu unterdrücken.grep
zurückgegeben wird0
,test
wird nicht ausgeführt.grep
zurückgegeben wird1
,test
wird ausgeführt und zurückgegeben0
.grep
ein anderer Wert zurückgegeben wird,test
wird ausgeführt und zurückgegeben1
.Im letzten Fall wird das Skript aufgrund von
set -e
oder sofort beendetset -o pipefail
. Wenn Sie sich jedoch überhaupt nicht umgrep
Fehler kümmern , können Sie natürlich schreibenps -ef | grep bar | { grep -v grep || true; }
wie von Sean vorgeschlagen .
[zusätzliche] Verwendung in Shell-Skripten
Wenn Sie in Shell-Skripten
grep
viel verwenden, empfehle ich Ihnen, eine Dienstprogrammfunktion zu definieren:# "catch exit status 1" grep wrapper c1grep() { grep "$@" || test $? = 1; }
Auf diese Weise wird Ihre Pfeife wieder kurz und einfach, ohne die Funktionen von
set -e
und zu verlierenset -o pipefail
:Zu Ihrer Information:
c1grep
zu betonen, dass es einfach den Exit-Status erreicht1
, sonst nichts.grep
stattdessen die Funktion aufrufen können (grep() { env grep "$@" ...; }
), aber ich bevorzuge einen weniger verwirrenden und expliziteren Namenc1grep
.[zusätzlich]
ps
+grep
Wenn Sie also wissen möchten, wie Sie
grep -v grep
den| grep
Teil vermeiden oder gar vermeidenps|grep
können, werfen Sie einen Blick auf einige der anderen Antworten. aber das ist imho etwas off-topic.[1]
grep
Manpagequelle
Ein guter Trick, den Sie vermeiden sollten,
grep -v grep
ist folgender:ps -ef | grep '[b]ar'
Dieser reguläre Ausdruck stimmt nur mit der Zeichenfolge "bar" überein. In der
ps
Ausgabe wird die Zeichenfolge "bar" jedoch beim grep-Prozess nicht angezeigt.In den Tagen, bevor ich davon erfuhr
pgrep
, habe ich diese Funktion geschrieben, um den obigen Befehl zu automatisieren:psg () { local -a patterns=() (( $# == 0 )) && set -- $USER for arg do patterns+=("-e" "[${arg:0:1}]${arg:1}") done ps -ef | grep "${patterns[@]}" }
Dann,
verwandelt sich in
ps -ef | grep -e '[f]oo' -e '[b]ar'
quelle
grep [b]ar
und der reguläre Ausdruck[b]ar
kann nicht mit der Zeichenfolge übereinstimmen[b]ar
- der reguläre Ausdruck entspricht genau 3 Zeichen, während die Zeichenfolge 5 Zeichen enthältWarum sollten Sie
ps
nach massiven Produktionsmengen fragen ,-ef
wenn Sie nur 99% davon wegwerfen wollen?ps
und vor allem die GNU-Version ist ein Schweizer Taschenmesser mit praktischer Funktionalität. Versuche dies:Ich spezifiziere
-o pid=
hier nur weil, aber in der Tat ist es sinnlos, da wir sowieso alles von stdout wegwerfen. Es wäre jedoch nützlich, wenn Sie die tatsächlich laufende PID wissen möchten.ps
kehrt automatisch mit einem Status ungleich Null zurück, wenn-C
nichts übereinstimmt, und mit Null, wenn es übereinstimmt. Man könnte das einfach sagenps -C bar 1>/dev/null && echo bar running || echo bar not running
Oder
if ps -C bar 1>/dev/null ; then echo bar running else echo bar not running fi
Ist das nicht einfacher? Keine Notwendigkeit für grep, nicht zweimal oder sogar einmal.
quelle
grep
dass mit jeder Eingabe verwendet werden kann, nicht nur vonps
.foo=`ps -ef | grep bar | grep -v grep` || true
quelle
Versuchen Sie es so zu machen:
cat gibt immer 0 zurück und ignoriert den Exit-Code von grep
quelle
not found
einem anderen Fehler auftreten kann. Diese Lösung schlägt auch fehl, wenn Modi durch Befehleset -e
oderset -o pipefail
Befehle verursacht werden.