我對alias的從新認識:經過alias讓rm更安全

bash&shell系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.htmlhtml


rm的悲劇老是發生在不經意之間,因此不管是在shell腳本中仍是交互式bash環境下,執行rm命令時總應該三思三思再三思。也所以,不少人想盡辦法防止文件誤刪除,方法也各類各樣。shell

1.1 alias通常用法

1.默認rm是"rm -i"的別名,ll就是"ls -l"的別名。能夠自定義別名來代替某些命令配合某些選項,也能夠定義別名組合多個命令。例如:安全

[root@xuexi ~]# alias ls='ls -lA'

這樣在列出目錄時將同時列出隱藏文件。bash

2.使用不帶參數的alias將列出當前shell環境下全部的已定義的別名。函數

3.另外須要說明的是,當別名和命令同名時,將優先執行別名(不然別名就沒有意義了),這能夠從which的結果中看出:測試

[root@xuexi ~]# which mv
alias mv='mv -i'
        /bin/mv

若是定義的命名名稱和原始命令同名(例如定義的別名 ls='ls -l' ),此時若是想要明確使用原始命令,能夠刪除別名或者使用絕對路徑或者使用轉義符來還原命令。spa

4.alias命令是臨時定義別名,要定義長久生效的別名就將別名定義語句寫入/etc/profile或~/.bash_profile或~/.bashrc,第一個對全部用戶有效,後面兩個對對應用戶有效。修改後記得使用source來從新調取這些配置文件。code

5.使用unalias能夠臨時取消別名。htm

1.2 alias的缺陷

別名這東西定義和使用起來有點模糊,如下面這個別名命令爲例,在有的shell腳本的書籍上使用了這樣的定義,但倒是錯誤的,緣由稍後說明。blog

alias rmm='cp $@ ~/backup;rm $@'

該別名的目的是刪除文件時先備份到一個目錄下,而後再刪除。按照man bash裏的說明,別名rmm只是第一個cp命令的別名,分號後的rm不是別名的一部分,而是緊跟在別名後的下一行命令。當執行別名rmm時,首先讀取別名到分號位置處,而後進行別名擴展,執行完別名命令後,再執行分號後的rm命令。

之因此說上面的命令是錯誤的命令,問題出在cp的參數"$@",該變量本表示提供的全部參數,但因爲cp命令後使用分號分隔並定義了另外一個命令,這使得執行別名命令時,參數沒法傳遞到cp命令上,而只能傳遞到最後一個命令rm上,也就是說cp後的"$@"是空值。因此該別名等價於:

alias rmm='cp ~/backup;rm $@'

是否真的如此,使用echo測試一番便可。

[root@xuexi ~]# alias rmm='echo cp $@ ~/backup;echo rm $@'
[root@xuexi ~]# rmm /etc/fstab /etc/hosts
cp /root/backup
rm /etc/fstab /etc/hosts

從上面的結果中看到cp後的"$@"根本就沒有進行擴展,而是空值。

那若是別名定義語句中沒有使用分號或其餘方法定義額外的命令,而是隻有一個命令呢?別名必定就能正確工做嗎?非也。如下面的例子爲例:

[root@xuexi ~]# alias rmm='echo mv -f $@ ~/backup'

[root@xuexi ~]# rmm /etc/fstab /etc/hosts
mv -f /root/backup /etc/fstab /etc/hosts

發現問題了嗎?"$@"是擴展在"~/backup"目錄以後的,也就是說下面mv的別名想要替代rm,是沒法正常工做的:

alias rm='mv -f $@ ~/backup'

之因此沒法正常工做,是由於~/backup也是"$@"的一部分,且是"$@"中最前面的參數。執行下面的命令就知道了:

[root@xuexi ~]# echo mv -f "$@" ~/backup /etc/fstab /etc/hosts
mv -f /root/backup /etc/fstab /etc/hosts

從上面的分析能夠知道,alias是有其缺陷的,它只適合進行簡單的命令和參數替換、補全,想要實現複雜的命令替代有點難度。所以man bash中建議儘可能使用函數來取代別名(For almost every purpose, aliases are superseded by shell functions)。

1.3 別名的最佳實現

毫無疑問,寫個shell腳本比別名安全、完整多了,這是替代別名的一種方法。而我我的的建議是,在別名的定義語句中使用函數來克服別名的缺陷。

例如,爲了讓rm安全執行,使用如下兩種方法定義別名:

alias rm='copy1(){ /bin/cp -a $@ ~/backup;rm $@; };copy1 $@'
alias rm='move1(){ /bin/mv -f $@ ~/backup; };move1 $@'

由於執行別名時的參數只能傳遞給最後一個命令即copy1或move1函數,但"$@"表明的參數能夠傳遞給函數,讓函數中的"$@"獲得正確的擴展,因而整個別名都能合理且正確地執行。

或者直接定義一個shell function替代rm。例如向/etc/profile.d/rm.sh文件中寫入:

function rm(){ [ -d ~/rmbackup ] || mkdir ~/rmbackup;/bin/mv -f $@ ~/rmbackup; }
chmod +x /etc/profile.d/rm.sh
source /etc/profile.d/rm.sh

如此,執行rm命令時,便會執行此處定義的rm函數,使得rm變得更安全。但注意,這樣的函數默認沒法直接在腳本中使用,除非使用 export -f function_name 導出函數,使其能夠被子shell繼承。因此,可在/etc/profile.d/rm.sh文件的尾部加上導出語句:

function rm(){ [ -d ~/rmbackup ] || mkdir ~/rmbackup;/bin/mv -f $@ ~/rmbackup; }
export -f rm

若是function名和命令名相同,則默認優先執行function,除非使用command明確指定。例如上面定義了rm函數,若是想執行rm命令,除了使用/bin/rm,還能夠以下操做:

command rm a.txt

若是是在shell腳本里涉及到rm命令,那麼更建議在每次rm以前先cd到那個目錄下,而後再rm相對路徑,這樣至少能保證不出現符號"/"。固然,更重要的是腳本習慣一些編寫腳本的規範,印在骨子裏那種,就算想出問題也難。

相關文章
相關標籤/搜索