Apakah ada cara bagi skrip shell untuk mengetahui program mana yang telah menjalankannya?

13

Dalam dunia * nix, apakah ada cara bagi skrip shell untuk memiliki informasi tentang program mana yang telah menjalankannya?


Contoh:

/path/to/script1 /path/to/script_xyz

dalam skenario imajiner ini, script_xyzakan memiliki informasi jalur ( /path/to/script1)

atau

memproses PID

entitas yang telah menjalankannya.

Catatan: Saya ingin tahu tentang berbagai solusi dan pendekatan, saya tidak berharap ini benar-benar mungkin terjadi

Miloš Đakonović
sumber
3
Subjek Anda menanyakan program mana yang menjalankan skrip. Tetapi pertanyaan Anda yang sebenarnya tampaknya meminta juru bahasa naskah. Yang mana dari keduanya yang benar-benar Anda tanyakan?
kasperd
@kasperd Anda benar. Pertanyaannya adalah tentang program, tapi itu sebenarnya penerjemah. Itu sebabnya saya merasa bahwa ini tidak mungkin pada awalnya.
Miloš Đakonović

Jawaban:

23

Sering ada kebingungan antara proses forking dan eksekusi.

Ketika Anda melakukannya pada prompt bashshell.

$ sh -c 'exec env ps'

Proses P1 yang mengeluarkan $prompt itu sedang menjalankan bashkode. Itu bashkode garpu proses baru P2 yang mengeksekusi /bin/shyang kemudian mengeksekusi /usr/bin/env, yang kemudian mengeksekusi /bin/ps.

Jadi P2 pada gilirannya dieksekusi kode bash, sh, envdan ps.

ps(atau perintah lain seperti skrip yang akan kami gunakan di sini) tidak memiliki cara untuk mengetahui bahwa itu telah dieksekusi oleh envperintah.

Yang bisa dilakukan adalah mencari tahu apa proses id induknya, yang dalam hal ini akan menjadi P1 atau 1jika P1 telah mati dalam interval atau di Linux proses lain yang telah ditunjuk sebagai subreaper bukan 1.

Ia kemudian dapat meminta sistem untuk perintah apa yang sedang dijalankan proses (seperti dengan readlink /proc/<pid>/exedi Linux) atau argumen apa yang diteruskan ke perintah terakhir yang dijalankan (seperti dengan ps -o args= -p <pid>).

Jika Anda ingin skrip Anda tahu apa yang memintanya, cara yang dapat diandalkan adalah meminta penyerang memberi tahu. Itu bisa dilakukan misalnya melalui variabel lingkungan. Misalnya script1dapat ditulis sebagai:

#! /bin/sh -
INVOKER=$0 script2 &

Dan script2:

#! /bin/sh -
printf '%s\n' "I was invoked by $INVOKER"
# and in this case, we'll probably find the parent process is 1
# (if not now, at least one second later) as script1 exited just after
# invoking script2:
ps -fp "$$"
sleep 1
ps -fp "$$"
exit

$INVOKERakan ( umumnya ) berisi path ke script1. Dalam beberapa kasus, ini mungkin merupakan jalur relatif, dan jalur tersebut akan relatif terhadap direktori kerja saat ini pada saat script1dimulai. Jadi jika script1mengubah direktori kerja saat ini sebelum memanggil script2, script2akan mendapatkan informasi yang salah tentang apa yang memanggilnya. Jadi mungkin lebih baik untuk memastikan $INVOKERberisi path absolut (lebih disukai menjaga nama dasarnya) seperti dengan menulis script1sebagai:

#! /bin/sh -
mypath=$(
  mydir=$(dirname -- "$0") &&
  cd -P -- "$mydir" &&
  pwd -P) && mypath=$mypath/$(basename -- "$0") || mypath=$0

... some code possibly changing the current working directory
INVOKER=$mypath script2

Dalam shell POSIX, $PPIDakan berisi pid dari induk dari proses yang mengeksekusi shell pada saat inisialisasi shell. Setelah itu, seperti yang terlihat di atas, proses induk dapat berubah jika proses id $PPIDmati.

zshdalam zsh/systemmodul, dapat meminta pid induk saat ini dari shell (sub-) saat ini dengan $sysparams[ppid]. Dalam shell POSIX, Anda bisa mendapatkan ppid saat ini dari proses yang mengeksekusi penerjemah (dengan asumsi itu masih berjalan) dengan ps -o ppid= -p "$$". Dengan bash, Anda bisa mendapatkan ppid dari shell (sub-) saat ini dengan ps -o ppid= -p "$BASHPID".

Stéphane Chazelas
sumber
8

Ya, suatu program dapat mengetahui siapa orang tuanya.

Untuk mengilustrasikannya, mari kita buat dua skrip bash. Yang pertama melaporkan PID-nya dan memulai skrip kedua:

$ cat s1.sh
#!/bin/bash
echo s1=$$
bash s2.sh

Skrip kedua melaporkan ID prosesnya, PID induknya, dan baris perintah yang digunakan untuk menjalankan induknya:

$ cat s2.sh
#!/bin/bash
echo s2=$$ PPID=$PPID
echo "Parent command: $(ps -o cmd= -q $PPID)"

Sekarang, mari kita jalankan:

$ bash s1.sh
s1=17955
s2=17956 PPID=17955
Parent command: bash s1.sh

Seperti yang Anda lihat, skrip kedua sebenarnya tahu PID dari induknya. Menggunakan ps, PID itu mengungkapkan baris perintah yang digunakan untuk memanggil orang tua.

Untuk diskusi tentang PPID secara lebih mendalam, lihat jawaban Stéphane Chazelas .

John1024
sumber
Terima kasih. Menjalankan skrip contoh yang saya dapatkan s1, s2dan PPIDnilai tetapi kemudian, dalam beberapa baris setelah ERROR: Unsupported SysV option.dan beberapa baris dengan penjelasan tambahan dan - nilai kosong untukParent command
Miloš Đakonović
John menggunakan beberapa fitur ps yang tidak tersedia (atau disediakan secara berbeda) di platform Anda, periksa halaman manual ps (1) Anda.
Jasen
@ Miloshio Saya menguji di atas menggunakan psdari procps-ngpaket, versi 3.3.12. Seperti yang disarankan Jasen, Anda mungkin menggunakan versi yang berbeda yang mungkin memerlukan sintaks yang berbeda untuk mencetak baris perintah induk. Coba ps -f | grep $PPID.
John1024