Bagaimana saya bisa menentukan shell yang sedang saya kerjakan?
Apakah output dari psperintah saja sudah cukup?
Bagaimana ini bisa dilakukan dalam rasa Unix yang berbeda?
Bagaimana saya bisa menentukan shell yang sedang saya kerjakan?
Apakah output dari psperintah 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 psoutput 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 pscantumannya 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 grepoutput 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 SHELLvariabel 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 psatau $0.
Namun, jika executable tidak cocok dengan shell Anda yang sebenarnya (mis. /bin/shSebenarnya 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 $PS3dan $PS4mengatur, sedangkan shell Bourne normal ( sh) hanya memiliki $PS1dan $PS2mengatur. Ini umumnya tampaknya yang paling sulit untuk dibedakan - satu - satunya perbedaan dalam seluruh rangkaian variabel lingkungan antara shdan kshkami 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_VERSIONatau 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 egrepatau grep -edengan 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 -efdan grepdilakukan 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.
psyang mungkin tidak dimengerti -psehingga Anda mungkin perlu menggunakannya /bin/ps -p $$.
$$kecuali fishyang harus Anda gunakan ps -p %self.
/bin/ps. psbisa 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 %selfpada ikan.
readlink /proc/$$/exe
shditiru oleh bash, ps -p memberi Anda /usr/bin/bashbahkan 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.)
-qsebagai 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 $0jika perlu.
Anda dapat mencoba:
ps | grep `echo $$` | awk '{ print $4 }'
Atau:
echo $SHELL
/pattern/ { action }?
$SHELLvariabel 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] }'
$SHELLtidak perlu selalu menampilkan shell saat ini. Ini hanya mencerminkan shell default yang akan dipanggil.
Untuk menguji di atas, katakanlah bashshell 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; ./scriptmemberi./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 ksh93bukannya 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 lsoutput 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, taildan 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 psjawaban) 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/shmendukung standar POSIX dan sistem Anda memiliki lsofperintah yang terinstal - kemungkinan alternatif lsofdalam 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) envdi baris tempat Anda memeriksa lsofketersediaan. 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 $BASHvariabel 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 fishshell (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,, taildan sed(tanpa ekstensi GNU; coba tambahkan --posixuntuk memeriksanya). Itu semua adalah perintah POSIX standar. Saya yakin tailbisa dihapus, tetapi sedfu 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 psseperti solusi lain, tetapi tidak bergantung pada seddan awkdan menyaring sampah dari perpipaan dan psitu 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, yashdll Biasanya, jika Anda menggunakan bash, zsh, ksh, apa pun - Anda harus tidak peduli tentang hal-hal seperti itu.
kshset fitur sebagian besar merupakan subset dari bashfitur (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 $SHELLlakukan apa yang Anda coba lakukan dan lakukan dengan baik. Yang kedua juga tidak baik, karena $SHELLvariabel lingkungan berisi shell default untuk pengguna saat ini, bukan shell yang sedang berjalan. Jika saya misalnya telah bashditetapkan sebagai shell default, jalankan zshdan echo $SHELL, itu akan dicetak bash.
echo $SHELLmungkin 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/shyang sebenarnya bisa berupa abu, tanda hubung, bash, dll.