Bash

在函式內部設置 -f

  • March 16, 2019

我有一個包含文件的目錄file1.cfile2.c並且file3.c. 命令find 輸出:

$find -name "*.c"
./file1.c
./file2.c
./file3.c

然後我想使用find 不帶引號的.*c. 為此,我使用set -f

$echo $-            # check current options
himBHs
$set -f
$echo $-            # check that f option is set
fhimBHs
$find -name *.c
./file1.c
./file2.c
./file3.c
$set +f             # unset f option

我在函式中嘗試了相同的命令.bashrc

find() {
   set -f
   eval command find $@
   set +f
}

但測試它給出了錯誤:

$ . ~/.bashrc && find -name *c
find: paths must precede expression: file1.c
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression

函式中出現此錯誤的原因是什麼?find版本:GNU 4.6.0

你沒有說,但你必須像這樣呼叫函式:

find -name *.c

但是 globbing 還沒有關閉,所以 shell*.c在呼叫之前擴展了。所以find命令看到’-name’後跟三個參數,因此是錯誤消息。

您可以使用反斜杠代替引號。

find -name \*.c

您的函式禁用文件名通配,但您使用外殼擴展的 glob 呼叫它(文件名通配在您呼叫它的外殼中關閉)。

換句話說,你命令中的 glob

find -name *c

在呼叫您的find函式之前展開。這會導致find實用程序無法理解的函式內部呼叫。

您可以通過使用帶引號的參數呼叫您的函式來解決這個問題:

find -name "*c"

但請注意,現在該功能完全沒用,因為它只是複制了您已經輸入的命令。

除了無用之外,它也是錯誤的*。由於$@在您的程式碼中未引用,因此它會將參數拆分為空格(預設情況下)。這意味著您不能*使案例如

find -name "* *"

查找其中包含空格的名稱。

另請注意,由於eval,shell 將對$@. 這意味著

find -name '$(echo hello)'

不會找到名為$(echo hello)but的文件hello

另一件事是,如果呼叫 shell已經使用set -f,那麼這將被函式禁用。


讓我們創建一個有用的函式。查找多個文件名模式的函式,例如:

myfind "*.c" "*.txt" "* *"

我們希望上面的命令返回以.c.txt或包含空格的路徑名。

這是功能:

myfind () {
   # Replace each positional parameter "$pattern"
   # with -o -name "$pattern"
   for pattern do
       set -- "$@" -o -name "$pattern"
       shift
   done
   shift  # shift off the initial "-o"

   find . "$@"
}

鑑於上述函式呼叫,它將最終執行

find . -name '*.c' -o -name '*.txt' -o -name '* *'

如果您更習慣將bash數組用於列表,並且不介意更多輸入:

myfind () {
   local -a args

   # Build the argument list for find by adding
   # -o -name "$pattern"
   # for each positional parameter 
   for pattern do
       args+=( -o -name "$pattern" )
   done
   args=( "${args[@]:1}" )  # remove the initial "-o"

   find . "${args[@]}"
}

引用自:https://unix.stackexchange.com/questions/500085