Shell-Script

zsh 中是否存在特定文件和文件類型的條件測試

  • December 1, 2017

我想檢查目前目錄是否存在副檔名為*、 或 或*abc名為baktmp文件的文件。我無法讓這個(最終是函式的一部分)在 zsh 中工作。它執行,但無法正確檢測。tmpout.wrk

if [[ -f *.(abc|bak|tmp) || -f tmpout.wrk ]]; then 
   echo 'true'; 
else 
   echo 'false'; 
fi

TL;博士

set -o extendedglob
if [[ -n *.(abc|bak|tmp)(#qN) || -f tmpout.wrk ]]; then

否則,通過一些測試,

% [[ -f /etc/passwd ]] && echo yea
yea
% echo /etc/passw?
/etc/passwd
% [[ -f /etc/passw? ]] && echo yea
% 

好的,zsh這裡在做什麼?

% set -x
% [[ -f /etc/passw? ]] && echo yes
+zsh:13> [[ -f '/etc/passw?' ]]
% 

他們的單引號肯定不會一無所獲。讓我們搜尋…然後搜尋…啊,這是關於文件名生成的內容[[man zshall``CONDITIONAL EXPRESSIONS

  Filename  generation is not performed on any form of argument to condi-
  tions.  However, it can be forced in any case where normal shell expan-
  sion  is  valid and when the option EXTENDED_GLOB is in effect by using
  an explicit glob qualifier of the form (#q) at the end of  the  string.
  A  normal  glob qualifier expression may appear between the `q' and the
  closing parenthesis; if none  appears  the  expression  has  no  effect
  beyond causing filename generation.  The results of filename generation
  are joined together to form a single word, as with the results of other
  forms of expansion.

  This  special  use of filename generation is only available with the [[
  syntax.  If the condition occurs within the [ or test builtin  commands
  then  globbing  occurs instead as part of normal command line expansion
  before the condition is evaluated.  In this case it may generate multi-
  ple words which are likely to confuse the syntax of the test command.

  For example,

         [[ -n file*(#qN) ]]

  produces  status  zero if and only if there is at least one file in the
  current directory beginning with the string `file'.  The globbing qual-
  ifier  N  ensures  that the expression is empty if there is no matching
  file.

所以考慮到這一點,

% [[ -f /etc/passw?(#q) ]] && echo yes
+zsh:14> [[ -f /etc/passwd ]]
+zsh:14> echo yes
yes
% exec zsh -l

對於您的情況,考慮可能沒有文件的情況:

% mkdir dir
% cd dir
% touch blah.foo
% [[ -f *.(foo|bar|baz)(#q) ]] && echo yea
yea
% rm blah.foo
% [[ -f *.(foo|bar|baz)(#q) ]] && echo yea
zsh: no matches found: *.(foo|bar|baz)(#q)
% [[ -f *.(foo|bar|baz)(#qN) ]] && echo yea
% touch a.foo b.foo
% [[ -f *.(foo|bar|baz)(#qN) ]] && echo yea
% [[ -n *.(foo|bar|baz)(#qN) ]] && echo yea
yea
% 

(儘管-n我們只檢查 glob 是否匹配,而不是相應的文件是正常文件)。

要測試 glob 是否至少返回一個文件,您可以執行以下操作:

if ()(($#)) (*.(abc|bak|tmp)|tmpout.wrk)(NY1); then
 echo true
else
 echo false
fi

要在符號連結解析後檢查其中至少一個是正常文件,請添加-. glob 限定符:

if ()(($#)) (*.(abc|bak|tmp)|tmpout.wrk)(NY1-.); then
 echo true
else
 echo false
fi
  • ()(($#))是一個匿名函式,我們將 glob 的結果傳遞給它。該函式的主體 ( (($#))) 只是測試非零參數的數量。
  • N作為該 glob 的 glob 限定符打開nullglob(當 glob 不匹配任何文件時使 glob 擴展為空)
  • Y1將擴展限制為最多一個文件。這是一個性能優化。
  • -在符號連結解析之後考慮下一個 glob 限定符。
  • .僅考慮正常文件(因此此處正常文件或符號連結最終解析為正常文件,就像[ -f file ]命令一樣)。

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