BASH 函数不转义控制字符

ehi*_*ime 4 shell-script bashrc bash-scripting

我有一个用来查找东西的函数,但不幸的是,每当我向它传递一个控制字符($intVal或其他testing :)时,它就会窒息。我想知道修复是什么?

我可以理解在 grep中使用$or%:etc 而不转义会导致此问题,但是由于我是通过引用传入的,因此我不确定如何转义它...

无论如何,这是代码。

function ffind()
{
     if [ $1 ] ; then
         find -type f | grep -ir '$1' * | grep -v '.svn'
     else
         echo "'$1' is not a valid resource"
     fi
}
Run Code Online (Sandbox Code Playgroud)

例子):

$ ffind $intVal
'' is not a valid resource

$ ffind "testing :"
bash: [: testing: unary operator expected
'testing :' is not a valid resource
Run Code Online (Sandbox Code Playgroud)

use*_*686 9

第一个例子

$ ffind $intVal
'' is not a valid resource
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为这$foo是 variables 的语法,并且您没有名为intValset的变量,因此$intVal被转换为空字符串。由于该变量也未加引号,因此根本没有参数传递给ffind

要解决此问题,请转义$\$intVal(反斜杠)或'$intVal'(单引号)。

但是,如果您确实一个名为 的变量intVal,请将其放在引号中 – "$intVal"– 这将扩展变量的值,但不会拆分它。

请注意,bash 中没有“通过引用传递”这样的东西。只有按值传递和(棘手的)按名称传递。

第二个例子

$ ffind "testing :"
bash: [: testing: unary operator expected
'testing :' is not a valid resource
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为你忘了引号$1if [ $1 ]线,因此它是受词分裂,和三个参数传递给[内置:

  • [
  • testing
  • :
  • ]

而不是预期的两个:

  • [
  • testing :
  • ]

示例 #1 也受此影响,因为[ $1 ]拆分为 (" [", " ]") 而不是 (" [", " ?", " ]")。示例 #1 是偶然工作的,因为显然[ ]是有效的。(我不知道……)

要解决此问题,请在$1–周围加上双引号[ "$1" ]

注意:虽然[是标准的,但也有一个特定于 bash 的[[运算符,它实际上具有与其余代码不同的解析规则——特别是,它不拆分扩展变量。在 bash 中,[[ $1 ]][[ "$1" ]]两者都是等价的,不像它们的[替代品。

更多错误

您的函数还存在示例中未显示的其他几个问题。

find -type f | grep -ir '$1' * | grep -v '.svn'
Run Code Online (Sandbox Code Playgroud)

在这一行:

  1. 这个词'$1'周围有单引号。这意味着bash不会扩展其内容——您实际上是在告诉grep搜索 regex $1,而不是命令行参数。

    要解决此问题,请改用双引号 – "$1"

  2. 第一个grep命令被告知递归搜索当前目录(和通配符)中所有文件的内容-r*

    同时,您正在将 的输出通过管道传输find -type fgrep 中——似乎试图告诉grep搜索所有文件的名称

    这是行不通的,因为grep与大多数过滤器一样,如果给定一个或多个文件进行搜索,则不会从 stdin 读取。我不知道你要搜索什么——文件名或文件内容——所以选择一个:

  3. 在第二个grep命令中,搜索正则表达式有点太宽泛了。(请记住,它是一个正则表达式,而不是一个固定字符串。).svn甚至会匹配“ not-a-svn-file”之类的东西。

    要排除.svn目录,请grep -v "/\.svn/"改用。

    如果搜索文件内容( grep -ir ...),最好grep -v完全去掉该命令,并添加--exclude-dir=".svn"到第一个。

您可以在此时停止阅读

下面的项目只是sh脚本中的好习惯。

  1. function关键字是不必要的:ffind() { ...是足够的,而在所有POSIX弹作品(而function ffind 不会)。

  2. 如果脚本、程序或函数失败,它应该向其父级返回“失败”状态。按照惯例,Unix 程序认为0意味着“成功”和其他任何“失败”(尽管后者也有例外)。

    要显式返回状态,请return <num>在函数(或exit <num>独立脚本)中使用:

    else
        echo "'$1' is not a valid resource" >&2
        return 1
    fi
    
    Run Code Online (Sandbox Code Playgroud)
  3. 类似地,错误消息不应与普通的 stdout 混合,而应使用重定向运算符写入stderr (fd #2) >&(参见上面的示例)。通过这种方式,您可以将正常输出重定向到文件(例如ffind intVal > results.txt),同时仍然在屏幕上显示错误。

固定码

ffind()
{
     if [ "$1" ] ; then
         grep -ir --exclude-dir=".svn" "$1" .
     else
         echo "'$1' is not a valid resource" >&2
         return 1
     fi
}
Run Code Online (Sandbox Code Playgroud)

更好的工具

ack声称“比 grep 好”。运行ack "testing :"会搜索你的源代码,并自动跳过.svn和类似的目录。