lon*_*nix 3 scripting bash find quoting
我需要找到目录中的所有内容,不包括某些子目录和文件。我的脚本需要将其作为函数调用:
function findStuff() {
# define exclusions
ignore_dirs=("$1" "*foo*") # exclude base dir
ignore_files=("one.txt" "two.txt" "*three*.txt")
# build patterns for find command
dir_pattern=""
file_pattern=""
for i in "${ignore_dirs[@]}"; do dir_pattern=$dir_pattern" ! -path \"$i\""; done
for i in "${ignore_files[@]}"; do file_pattern=$file_pattern" ! -name \"$i\""; done
# find
find "$1 $dir_pattern $file_pattern"
# now do other stuff with the results...
}
findStuff /some/base/dir
Run Code Online (Sandbox Code Playgroud)
但这给了我一个No such file or directory
错误。
所以我想看看命令实际上是什么,并尝试将echo find "$1 $dir_pattern $file_pattern"
其粘贴到命令行上并且它起作用了。然后我将它粘贴到脚本中并运行它,它也起作用了!
所以我认为它因为一些逃避问题而失败。我做错了什么?
find
将使用第一个参数(最多为第一个参数是开头-
或者说是!
或(
)是它得到顶级的路径进行搜索。find
当您在函数中调用它时,您将给出一个参数,即字符串$1 $dir_pattern $file_pattern
(扩展了变量)。找不到此路径。
您还在打算提供给的参数中包含文字双引号find
。双引号是为了防止 shell 扩展全局模式和在空格(或IFS
变量包含的任何内容)上进行拆分,但是如果您使用 eg,! -name \"thing\"
那么双引号将成为find
用于与文件名进行比较的模式的一部分。
使用数组,并find
正确引用单独的参数:
myfind () {
local ignore_paths=( "$1" "*foo*" )
local ignore_names=( "one.txt" "two.txt" "*three*.txt" )
local path_args=()
for string in "${ignore_paths[@]}"; do
path_args+=( ! -path "$string" )
done
local name_args=()
for string in "${ignore_names[@]}"; do
name_args+=( ! -name "$string" )
done
find "$1" "${path_args[@]}" "${name_args[@]}"
}
Run Code Online (Sandbox Code Playgroud)
每次我们追加到path_args
和name_args
上面时,我们都会向列表中添加三个元素,!
,-path
或-name
, 和"$string"
。展开"${path_args[@]}"
and 时"${name_args[@]}"
(注意双引号),元素将被单独引用。
等效实现适用于/bin/sh
:
myfind () (
topdir=$1
set --
# paths to ignore
for string in "$topdir" "*foo*"; do
set -- "$@" ! -path "$string"
done
# names to ignore
for string in "one.txt" "two.txt" "*three*.txt"; do
set -- "$@" ! -name "$string"
done
find "$topdir" "$@"
)
Run Code Online (Sandbox Code Playgroud)
在sh
shell 中,我们只有一个数组可供我们使用,它是位置参数列表$@
,因此我们find
在其中收集我们的选项。该bash
特异性的解决方案也被写入到使用单一数组,显然和sh
变化将运行bash
了。
最后,您的echo
测试输出不是您的函数将执行的命令的准确表示。
考虑一下:
cat "my file name"
Run Code Online (Sandbox Code Playgroud)
它运行cat
在一个叫做 的东西上my file name
,并且
echo cat "my file name"
Run Code Online (Sandbox Code Playgroud)
输出字符串cat my file name
。这是因为 shell 在执行命令之前删除了字符串周围的引号。运行该命令,cat
将查找三个文件,而不是一个。
当您将其复制粘贴到 shell 中时,您的命令运行良好,因为您在输出的字符串中包含了文字双引号echo
(通过转义它们),但这不是您的函数执行的实际命令。