2

我經常使用find在巨大的源代碼樹中搜索文件和符號。如果我不限制目錄和文件類型,則需要花費幾分鐘的時間在文件中搜索符號。 (我已經安裝了源代碼樹SSD上,一半的搜索時間。)如何在變量中轉義特殊字符以在bash中提供命令行參數

我有幾個別名,以限制我要搜索的目錄,如:

alias findhg='find . -name .hg -prune -o' 
alias findhgbld='find . \(-name .hg -o -name bld \) -prune -o' 
alias findhgbldins='find . \(-name .hg -o -name bld -o -name install \) -prune -o' 

我當時也限制文件類型爲好,如:

findhgbldins \(-name '*.cmake' -o -name '*.txt' -o -name '*.[hc]' -o -name '*.py' -o -name '*.cpp' \) 

但有時我只想在cmake的文件來檢查符號:

findhgbldins \(-name '*.cmake' -o -name '*.txt' \) -exec egrep -H 'pattern' \; 

我可以做一大堆的別名對所有可能的組合,但是這將是一個容易得多,如果我可以使用變量來選擇文件類型,例如:

export SEARCHALL="\(-name '*.cmake' -o -name '*.txt' -o -name '*.[hc]' -o -name '*.py' -o -name '*.cpp' \)" 
export SEARCHSRC="\(-name '*.[hc]' -o -name '*.cpp' \)" 

,然後調用:

findhgbldins $SEARCHALL -exec egrep -H 'pattern' \; 

我試過了幾個變種的轉義\,(,*),但沒有組合工作。 我可以使它工作的唯一方法是在Bash中關閉globbing,即set -f,然後調用我的'find'-inteption然後再次打開globbing。我想出了

一個替代方案是定義的一組功能(具有相同的名稱作爲我的別名findhgfindhgbldins,和findhgbldins),其採取了在一個case結構中使用的簡單的參數,用於選擇不同的文件類型我找,像:

findhg { 
    case $1 in 
     '1') 
      find <many file arguments> ;; 
     '2') 
      find <other file arguments> ;; 
     ... 
    esac 
} 

findhgbld { 
    case $1 in 
     '1') 
      find <many file arguments> ;; 
     '2') 
      find <other file arguments> ;; 
     ... 
    esac 
} 

etcetera 

我的問題是:它在所有可能通過這些類型的參數命令作爲一個變量?

還是有可能以不同的方式來實現具有命令(findhgfindhgbldfindhgbldins)和一個單獨的參數來創建大量的組合用於搜索的組合相同,即?

+0

你需要'egrep'嗎? 'fgrep'會更簡單嗎? – cdarke

回答

2

真的不可能做到你想要的而沒有不愉快。基本問題是,當你展開一個沒有雙引號的變量時(例如findhgbldins $SEARCHALL),它會對變量的值進行分詞和擴展,但不會解釋引號或轉義,所以沒有辦法在變量的值來抑制glob膨脹(當然,除非你使用無效的glob模式,但是find也不能正確匹配它們)。在其周圍放置雙引號(findhgbldins "$SEARCHALL")會抑制glob擴展,但它也會抑制單詞分割,您需要讓find正確解釋表達式。您可以完全關閉全局擴展(set -f,正如您所提到的),但是它會關閉所有內容,而不僅僅是這個變量。

有一件事情會起作用(但會令人討厭使用),那就是將搜索選項放在數組中而不是普通變量中,例如,:

SEARCHALL=(\(-name '*.cmake' -o -name '*.txt' -o -name '*.[hc]' -o -name '*.py' -o -name '*.cpp' \)) 
findhgbldins "${SEARCHALL[@]}" -exec egrep -H 'pattern' \; 

但是這是一個很大打字使用它(以及你需要每一個報價,支架,撐,等拿到陣列向右展開)。不是很有幫助。

我的首選選項是構建一個函數,將其第一個參數解釋爲匹配的文件類型列表(例如findhgbldins mct -exec egrep -H 'pattern' \;可能會找到make/cmake,c/h和文本文件)。像這樣:

findhgbldins() { 
filetypes=() 
if [[ $# -ge 1 && "$1" != "-"* ]]; then # if we were passed a type list (not just a find primitive starting with "-") 
    typestr="$1" 
    while [[ "${#typestr}" -gt 0 ]]; do 
     case "${typestr:0:1}" in # this looks at the first char of typestr 
      c) filetypes+=(-o -name '*.[ch]');; 
      C) filetypes+=(-o -name '*.cpp');; 
      m) filetypes+=(-o -name '*.make' -o '*.cmake');; 
      p) filetypes+=(-o -name '*.py');; 
      t) filetypes+=(-o -name '*.txt');; 
      ?) echo "Usage: $0 [cCmpt] [find options]" >2 
       exit ;; 
     esac 
     typestr="${typestr:1}" # remove first character, so we can process the remainder 
    done 
    # Note: at this point filetypes will be something like '-o' -name '*.txt' -o -name '*.[ch]' 
    # To use it with find, we need to remove the first element (`-o`), and add parens 
    filetypes=(\("${filetypes[@]:1}" \)) 
    shift # and get rid of $1, so it doesn't get passed to `find` later! 
fi 

# Run `find` 
find . \(-name .hg -o -name bld -o -name install \) -prune -o "${filetypes[@]}" "[email protected]" 
} 

...你也可以使用類似的方法來建立一個目錄清單,如果你想。

正如我所說,這將是我的首選選項。但是,如果你真的想要使用變量方法,那就有一個技巧(我的意思是技巧)。它被稱爲magic alias,它利用了別名在通配符之前展開的事實,但函數之後被處理,並且在組合中完成了一些完全不自然的事情。事情是這樣的:

alias findhgbldins='shopts="$SHELLOPTS"; set -f; noglob_helper find . \(-name .hg -o -name bld -o -name install \) -prune -o' 
noglob_helper() { 
    "[email protected]" 
    case "$shopts" in 
     *noglob*) ;; 
     *) set +f ;; 
    esac 
    unset shopts 
} 
export SEARCHALL="(-name *.cmake -o -name *.txt -o -name *.[hc] -o -name *.py -o -name *.cpp)" 

然後,如果你運行findhgbldins $SEARCHALL -exec egrep -H 'pattern' \;,它擴展了別名,記錄了當前的shell選項,關閉通配符,並傳遞find命令(包括$ SEARCHALL,文字分裂而不是水珠展開)到noglob_helper,它運行帶有所有選項的find命令,然後再打開glob擴展(如果它在保存的shell選項中未被禁用),以免它稍後混淆。這是一個徹頭徹尾的黑客,但它應該實際上工作。

+0

您的功能很有用,謝謝!我唯一需要改變的是在文件類型前添加'-name'。例如。 'filetypes + =( - o -name'*。[ch]');;' – NZD

+0

D'oh!當我在發佈之前只進行測試時會發生這種情況。無論如何,我很高興它是有用的;我已經修復了它的記錄。 –

相關問題