$ screenfetch
/:-------------:\ user@host
:-------------------:: OS: Fedora 30 Thirty
:-----------/shhOHbmp---:\ Kernel: x86_64 Linux 5.3.16-200.fc30.x86_64
/-----------omMMMNNNMMD ---: Uptime: 13h 27m
:-----------sMMMMNMNMP. ---: Packages: 2531
:-----------:MMMdP------- ---\ Shell: python3.8
,------------:MMMd-------- ---: Resolution: 1920x1080
:------------:MMMd------- .---: DE: GNOME
:---- oNMMMMMMMMMNho .----: WM: GNOME Shell
:-- .+shhhMMMmhhy++ .------/ WM Theme: Adwaita
:- -------:MMMd--------------: GTK Theme: Adwaita [GTK2/3]
:- --------/MMMd-------------; Icon Theme: Adwaita
:- ------/hMMMy------------: Font: Cantarell 11
:-- :dMNdhhdNMMNo------------; CPU: Intel Core i7-8550U @ 8x 4GHz
:---:sdNMMMMNds:------------: GPU: Mesa DRI Intel(R) UHD Graphics 620 (Kabylake GT2)
:------:://:-------------:: RAM: 6817MiB / 15779MiB
:---------------------://
Shell:を見ての通りちょっと悲しい。そもそもscreenfetchってどうシェルをとってるんだ?と思ったので読む話。あとできればXonshって表示してほしい。
screenfetchを読もう
screenfetchって1つのシェルスクリプトなんですね・・・こわ
どうやらscreenfetch-devってのが本体っぽい。
で、シェル関係が知りたいのでshellでCtrl+F。
すると黄色具合から1843行目のdetectshell()
があやしい。
とりあえず読んでみよう
ひとまず前半を示す。
detectshell () {
if [[ ! "${shell_type}" ]]; then
if [[ "${distro}" == "Cygwin" || "${distro}" == "Msys" || "${distro}" == "Haiku" || "${distro}" == "Alpine Linux" ||
"${distro}" == "Mac OS X" || "${distro}" == "TinyCore" || "${distro}" == "Raspbian" || "${OSTYPE}" == "gnu" ]]; then
shell_type=$(echo "$SHELL" | awk -F'/' '{print $NF}')
elif readlink -f "$SHELL" 2>&1 | grep -q -i 'busybox'; then
shell_type="BusyBox"
else
if [[ "${OSTYPE}" =~ "linux" ]]; then
shell_type=$(tr '\0' '\n' </proc/$PPID/cmdline | head -1)
elif [[ "${distro}" =~ "BSD" ]]; then
shell_type=$(ps -p $PPID -o command | tail -1)
else
shell_type=$(ps -p "$(ps -p $PPID | awk '$1 !~ /PID/ {print $1}')" | awk 'FNR>1 {print $1}')
fi
shell_type=${shell_type/-}
shell_type=${shell_type//*\/}
fi
fi
.....
myShell=${shell_type}
verboseOut "Finding current shell...found as '$myShell'"
}
実際-v
オプションをつけて見てみるとverboseOut "Finding current shell...found as '$myShell'"
の行が動いてることがわかる。
まずは最初のifの中を見よう。
$shell_type
ってのがシェル_タイプの変数っぽい(小泉進次郎風に)。
まずはOSの種類を見てからOSにあわせてシェルを取得してるっぽい。
OSの種類はlinuxなのでif [[ "${OSTYPE}" =~ "linux" ]]
のブロックを見よう。ちなみに=~
は正規表現比較らしい。いま知った。
まあ見たらわかるように、tr '\0' '\n' </proc/$PPID/cmdline | head -1
がシェル_タイプってこと。後ろでちょっと加工してるけどまあそれはまた後に。
/proc/$PPID/cmdline
は、大体名前から察するように$PPIDのプロセスのコマンド。$PPID
がなんのpidなのかわからないがおそらくシェルのだろう。どっか他のコードでとってるんじゃないですかね。まあ見てみよう。
$ ps
PID TTY TIME CMD
20316 pts/5 00:00:01 xonsh
21745 pts/5 00:00:00 ps
$ cat /proc/20316/cmdline
/home/tia/.pyenv/versions/3.8.1/bin/python3.8-u/home/tia/.pyenv/versions/3.8.1/bin/xonsh--login
はい。まあ予想通りですね。と思ったけどなんか変だ。python3.8-u???なにこれと思ったけど右の方を見て察した。スペースが抜けてる。
なんでだろ。まあいいか。
xonshは直接xonshのバイナリを起動してるんじゃなくてpythonを起動してる。いやそれはそう。当たり前やん。
ですがまあ、なんとなく原因がわかりましたね。screenfetchはシェルを起動してるコマンドを取得してシェルの名前を取り出しているので、直接シェルじゃなくてpythonを起動しているxonshはxonshじゃなくてpythonと見られてしまう。
ついでにtrコマンドとheadコマンドを見てみよう。僕がしらないので。
...ggりタイム...
tr
は文字の入れ替え。str.replace()
。echo hoge | tr 'hoge' 'fuga'
で'hoge'.replace('hoge', 'fuga')
って意味。pythonで言うと
head
は指定行の取り出し。str.splitlines(False)[n]
。echo "hoge\nfuga" | head -1
で'hoge\nfuga'.splitlines(False)[0]
って意味。
で、cmdlineの中身をtr '\0' '\n'
してる。うーん、あ、さっきスペースが抜けてるって思ったところ、スペースじゃなくて\0
が入ってるのかも知れない。まあ実際打ってみればわかるか。
$ tr '\0' '\n' < /proc/20316/cmdline
/home/tia/.pyenv/versions/3.8.1/bin/python3.8
-u
/home/tia/.pyenv/versions/3.8.1/bin/xonsh
--login
おっけい。多分それっぽい。ちゃんと改行されてる。
そして、これにhead -1
してるので、1行目を取り出している。つまり、/home/tia/.pyenv/versions/3.8.1/bin/python3.8
。
完全にこれだ。
で、最後に
shell_type=${shell_type/-}
shell_type=${shell_type//*\/}
。多分/の一番右を取り出してるんだろう。
これでシェル_タイプがわかりましたね。
さて、後半。
detectshell () {
......
case $shell_type in
bash)
shell_version_data=$( detectshell_ver "$shell_type" "^GNU.bash,.version" "4" )
;;
BusyBox)
shell_version_data=$( busybox | head -n1 | cut -d ' ' -f2 )
;;
csh)
shell_version_data=$( detectshell_ver "$shell_type" "$shell_type" "3" )
;;
dash)
shell_version_data=$( detectshell_ver "$shell_type" "$shell_type" "3" )
;;
ksh)
shell_version_data=$( detectshell_ver "$shell_type" "version" "5" )
;;
tcsh)
shell_version_data=$( detectshell_ver "$shell_type" "^tcsh" "2" )
;;
zsh)
shell_version_data=$( detectshell_ver "$shell_type" "^zsh" "2" )
;;
fish)
shell_version_data=$( fish --version | awk '{print $3}' )
;;
esac
if [[ -n $shell_version_data ]];then
shell_type="$shell_type $shell_version_data"
fi
myShell=${shell_type}
verboseOut "Finding current shell...found as '$myShell'"
}
これは・・・・・・完全無視だ。$shell_type
がbash, BusyBox, csh, dash, ksh, tcsh, zsh, fish
のどれかならバージョンを読むっぽい。無視無視。
さあて、結論が出ました。
xonshはbashとかのようにbash
と直接呼び出しているわけでなくpython xonsh
とpythonの上に動いているので、直接呼び出されているpytohnがシェルとして検出される。以上。
対策
detectshell()
の一行目を見てみましょう。
detectshell () {
if [[ ! "${shell_type}" ]]; then
...
そう、最初に既に$shell_type
が設定してあるか見てるのだ。つまり対策としては
$ $shell_type = $(xonsh -V).strip()
$ screenfetch
以上!
/:-------------:\ user@host
:-------------------:: OS: Fedora 30 Thirty
:-----------/shhOHbmp---:\ Kernel: x86_64 Linux 5.3.16-200.fc30.x86_64
/-----------omMMMNNNMMD ---: Uptime: 14h 28m
:-----------sMMMMNMNMP. ---: Packages: 2531
:-----------:MMMdP------- ---\ Shell: xonsh/0.9.13.dev1
,------------:MMMd-------- ---: Resolution: 1920x1080
:------------:MMMd------- .---: DE: GNOME
:---- oNMMMMMMMMMNho .----: WM: GNOME Shell
:-- .+shhhMMMmhhy++ .------/ WM Theme: Adwaita
:- -------:MMMd--------------: GTK Theme: Adwaita [GTK2/3]
:- --------/MMMd-------------; Icon Theme: Adwaita
:- ------/hMMMy------------: Font: Cantarell 11
:-- :dMNdhhdNMMNo------------; CPU: Intel Core i7-8550U @ 8x 4GHz
:---:sdNMMMMNds:------------: GPU: Mesa DRI Intel(R) UHD Graphics 620 (Kabylake GT2)
:------:://:-------------:: RAM: 6954MiB / 15779MiB
:---------------------://