Bash: Cara menentukan apakah terminal dibuka oleh aplikasi pihak ketiga

9

Saya ingin skrip bash saya (khususnya saya ~/.bashrc) melakukan sesuatu hanya jika terminal dibuka oleh saya secara langsung, dan melakukan sesuatu yang lain jika dibuka melalui aplikasi misalnya VS Code. Bagaimana saya bisa menentukan apa masalahnya? Apakah ada variabel untuk itu? Terima kasih sebelumnya.

PaperBag
sumber
1
Ada semacam cara, tanggapan pertama saya adalah dengan contoh kedua di askubuntu.com/a/1042727/295286 . Coba buka VS dan jalankan envperintah. Lihat apakah ada variabel VS-spesifik yang bisa kita gunakan.
Sergiy Kolodyazhnyy
1
Jika tidak ada, coba sebaliknya: Lihat apakah emulator terminal Anda menetapkan variabel. Saya menggunakan yakuakedan memiliki PULSE_PROP_OVERRIDE_application.name=Yakuakeset variabel , dan xtermset XTERM_VERSION=XTerm(322)pada mesin saya.
hidangan penutup
@SergiyKolodyazhnyy Bisakah Anda menulis jawaban untuk pendekatan variabel lingkungan?
hidangan penutup
@Dabut saya akan, tetapi saya tidak memiliki VS diinstal, OP tidak merespon jika ada variabel lingkungan tertentu yang dapat kita lekatkan.
Sergiy Kolodyazhnyy
@SergiyKolodyazhnyy Aku juga tidak, tetapi judul pertanyaan mengatakan aplikasi pihak ketiga dan saya kira itu berfungsi seperti halnya emulator terminal - Saya pikir jawaban seperti env >env_term1dalam satu emulator, env >env_term2yang kedua dan bagaimana menggunakan apa yang diff env_term{1,2}dikatakan sangat berguna. Lagipula, OP mengatakan misalnya VS Code .
hidangan penutup

Jawaban:

10

Anda mungkin bisa melakukannya dengan berjalan kembali ke leluhur shell dan mencari tahu apakah itu dimulai oleh sesuatu yang setara dengan "Anda", atau program lain.

Dapatkan PID (ID proses) shell, dan dari itu PPID (ID proses induk). Terus naik sampai Anda menemukan sesuatu yang memberi tahu Anda dari mana asalnya. Anda mungkin perlu melakukan percobaan pada sistem Anda - setidaknya, saya tidak tahu apakah itu akan bersifat universal.

Misalnya, di sistem saya, dapatkan PID dari shell dan gunakan psuntuk menunjukkan bahwa itu bash:

$ echo $$
18852
$ ps --pid 18852
  PID TTY          TIME CMD
18852 pts/1    00:00:00 bash

Dapatkan PPID 18852:

$ ps -o ppid= -p 18852
18842

Cari tahu apa itu PPID (18842):

$ ps --pid 18842
  PID TTY          TIME CMD
18842 ?        00:00:02 gnome-terminal

Kita dapat melihatnya gnome-terminal, yaitu terminal emulator / terminal windows. Mungkin itu cukup baik untuk Anda, jika shell Anda diluncurkan oleh program lain tidak berjalan di jendela terminal emulator.

Jika tidak cukup baik, naik ke level lain:

$ ps -o ppid= -p 18842
 2313
$ ps --pid 2313
  PID TTY          TIME CMD
 2313 ?        00:00:00 init

Ini memberitahu kita bahwa gnome-terminalitu dimulai oleh init. Saya kira shell Anda yang dijalankan oleh program lain akan memiliki sesuatu yang berbeda di sana.

Mark Smith
sumber
... atau mungkin dengan berjalan menuju hasilpstree -s $$
steeldriver
9
"Ini memberitahu kita bahwa gnome-terminal dimulai oleh init" Saya merasa tidak mungkin init akan memulai windows terminal. Sebaliknya, apa pun yang dimulai terminal gnome mati, dan terminal gnome dipasangkan kembali untuk init. Memeriksa terminal gnome, sepertinya itu garpu ganda. Jadi ketika dijalankan, pertama-tama bercabang sendiri dan membunuh proses asli, melanjutkan yang baru.
JoL
@ Joil titik adil. Itu initproses tidak pid 1 meskipun, tidak yakin apakah itu akan mengubah apa pun.
kasperd
Terima kasih banyak! Saya dapat mendeteksi bahwa baik VS Code maupun Eclipse tidak menjalankan terminal sebagai anak gnome-terminal. Saya menjalankan perintah saya di bawah if [ $(pstree -s $$ | grep "gnome-terminal" -c) -gt 0 ]; then ...dan itu berhasil.
PaperBag
9

Sejauh Visual Studio Code berjalan, tampaknya ada cara untuk mengatur variabel lingkungan tambahan untuk terminal terintegrasi . Jadi, atur Visual Studio untuk menggunakan konfigurasi ini:

"terminal.integrated.env.linux": {
  "visual_studio": "true"
}

