我想用來find
在一組受通配符限制的資料夾中尋找文件,但路徑名稱中有空格。
從命令列來看,這很容易。以下範例均有效。
find te*/my\ files/more -print
find te*/'my files'/more -print
find te*/my' 'files/more -print
這些將在例如terminal/my files/more
和中查找文件tepid/my files/more
。
但是,我需要將其作為腳本的一部分;我需要的是這樣的:
SEARCH='te*/my\ files/more'
find ${SEARCH} -print
不幸的是,無論我做什麼,我似乎都無法在find
腳本中的命令中混合通配符和空格。上面的範例傳回以下錯誤(請注意意外的雙反斜線):
find: ‘te*/my\\’: No such file or directory
find: ‘files/more’: No such file or directory
嘗試使用引號也會失敗。
SEARCH="te*/'my files'/more"
find ${SEARCH} -print
這將傳回以下錯誤,忽略引號的含義:
find: ‘te*/'my’: No such file or directory
find: ‘files'/more’: No such file or directory
這裡還有一個例子。
SEARCH='te*/my files/more'
find ${SEARCH} -print
正如預期的那樣:
find: ‘te*/my’: No such file or directory
find: ‘files/more’: No such file or directory
我嘗試過的每個變體都會返回錯誤。
我有一個解決方法,這有潛在危險,因為它返回太多資料夾。我將所有空格轉換為問號(單字元通配符),如下所示:
SEARCH='te*/my files/more'
SEARCH=${SEARCH// /?} # Convert every space to a question mark.
find ${SEARCH} -print
這相當於:
find te*/my?files/more -print
這不僅會傳回正確的資料夾terse/myxfiles/more
,還會傳回不應該的資料夾。
我怎樣才能實現我想做的事情? Google沒有幫助我:(
答案1
完全相同的命令在腳本中應該可以正常工作:
#!/usr/bin/env bash
find te*/my\ files/ -print
如果你需要要將其作為變量,它會變得更加複雜:
#!/usr/bin/env bash
search='te*/my\ files/'
eval find "$search" -print
警告:
這樣使用eval
並不安全,如果您的檔案名稱可以包含某些字符,則可能會導致執行任意且可能有害的代碼。看bash 常見問題 48了解詳情。
最好將路徑作為參數傳遞:
#!/usr/bin/env bash
find "$@" -name "file*"
另一種方法是完全避免find
並使用 bash 的擴展通配符功能和通配符:
#!/usr/bin/env bash
shopt -s globstar
for file in te*/my\ files/**; do echo "$file"; done
bashglobstar
選項可讓您使用**
遞歸匹配:
globstar
If set, the pattern ** used in a pathname expansion con‐
text will match all files and zero or more directories
and subdirectories. If the pattern is followed by a /,
only directories and subdirectories match.
要使其 100% 類似於查找並包含點文件(隱藏文件),請使用
#!/usr/bin/env bash
shopt -s globstar
shopt -s dotglob
for file in te*/my\ files/**; do echo "$file"; done
您甚至可以echo
直接使用它們而無需循環:
echo te*/my\ files/**
答案2
數組怎麼樣?
$ tree Desktop/ Documents/
Desktop/
└── my folder
└── more
└── file
Documents/
└── my folder
├── folder
└── more
5 directories, 1 file
$ SEARCH=(D*/my\ folder)
$ find "${SEARCH[@]}"
Desktop/my folder
Desktop/my folder/more
Desktop/my folder/more/file
Documents/my folder
Documents/my folder/more
Documents/my folder/folder
(*)
擴展為與通配符匹配的任何內容的數組。並"${SEARCH[@]}"
擴展到數組 ( [@]
) 中的所有元素,每個元素都單獨引用。
後來,我意識到發現自己應該有能力做到這一點。就像是:
find . -path 'D*/my folder/more/'
答案3
現在有點過時了,但是,如果這可以幫助任何人解決這個問題,那麼只要擴展路徑名中沒有特殊字符代替星號,使用 RE 的整理符號而不在 find 命令行參數中[[.space.]]
引用變量就可以工作。$SEARCH
set -x
mkdir -p te{flon,nnis,rrine}/my\ files/more
SEARCH=te*/my[[.space.]]files/more
find $SEARCH
給出以下結果:
+ mkdir -p 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more'
+ SEARCH='te*/my[[.space.]]files/more'
+ find 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more'
teflon/my files/more
tennis/my files/more
terrine/my files/more
為了防止出現不需要的字符,可以*
用任何整理元素(字符)替換星號 ( ):
set -x
mkdir -p -- te{,-,_}{flon,nnis,rrine}/my\ files/more
SEARCH=te[[:alnum:][.space.][.hyphen.][.underscore.]]*/my[[.space.]]files/more
find $SEARCH
給出以下結果:
+ mkdir -p -- 'teflon/my files/more' 'tennis/my files/more' 'terrine/my files/more' \
'te-flon/my files/more' 'te-nnis/my files/more' 'te-rrine/my files/more' \
'te_flon/my files/more' 'te_nnis/my files/more' 'te_rrine/my files/more'
+ SEARCH='te[[:alnum:][.space.][.hyphen.][.underscore.]]*/my[[.space.]]files/more'
+ find 'te-flon/my files/more' 'te_flon/my files/more' 'teflon/my files/more' \
'te-nnis/my files/more' 'te_nnis/my files/more' 'tennis/my files/more' \
'te-rrine/my files/more' 'te_rrine/my files/more' 'terrine/my files/more'
te-flon/my files/more
te_flon/my files/more
teflon/my files/more
te-nnis/my files/more
te_nnis/my files/more
tennis/my files/more
te-rrine/my files/more
te_rrine/my files/more
terrine/my files/more
請注意,要縮短該行, 可以用or[.hyphen.]
替換 ,並且 可以用or替換。[.-.]
-
[.underscore.]
[._.]
_
\
為了方便閱讀,我自己添加了帶有反斜線()的截斷行。
答案4
我終於找到答案了。
在所有空格中添加反斜線:
SEARCH='te*/my files/more'
SEARCH=${SEARCH// /\\ }
此時,SEARCH
包含te*/my\ files/more
.
然後,使用eval
.
eval find ${SEARCH} -print
就這麼簡單!使用繞過來自變數eval
的解釋。${SEARCH}