Uzyskaj PID dowolnego polecenia w sekwencji poleceń w tle

11

Jeśli bashwykonam:

cmd1 | cmd2 | ... | cmdi | ... | cmdn &

gdzie cmd{1..n}mogą nie być wyraźne, w jaki sposób uzyskać PID cmdi? Alternatywnie, jak mogę zasygnalizować cmdiproces? (Na przykład, wysłać go SIGUSR1?) pkill/ pgrep, pidofItd. Nie wygląda dobrych odpowiedzi, od innych instancji cmdimoże działa, w tym w ramach tego samego rurociągu. jobs -ppodaje PID cmd1dla mnie.

imoże być cokolwiek {1..n}.

muru
źródło
możliwy duplikat Jak mogę uzyskać numer pid procesu rozpoczętego w ten sposób
G-Man mówi „Przywróć Monikę”
1
@ G-Man Chcesz wyjaśnić? Widzę tylko powierzchowne podobieństwo i jak wyjaśniłem w odpowiedzi Ramesha, modyfikowanie zestawu poleceń nie ma większego zastosowania.
muru
Powierzchowne podobieństwo? cat /var/run/out | nc -l 8080jest tylko pozornie podobny do cmd1 | cmd2? Twoje ograniczenie, że chcesz wpisać rurociąg z gołą kością, a następnie odzyskać PID, jest (1) nie wymienione w pytaniu i (2) jest mało prawdopodobne, aby pozwolić na dobre, ogólne rozwiązanie.
G-Man mówi „Reinstate Monica”
@ G-Man Przeciwnie, nakładasz ograniczenia, których proste nie są określone. cmd1 | cmd2jest bardzo szczególnym przypadkiem, w którym oba PID są łatwo dostępne. Czy mówiłem coś o n? Dlaczego więc miałbyś założyć n = 2? Czy mówiłem coś o tym, czym jest cmdi? Dlaczego więc miałbyś zakładać, że mogę zmodyfikować cmdi? Proszę o ogólne rozwiązanie, a pan nakłada ograniczenia.
muru

Odpowiedzi:

6

W oryginalnej wersji pytania, gdy pożądany był tylko PID ostatniego polecenia, specjalna zmienna $!jest idealna.

foo | bar | baz &
baz_pid=$!

Nie ma podobnego łatwego dostępu do PID innych procesów.

Dodanie $pipestatus(zsh) i $PIPESTATUS(bash) zajęło dużo czasu , dając w końcu dostęp do wszystkich statusów wyjściowych w potoku, oprócz $?ostatniego, który istniał od czasu oryginalnej powłoki Bourne'a. Może w $!końcu wydarzy się coś analogicznego .


źródło
Czy miałbyś coś przeciwko, gdybym zredagował pytanie, aby poprosić o PID dowolnego polecenia na liście? Czy powinienem zacząć nowe pytanie?
muru
Prawdopodobnie będziesz musiał dużo dłużej czekać na odpowiedź na to pytanie. Nie mam silnych odczuć co do organizacji witryny
W porządku, natychmiastowy problem został rozwiązany, teraz kieruje się ciekawością. Zmienię to wtedy. Tylko heads-up, ponieważ widziałem, jak pytania zmieniają się drastycznie, a starsze odpowiedzi wyglądają nie na miejscu.
muru
@muru - zwróć uwagę, że lepiej byłoby uczynić go nowym Q odnoszącym się do tego.
slm
@slm należycie odnotowano. Zrobi to w przyszłości.
muru
4

Myślę, że możesz zrobić coś, co sugerowano tutaj .

(ls -l | echo "Hello" | df -h & echo $! >&3 ) 3>pid

Tutaj w powyższym przykładzie pobrałem pid trzeciego procesu potokowego i zanotowałem go do pliku pid. Mogłabym to zanotować dla każdego procesu potokowego.

Ramesh
źródło
Ciekawe, ale wymagałoby to modyfikacji zestawu poleceń. Niewiele zużywa się po wykonaniu poleceń.
muru
@muru - co? jaki jest pożytek z PID po zakończeniu wykonywania? chcesz PID rurociągu? jobs -p. zasygnalizuj to za pomocą SIGPIPE. Czy chcesz cmdi- to.
mikeserv
1
@ mikeserv Nie, jeśli są w tle i działają tak, jak mówimy. Jaką magią mam zmodyfikować w tym celu linię poleceń?
muru
1
@muru, co byłoby czarnoksięstwem. potrzebujesz debuggera.
mikeserv
Uważam to za użyteczny wzorzec do uruchamiania procesów w tle, czekania, aż osiągną jakiś stan, a następnie ich zabijania. Jeśli ktoś jest zainteresowany: gist.github.com/MatrixManAtYrService/...
MatrixManAtYrService
2

Niezbyt przenośnym rozwiązaniem specyficznym dla systemu Linux może być śledzenie procesów za pomocą łączących je potoków. Możemy uzyskać PID pierwszego ( jobs -p) i ostatniego ( $!) polecenia w potoku. Używając PID, ten skrypt może wykonać zadanie:

#! /bin/bash

PROC=$1
echo $PROC

if [[ $(readlink /proc/$PROC/fd/1) =~ ^pipe: ]]
then
    # Assuming first process in chain...
    NEXT_FD=1
elif [[ $(readlink /proc/$PROC/fd/0) =~ ^pipe: ]]
then
    # Last process in chain...
    NEXT_FD=0
else
    # Doesn't look like a pipe.
    exit
fi

NEXT_PROC_PIPE=$(readlink /proc/$PROC/fd/$NEXT_FD)

while [[ $NEXT_PROC_PIPE =~ ^pipe: ]] 
do
    PROC=$(find /proc/*/fd -type l -printf "%p/%l\n" 2>/dev/null | awk -F'/' '($6 == "'"$NEXT_PROC_PIPE"'") && ($3 != "'$PROC'" ) {print $3}')
    NEXT_PROC_PIPE=$(readlink /proc/$PROC/fd/$NEXT_FD)
    echo $PROC
done
muru
źródło
Dla ciekawskich jest więcej na temat tego rodzaju rzeczy tutaj: unix.stackexchange.com/a/486233/146169
MatrixManAtYrService
0

W tym kodzie używam tablic zerowych. Uważaj tylko, przez co biegniesz eval.

#!/bin/bash

cmd=('sleep 10' 'sleep 2' 'sleep 5')
first=1
for c in "${cmd[@]}"; do
  ((first)) && { pipe=$c; first=0; } || pipe+='|'$c
done
shopt -u lastpipe
eval $pipe &

printf 'Pipe:\n%s\n\n' "$pipe"

shellpid=$BASHPID
parent=$(ps -o pid= --ppid $shellpid | head -n -1)
declare -a pids=()
mapfile -t pids < <(printf '%s\n' $(ps -o pid= --ppid $parent))
printf '%s\n' 'Listing the arrays:'
printf '%2s %6s %s\n' i PID command
for i in "${!cmd[@]}"; do
    printf '%2d %6d %s\n' "$i" "${pids[i]}" "${cmd[i]}"
done

printf '\n%s\n' 'ps listing:'
ps xao pid,ppid,command | head -n 1
ps xao pid,ppid,command | tail | head -n -3
jarno
źródło