我试图在我的.bashrc
文件中定义以下 bash 函数:
function myfind() {
find $1 -not -path venv -not -path .tox -name "$2" | xargs grep -n "$3"
}
这没有达到我的预期。当我使用该函数搜索文件中的文本时,例如
myfind . *.py test
它不返回任何内容。但是当我直接使用表达式时,即
find . -not -path venv -not -path .tox -name "*.py" | xargs grep -n "test"
它返回一些匹配项。
我已经尝试使用双引号,例如
function myfind() {
find $1 -not -path venv -not -path .tox -name '"$2"' | xargs grep -n '"$3"'
}
或者
function myfind() {
find $1 -not -path venv -not -path .tox -name "'$2'" | xargs grep -n "'$3'"
}
并试图逃避像这样的引号
function myfind() {
find $1 -not -path venv -not -path .tox -name \"$2\" | xargs grep -n \"$3\"
}
但这些似乎都不起作用。
如何修复这个 bash 函数,以便在参数周围“使用”引号?
答案1
蒂蒂姆:
myfind() {
find "$1" '(' -name venv -o -name .tox ')' -prune -o \
-name "$2" -type f -exec grep -Hne "$3" -- {} +
}
进而:
myfind . '*.py' test
也可以看看为什么循环查找的输出是不好的做法?为什么调用这样xargs
的输出是错误的。find
您的方法还有一些其他问题:
-path venv
永远不会将任何内容-path
与正在考虑的文件的完整路径上的匹配项相匹配,这将类似于<contents-of-$1>/subdir/file
.只有在调用的顶层myfind venv ...
它才会匹配。我假设您想忽略存储在名为 的目录下的文件venv
。虽然您可以这样做,但首先! -path '*/venv/*'
告诉不要进入这些目录比事后过滤掉其中的所有文件更有效(如果文件路径在语言环境中不是有效文本,也会出现问题) 。find
! -path '*/venv/*'
对于
myfind first *.py last
,您要求 shell 扩展*.py
到当前目录中的匹配文件列表,以构成 的第二个到倒数第二个参数myfind
,而它是*.py
您想要传递给 for 的文字参数myfind
,以便myfind
将其传递给find
after-name
,因此需要引用该*
字符,这样 shell 就不会将其解释为通配符。在这里,我们*.py
用'*.py'
while'*'.py
或引用整个内容\*.py
就足够了,因为其他 3 个字符(.
、p
和y
)对于 shell 来说并不特殊。如果使用 zsh 而不是 bash,您可以
alias myfind='noglob myfind'
在myfind
.然后你就可以myfind . *.py test
不用*.py
shell 扩展它了。参数扩展必须加引号以防止 split+glob(
"$1"
,而不是$1
)。如果没有
-H
(GNU 扩展),如果find
只找到一个文件,grep
则不会打印其路径,因此您不知道匹配项在哪里。对于grep
不支持 的实现-H
,您可以/dev/null
作为额外参数传递:-exec grep -ne "$3" -- /dev/null {} +
。请参阅
!
与非标准等效的标准(和更短)-not
。with
grep -n "$3"
,如果$3
开始于-
,它将被 视为一个选项grep
。因此,-e "$3"
将 的内容$3
作为参数传递给-e
选项。也存在同样的问题find "$1"
,但不幸的是,没有标准的解决方法。find -- "$1"
将阻止 的内容$1
被视为选项由find
它开始-
,但它仍然会被视为谓词,所以这是没有意义的。例如,名为!
或 的目录(
也会是一个问题。 BSD对此find
有支持,但 GNU和大多数其他实现不支持它。用于传递以破折号开头的目录。find -f "$1"
find
find
myfind ./-dir-starting-with-data- '*.py' pattern
sh
代码中唯一不标准语法的是function name()
函数定义语法。 ksh 语法是function name {...;}
,Bourne 和标准bash
语法是name() {...;}
。虽然bash
也偶然支持function name() { ...; }
,但没有真正充分的理由使用它。我添加了一个
-type f
仅考虑的常规的文件,因为您可能不想grep
在设备文件或 fifo 内开始搜索,并且它会报告目录或套接字的错误。使用 GNUfind
,您可以将其替换为,-xtype f
以便最终解析为常规文件的符号链接也被考虑。