Bagaimana saya bisa menentukan shell yang sedang saya kerjakan?
Apakah output dari ps
perintah saja sudah cukup?
Bagaimana ini bisa dilakukan dalam rasa Unix yang berbeda?
Bagaimana saya bisa menentukan shell yang sedang saya kerjakan?
Apakah output dari ps
perintah saja sudah cukup?
Bagaimana ini bisa dilakukan dalam rasa Unix yang berbeda?
Jawaban:
Ada tiga pendekatan untuk menemukan nama executable shell saat ini:
Harap dicatat bahwa ketiga pendekatan dapat dibodohi jika executable shell adalah /bin/sh
, tetapi itu benar-benar diganti namanya bash
, misalnya (yang sering terjadi).
Jadi pertanyaan kedua Anda apakah ps
output akan dilakukan dijawab dengan " tidak selalu ".
echo $0
- akan mencetak nama program ... yang dalam kasus shell adalah shell yang sebenarnya.
ps -ef | grep $$ | grep -v grep
- ini akan mencari ID proses saat ini dalam daftar proses yang sedang berjalan. Karena proses saat ini adalah shell, itu akan dimasukkan.
Ini tidak 100% dapat diandalkan, karena Anda mungkin memiliki proses lain yang ps
cantumannya menyertakan nomor yang sama dengan ID proses shell, terutama jika ID itu adalah angka kecil (misalnya, jika PID shell adalah "5", Anda mungkin menemukan proses yang disebut "java5" atau "perl5" dalam grep
output yang sama !). Ini adalah masalah kedua dengan pendekatan "ps", di atas tidak bisa mengandalkan nama shell.
echo $SHELL
- Jalur ke shell saat ini disimpan sebagai SHELL
variabel untuk setiap shell. Peringatan untuk yang satu ini adalah bahwa jika Anda meluncurkan shell secara eksplisit sebagai subprocess (misalnya, itu bukan shell login Anda), Anda akan mendapatkan nilai shell login Anda sebagai gantinya. Jika itu suatu kemungkinan, gunakan pendekatan ps
atau $0
.
Namun, jika executable tidak cocok dengan shell Anda yang sebenarnya (mis. /bin/sh
Sebenarnya bash atau ksh), Anda memerlukan heuristik. Berikut adalah beberapa variabel lingkungan khusus untuk berbagai kerang:
$version
diatur pada tcsh
$BASH
diatur pada bash
$shell
(huruf kecil) diatur ke nama shell aktual di csh atau tcsh
$ZSH_NAME
diatur pada zsh
ksh memiliki $PS3
dan $PS4
mengatur, sedangkan shell Bourne normal ( sh
) hanya memiliki $PS1
dan $PS2
mengatur. Ini umumnya tampaknya yang paling sulit untuk dibedakan - satu - satunya perbedaan dalam seluruh rangkaian variabel lingkungan antara sh
dan ksh
kami telah menginstal pada Solaris boxen adalah $ERRNO
,$FCEDIT
, $LINENO
, $PPID
, $PS3
, $PS4
, $RANDOM
, $SECONDS
, dan $TMOUT
.
ps -p $$
seperti yang ditunjukkan oleh Matthew Slattery . Untuk ksh
: echo $KSH_VERSION
atau echo ${.sh.version}
.
echo ${.sh.version}
mengembalikan "Pergantian Buruk". Lihat solusi saya di atas
ps -ef | grep …
... Ini bukan 100% diandalkan sebagai ...” Menggunakan ekspresi reguler sederhana melalui egrep
atau grep -e
dengan mudah dapat membawa keandalan hingga untuk-semua-maksud-dan-tujuan 100%: ps -ef | egrep "^\s*\d+\s+$$\s+"
. The ^
merek yakin kita mulai dari awal baris, yang \d+
makan sampai UID, yang $$
sesuai dengan PID, dan \s*
dan \s+
account untuk & memastikan spasi antara bagian-bagian lain.
ps -ef | awk '$2==pid' pid=$$
ps -p $$
harus bekerja di mana saja yang melibatkan ps -ef
dan grep
dilakukan solusi (pada varian Unix apa pun yang mendukung opsi POSIXps
) dan tidak akan menderita dari false positive yang diperkenalkan dengan mengambil urutan angka yang mungkin muncul di tempat lain.
ps
yang mungkin tidak dimengerti -p
sehingga Anda mungkin perlu menggunakannya /bin/ps -p $$
.
$$
kecuali fish
yang harus Anda gunakan ps -p %self
.
/bin/ps
. ps
bisa dengan mudah (sebenarnya itu cukup normal saat ini) dipasang di /usr/bin
. $(which ps) -p $$
adalah cara yang lebih baik. Tentu saja, ini tidak akan berhasil pada ikan, dan mungkin beberapa kerang lainnya. Saya pikir itu (which ps) -p %self
pada ikan.
readlink /proc/$$/exe
sh
ditiru oleh bash
, ps -p memberi Anda /usr/bin/bash
bahkan Anda menjalankannya sebagaish
Mencoba
ps -p $$ -oargs=
atau
ps -p $$ -ocomm=
ps -o fname --no-headers $$
.
test `ps -p $$ -ocomm=` == "bash" && do_something_that_only_works_in_bash
. (Baris berikutnya dalam skrip saya memiliki yang setara dengan csh.)
-q
sebagai gantinya -p
:SHELL=$(ps -ocomm= -q $$)
Jika Anda hanya ingin memastikan pengguna menggunakan skrip dengan Bash:
if [ ! -n "$BASH" ] ;then echo Please run this script $0 with bash; exit 1; fi
#!/bin/bash
: if [ ! -n "$BASH" ] ;then exec bash $0; fi
. Dengan baris ini skrip dijalankan menggunakan bash bahkan jika mulai menggunakan ksh atau sh. Kasus penggunaan saya tidak memerlukan argumen baris perintah, tetapi mereka dapat ditambahkan setelah $0
jika perlu.
Anda dapat mencoba:
ps | grep `echo $$` | awk '{ print $4 }'
Atau:
echo $SHELL
/pattern/ { action }
?
$SHELL
variabel lingkungan berisi shell, yang dikonfigurasi sebagai default untuk pengguna saat ini. Itu tidak mencerminkan shell, yang sedang berjalan. Juga lebih baik digunakan ps -p $$
daripada menangkap $$ karena positif palsu.
awk
,ps | awk '$1=='$$' { n=split($4,a,"/"); print a[n] }'
$SHELL
tidak perlu selalu menampilkan shell saat ini. Ini hanya mencerminkan shell default yang akan dipanggil.
Untuk menguji di atas, katakanlah bash
shell default, coba echo $SHELL
, dan kemudian di terminal yang sama, masuk ke beberapa shell lain ( KornShell (ksh) misalnya) dan coba$SHELL
. Anda akan melihat hasilnya sebagai bash dalam kedua kasus.
Untuk mendapatkan nama shell saat ini, Gunakan cat /proc/$$/cmdline
. Dan path ke shell dieksekusi oleh readlink /proc/$$/exe
.
/proc
.
Saya punya trik sederhana untuk menemukan shell saat ini. Cukup ketikkan string acak (yang bukan perintah). Ini akan gagal dan mengembalikan kesalahan "tidak ditemukan", tetapi pada awal baris itu akan mengatakan shell mana itu:
ksh: aaaaa: not found [No such file or directory]
bash: aaaaa: command not found
echo 'aaaa' > script; chmod +x script; ./script
memberi./script.sh: 1: aaaa: not found
Berikut ini akan selalu memberikan shell aktual yang digunakan - itu mendapatkan nama executable aktual dan bukan nama shell (yaitu ksh93
bukannya ksh
, dll.). Sebab /bin/sh
, itu akan menunjukkan shell aktual yang digunakan, yaitu dash
.
ls -l /proc/$$/exe | sed 's%.*/%%'
Saya tahu bahwa ada banyak yang mengatakan bahwa ls
output tidak boleh diproses, tetapi berapa probabilitas Anda akan memiliki shell yang Anda gunakan yang diberi nama dengan karakter khusus atau ditempatkan di direktori bernama dengan karakter khusus? Jika ini masih terjadi, ada banyak contoh lain melakukannya secara berbeda.
Seperti yang ditunjukkan oleh Toby Speight , ini akan menjadi cara yang lebih tepat dan lebih bersih untuk mencapai hal yang sama:
basename $(readlink /proc/$$/exe)
/proc
. Tidak semua kotak Linux di dunia.
basename $(readlink /proc/$$/exe)
untuk ls
+ sed
+ echo
.
ash -> /bin/busybox
, ini akan memberikan / bin / busybox.
Saya telah mencoba berbagai pendekatan dan yang terbaik untuk saya adalah:
ps -p $$
Ini juga berfungsi di bawah Cygwin dan tidak dapat menghasilkan false positive sebagai PID grepping. Dengan sedikit pembersihan, hanya menghasilkan nama yang dapat dieksekusi (di bawah Cygwin dengan path):
ps -p $$ | tail -1 | awk '{print $NF}'
Anda dapat membuat fungsi sehingga Anda tidak harus menghafalnya:
# Print currently active shell
shell () {
ps -p $$ | tail -1 | awk '{print $NF}'
}
... dan kemudian jalankan shell
.
Itu diuji di bawah Debian dan Cygwin.
ps
, tail
dan gawk
, cmd tidak mendefinisikan $$
PID jadi itu pasti tidak dapat bekerja di bawah cmd polos.
ps -p$$ -o comm=
? POSIX mengatakan bahwa menentukan semua header kosong akan menekan header sepenuhnya. Kami masih gagal (seperti semua ps
jawaban) ketika kami bersumber dari skrip yang dieksekusi langsung (mis #!/bin/sh
.).
Varian saya saat mencetak proses induk:
ps -p $$ | awk '$1 == PP {print $4}' PP=$$
Jangan menjalankan aplikasi yang tidak perlu ketika AWK dapat melakukannya untuk Anda.
awk
, kapan ps -p "$$" -o 'comm='
bisa melakukannya untuk Anda?
Ada banyak cara untuk mengetahui shell dan versinya. Inilah beberapa yang bekerja untuk saya.
Mudah
Pendekatan hackish
$> ******* (Ketikkan satu set karakter acak dan dalam output Anda akan mendapatkan nama shell. Dalam kasus saya -bash: chapter2-a-sample-isomorphic-app: perintah tidak ditemukan )
Asalkan Anda /bin/sh
mendukung standar POSIX dan sistem Anda memiliki lsof
perintah yang terinstal - kemungkinan alternatif lsof
dalam hal ini adalah pid2path
- Anda juga dapat menggunakan (atau mengadaptasi) skrip berikut yang mencetak path lengkap:
#!/bin/sh
# cat /usr/local/bin/cursh
set -eu
pid="$$"
set -- sh bash zsh ksh ash dash csh tcsh pdksh mksh fish psh rc scsh bournesh wish Wish login
unset echo env sed ps lsof awk getconf
# getconf _POSIX_VERSION # reliable test for availability of POSIX system?
PATH="`PATH=/usr/bin:/bin:/usr/sbin:/sbin getconf PATH`"
[ $? -ne 0 ] && { echo "'getconf PATH' failed"; exit 1; }
export PATH
cmd="lsof"
env -i PATH="${PATH}" type "$cmd" 1>/dev/null 2>&1 || { echo "$cmd not found"; exit 1; }
awkstr="`echo "$@" | sed 's/\([^ ]\{1,\}\)/|\/\1/g; s/ /$/g' | sed 's/^|//; s/$/$/'`"
ppid="`env -i PATH="${PATH}" ps -p $pid -o ppid=`"
[ "${ppid}"X = ""X ] && { echo "no ppid found"; exit 1; }
lsofstr="`lsof -p $ppid`" ||
{ printf "%s\n" "lsof failed" "try: sudo lsof -p \`ps -p \$\$ -o ppid=\`"; exit 1; }
printf "%s\n" "${lsofstr}" |
LC_ALL=C awk -v var="${awkstr}" '$NF ~ var {print $NF}'
-i
(-> abaikan lingkungan) env
di baris tempat Anda memeriksa lsof
ketersediaan. gagal dengan: env -i PATH="${PATH}" type lsof
->env: ‘type’: No such file or directory
Jika Anda hanya ingin memastikan bahwa Anda menjalankan (versi tertentu) Bash, cara terbaik untuk melakukannya adalah dengan menggunakan $BASH_VERSINFO
variabel array. Sebagai variabel array (baca-saja), variabel ini tidak dapat diatur di lingkungan, jadi Anda dapat yakin itu datang (jika sama sekali) dari shell saat ini.
Namun, karena Bash memiliki perilaku yang berbeda ketika dipanggil sh
, Anda juga perlu memeriksa $BASH
variabel lingkungan yang diakhiri /bash
.
Dalam skrip yang saya tulis yang menggunakan nama fungsi dengan -
(bukan garis bawah), dan tergantung pada array asosiatif (ditambahkan dalam Bash 4), saya memiliki pemeriksaan kewarasan berikut (dengan pesan kesalahan pengguna yang membantu):
case `eval 'echo $BASH@${BASH_VERSINFO[0]}' 2>/dev/null` in
*/bash@[456789])
# Claims bash version 4+, check for func-names and associative arrays
if ! eval "declare -A _ARRAY && func-name() { :; }" 2>/dev/null; then
echo >&2 "bash $BASH_VERSION is not supported (not really bash?)"
exit 1
fi
;;
*/bash@[123])
echo >&2 "bash $BASH_VERSION is not supported (version 4+ required)"
exit 1
;;
*)
echo >&2 "This script requires BASH (version 4+) - not regular sh"
echo >&2 "Re-run as \"bash $CMD\" for proper operation"
exit 1
;;
esac
Anda bisa menghilangkan pemeriksaan fungsional yang agak paranoid untuk fitur dalam kasus pertama, dan hanya berasumsi bahwa versi Bash masa depan akan kompatibel.
Tidak ada jawaban yang bekerja dengan fish
shell (tidak memiliki variabel $$
atau $0
).
Ini bekerja untuk saya (diuji pada sh
, bash
, fish
, ksh
, csh
, true
, tcsh
, dan zsh
; openSUSE 13.2):
ps | tail -n 4 | sed -E '2,$d;s/.* (.*)/\1/'
Perintah ini menghasilkan string seperti bash
. Di sini saya hanya menggunakan ps
,, tail
dan sed
(tanpa ekstensi GNU; coba tambahkan --posix
untuk memeriksanya). Itu semua adalah perintah POSIX standar. Saya yakin tail
bisa dihapus, tetapi sed
fu saya tidak cukup kuat untuk melakukan ini.
Menurut saya, solusi ini tidak terlalu portabel karena tidak berfungsi pada OS X. :(
sed: invalid option -- 'E'
bash 3.2.51 dan tcsh 6.15.00
Solusi saya:
ps -o command | grep -v -e "\<ps\>" -e grep -e tail | tail -1
Ini harus portabel di berbagai platform dan cangkang. Ini menggunakan ps
seperti solusi lain, tetapi tidak bergantung pada sed
dan awk
dan menyaring sampah dari perpipaan dan ps
itu sendiri sehingga shell harus selalu menjadi entri terakhir. Dengan cara ini kita tidak perlu bergantung pada variabel PID non-portabel atau memilih garis dan kolom yang tepat.
Saya telah menguji Debian dan macOS dengan Bash, Z shell ( zsh
), dan ikan (yang tidak bekerja dengan sebagian besar solusi ini tanpa mengubah ekspresi khusus untuk ikan, karena menggunakan variabel PID yang berbeda).
echo $$ # Gives the Parent Process ID
ps -ef | grep $$ | awk '{print $8}' # Use the PID to see what the process is.
grep $$
tidak bisa diandalkan. ps -ef | awk -v pid=$$ '$2==pid { print $8 }'
lebih baik, tapi kenapa tidak pakai saja ps -p $$
?
Di Mac OS X (dan FreeBSD):
ps -p $$ -axco command | sed -n '$p'
zsh
, dan itu memberi saya -bash
.
mutt
...: -b
Grepping PID dari output "ps" tidak diperlukan, karena Anda dapat membaca baris perintah masing-masing untuk setiap PID dari struktur direktori / proc:
echo $(cat /proc/$$/cmdline)
Namun, itu mungkin tidak lebih baik dari sekadar:
echo $0
Tentang menjalankan shell yang sebenarnya berbeda dari yang ditunjukkan namanya, satu ide adalah meminta versi dari shell menggunakan nama yang Anda dapatkan sebelumnya:
<some_shell> --version
sh
tampaknya gagal dengan kode keluar 2 sementara yang lain memberikan sesuatu yang bermanfaat (tetapi saya tidak dapat memverifikasi semuanya karena saya tidak memilikinya):
$ sh --version
sh: 0: Illegal option --
echo $?
2
Ini bukan solusi yang sangat bersih, tetapi melakukan apa yang Anda inginkan.
# MUST BE SOURCED..
getshell() {
local shell="`ps -p $$ | tail -1 | awk '{print $4}'`"
shells_array=(
# It is important that the shells are listed in descending order of their name length.
pdksh
bash dash mksh
zsh ksh
sh
)
local suited=false
for i in ${shells_array[*]}; do
if ! [ -z `printf $shell | grep $i` ] && ! $suited; then
shell=$i
suited=true
fi
done
echo $shell
}
getshell
Sekarang kamu bisa menggunakannya $(getshell) --version
.
Ini bekerja, meskipun, hanya pada kerang mirip KornShell (ksh).
dash
, yash
dll Biasanya, jika Anda menggunakan bash
, zsh
, ksh
, apa pun - Anda harus tidak peduli tentang hal-hal seperti itu.
ksh
set fitur sebagian besar merupakan subset dari bash
fitur (belum memeriksa ini dengan seksama sekalipun).
Lakukan hal berikut untuk mengetahui apakah shell Anda menggunakan Dash / Bash.
ls –la /bin/sh
:
jika hasilnya /bin/sh -> /bin/bash
==> Kemudian shell Anda menggunakan Bash.
jika hasilnya /bin/sh ->/bin/dash
==> Kemudian shell Anda menggunakan Dash.
Jika Anda ingin mengubah dari Bash ke Dash atau sebaliknya, gunakan kode di bawah ini:
ln -s /bin/bash /bin/sh
(ganti shell ke Bash)
Catatan : Jika perintah di atas menghasilkan kesalahan yang mengatakan, / bin / sh sudah ada, hapus / bin / sh dan coba lagi.
Silakan gunakan perintah di bawah ini:
ps -p $$ | tail -1 | awk '{print $4}'
echo $SHELL
lakukan apa yang Anda coba lakukan dan lakukan dengan baik. Yang kedua juga tidak baik, karena $SHELL
variabel lingkungan berisi shell default untuk pengguna saat ini, bukan shell yang sedang berjalan. Jika saya misalnya telah bash
ditetapkan sebagai shell default, jalankan zsh
dan echo $SHELL
, itu akan dicetak bash
.
echo $SHELL
mungkin sampah: ~ $ echo $SHELL /bin/zsh ~ $ bash bash-4.3$ echo $SHELL /bin/zsh bash-4.3$
Yang ini berfungsi dengan baik di Red Hat Linux (RHEL), macOS, BSD dan beberapa AIXes :
ps -T $$ | awk 'NR==2{print $NF}'
Atau, yang berikut ini juga harus berfungsi jika pstree tersedia,
pstree | egrep $$ | awk 'NR==2{print $NF}'
!
substitusi?) Mungkin lebih portabel daripada menemukan nama shell. Kebiasaan setempat mungkin membuat Anda menjalankan sesuatu bernama/bin/sh
yang sebenarnya bisa berupa abu, tanda hubung, bash, dll.