如何在 bash 函数中正确使用引号?

如何在 bash 函数中正确使用引号?

我试图在我的.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将其传递给findafter -name,因此需要引用该*字符,这样 shell 就不会将其解释为通配符。在这里,我们*.py'*.py'while'*'.py或引用整个内容\*.py就足够了,因为其他 3 个字符(.py)对于 shell 来说并不特殊。

    如果使用 zsh 而不是 bash,您可以alias myfind='noglob myfind'myfind.然后你就可以myfind . *.py test不用*.pyshell 扩展它了。

  • 参数扩展必须加引号以防止 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"findfindmyfind ./-dir-starting-with-data- '*.py' pattern

  • sh代码中唯一不标准语法的是function name()函数定义语法。 ksh 语法是function name {...;},Bourne 和标准bash语法是name() {...;}。虽然bash也偶然支持function name() { ...; },但没有真正充分的理由使用它。

  • 我添加了一个-type f仅考虑的常规的文件,因为您可能不想grep 在设备文件或 fifo 内开始搜索,并且它会报告目录或套接字的错误。使用 GNU find,您可以将其替换为,-xtype f以便最终解析为常规文件的符号链接也被考虑。

相关内容