Shell編程規範:Don't use ls | grep

1、背景

最近在寫一個私有化部署腳本的時候頻繁的使用了ls和grep的組合,可是插件ShellCheck卻一直在給我標黃,我這該死的潔癖終於在今天受不了了,這個使用了這麼多年的好搭檔怎麼就那麼不討喜呢!html

ls | grep file
ls | grep regexp
ls | grep -v multi

2、ShellCheck的規範

很明顯,提示告訴咱們不要使用ls|grep的搭配,建議咱們使用一個通配符或者帶有條件的循環。。。linux

一個ls都有這麼多講究,咱們來看個wiki的使用反例:git

$ touch 'a space' $'a\nnewline'
$ echo "don't taze me, bro" > a
$ ls | cat
a
a
newline
a space

ls後面用管道符輸出的結果怎麼和預想的不同??,咱們來看看文件列表:github

$ ls -l
total 8
-rw-r-----  1 lhunath  lhunath  19 Mar 27 10:47 a
-rw-r-----  1 lhunath  lhunath   0 Mar 27 10:47 a?newline
-rw-r-----  1 lhunath  lhunath   0 Mar 27 10:47 a space

Linux中的文件名是個神奇的存在,你能夠使用幾乎任意字符:空格、換行符、逗號、管道符等等。正則表達式

默認模式下,若是輸出不是終端的話,ls會用換行符分割開文件名,因此問題就來了:從ls的輸出中,您或計算機都沒法知道它的哪些部分構成了文件名。是每一個字嗎?不。是每行嗎?不。這個問題沒有正確答案,只有:你不知道。sql

哈哈哈,驚不驚喜?意不意外?固然,平時的使用其實都沒有問題的,只是插件不建議咱們這麼用,那我就是改改個人使用吧,潔癖太可怕了。。。shell

3、修改寫法

一、ls | grep file

這個其實挺好改的,個人本意是想看下prometheus的目錄權限、grafana目錄下grafana.db文件的權限bash

查一個目錄的權限能夠修改成(d指定只看目錄):koa

➜  service-monitor git:(master) ✗ ls -dl prometheus
drwxr-xr-x  17 Charles  staff  544  8 26 21:09 prometheus

因爲ls自己就有檢索的功能,查看文件的權限就很簡單了工具

➜  service-monitor git:(master) ✗ ls -l grafana/grafana.db
-rw-rw-rw-  1 Charles  staff  2232320  8 26 22:36 grafana/grafana.db

二、ls | grep regexp

簡化一下原有語句:

for tarfile in $(ls images | grep '.tar$'); do echo "loading $tarfile"; done

本意是想加載images文件夾下全部tar文件,先打印,後load鏡像。

咱們知道ls後面的查詢只能跟通配符,不能跟正則表達式,因此更改以下:

➜  build git:(master) ✗ ls images | grep '.tar$'
monitor.tar
➜  build git:(master) ✗ ls images/*.tar
images/monitor.tar

而後for循環可修改成:

➜  build git:(master) ✗ file=$(ls images | grep '.tar$')
➜  build git:(master) ✗ echo $file
monitor.tar
➜  build git:(master) ✗ file=images/*.tar
➜  build git:(master) ✗ echo $file
images/*.tar
➜  build git:(master) ✗ for tarfile in images/*tar; do echo "loading $tarfile"; done
loading images/monitor.tar

三、ls | grep -v multi

這個語句的本意是查詢出當前目錄下全部不是.sql或.zip結尾的文件
因爲ls不能用正則,也不能用反選,這個語句的修改一開始讓我還很頭疼,後來發現有個shell操做選項shopt,這個工具能夠激活或關閉指定的shell行爲選項。

好比我如今要打開extglob模式,該模式能夠給ls擴展匹配操做符,能使文件匹配更加方便. 否則不識別!

開啓命令:

shopt -s extglob

關閉命令:

shopt -u extglob

5個模式匹配操做符

  • ?(pattern-list) - 所給模式匹配0次或1次;
  • *(pattern-list) - 所給模式匹配0次以上包括0次;
  • +(pattern-list) - 所給模式匹配1次以上包括1次;
  • @(pattern-list) - 所給模式僅僅匹配1次;
  • !(pattern-list) - 不匹配括號內的所給模式。
#反選刪除文件:
#(打開extglob模式)
shopt -s extglob   
rm -fr !(file1)
 
#多個要排除的:
rm -rf !(file1|file2)

因此這時候咱們的修改就很簡單了

shopt -s extglob
rm -rf !(*.zip|*.sql) && ls -l

4、最後

改完以後心情豁然開朗,不少時候規範不僅是有束縛編碼行爲的好處,還能夠使你對代碼使用有着更深的理解。

5、參考

ShellCheck Wiki: Don't use ls | grep
ShellCheck Wiki: Iterating over ls output is fragile. Use globs.
Why you shouldn't parse the output of ls
Pattern Matching In Bash
shopt
linux extglob模式 和rm反選

相關文章
相關標籤/搜索