Dan di dalam ~/.bashrc:

if [ -n "$visual_studio" ]; then
    # do something for Visual Studio
else
    # do something else for other types of terminal
fi

Secara umum, Anda bisa mengandalkan lingkungan yang diberikan untuk bashproses tersebut. Misalnya, yang $TERMvariabel , dan menjalankan yang sama if..then...else...ficabang untuk [ "$TERM" = "xterm" ]atau sesuatu yang lain. Atas dasar kasus per kasus, Anda dapat menyelidiki perbedaan di lingkungan melalui menjalankan envdi setiap konsol, menyimpannya ke file seperti dalam env > output_console1.txt, dan diff output_console1.txt output_console2.txtseperti yang disarankan oleh pencuci mulut di komentar .

Sergiy Kolodyazhnyy
sumber
$Env:varbukan sintaks untuk variabel lingkungan di Bash. Ini sepertinya hal yang Powershell bagi saya.
Dietrich Epp
@DietrichEpp Ya, saya awalnya meneliti cara-cara untuk mengatur variabel lingkungan tambahan di Visual Studio, tetapi mengabaikan bahwa jawabannya menggunakan PowerShell. Jadi $foocukup. Kopi mungkin tidak cukup.
Sergiy Kolodyazhnyy
Untuk kasus umum program pihak ke-3 yang tidak memiliki pengaturan env, Anda dapat mengatur custom env var dalam pembungkus sebelum menjalankan program. Lihat jawaban saya .
Peter Cordes
2

Jika Anda berbicara tentang satu aplikasi pihak ketiga tertentu, maka gunakan variabel lingkungan. Sebagian besar program akan melewati seluruh lingkungan tidak berubah ketika mereka melakukan proses baru + fork.

Jadi, mulai aplikasi ini dengan custom env var yang dapat Anda periksa . mis. buat alias untuk suka alias vs=RUNNING_FROM_VSCODE=1 VSCode, atau buat skrip pembungkus seperti ini:

#!/bin/sh
export RUNNING_FROM_VSCODE=1
exec VSCode "$@"

Maka di Anda .bashrc, Anda bisa melakukannya

if (($RUNNING_FROM_VSCODE)); then
   echo "started from inside VSCode"
   # RUNNING_FROM_VSCODE=0  # optional if you only want the immediate child
fi

Pernyataan aritmatika bash (( ))benar jika ekspresi mengevaluasi ke integer non-nol (itulah sebabnya saya gunakan di 1atas). String kosong (untuk unset env var) salah. Ini bagus untuk variabel bash boolean, tetapi Anda bisa dengan mudah menggunakan truedan memeriksanya dengan POSIX tradisional

if [ "x$RUNNING_FROM_VSCODE" = "xtrue" ]; then
   echo "started from inside VSCode"
fi

Jika sebagian besar aplikasi Anda membersihkan lingkungan untuk anak-anaknya , tetapi masih tetap $PATHtidak berubah, Anda dapat menggunakan ini di pembungkus Anda:

#!/bin/sh
export PATH="$PATH:/dev/null/RUNNING_FROM_VSCODE"
exec VSCode "$@"

dan periksa dengan pencocokan pola seperti bash [[ "${PATH%RUNNING_FROM_VSCODE}" != "$PATH" ]]untuk memeriksa apakah melepas suffix dari PATH mengubahnya.

Ini seharusnya tidak berbahaya melakukan satu pencarian direktori tambahan ketika program mencari perintah eksternal yang tidak ditemukan. /dev/nulljelas bukan direktori pada sistem apa pun, jadi aman digunakan sebagai direktori palsu yang akan dengan cepat menghasilkan ENOTDIRjika pencarian PATH tidak menemukan apa yang mereka cari di entri PATH sebelumnya.

Peter Cordes
sumber
Skrip wrapper biasanya merupakan pendekatan yang masuk akal, karenanya +1. Satu-satunya kelemahan kecil adalah bahwa jika Anda memiliki 3 program, Anda mungkin ingin memiliki 3 skrip pembungkus atau satu skrip pembungkus mengambil 3 argumen yang berbeda, yang dapat membuatnya membosankan. Meskipun demikian itu adalah pendekatan yang solid.
Sergiy Kolodyazhnyy
1

Ini 2 sen saya. Cukup tambahkan ke .bashrc. Ganti terminalsdengan terminal favorit Anda dan exportperintah dengan terminal Anda.

run_in_terminal(){
  local parent_command="$(ps --no-headers --pid $PPID -o command | awk '{print $1;}')"
  local parent="$(basename $parent_command)"
  local terminals=( gnome-terminal st xterm ) # list your favorite terminal here
  if [[ ${terminals[*]} =~ ${parent} ]]; then
    # Your commands to run if in terminal
    export MY_VAR_IN_TERMINAL="test"
  fi
}
run_in_terminal
Zalatik
sumber
Ini tidak akan bekerja dengan model server-client gnome-terminal.
egmont