【问题标题】:What's a more concise way of finding text in a set of files?在一组文件中查找文本的更简洁的方法是什么?
【发布时间】:2008-10-21 18:02:20
【问题描述】:

我目前使用以下命令,但输入起来有点笨拙。什么是更短的替代方案?

find . -name '*.txt' -exec grep 'sometext' '{}' \; -print

这是我的要求:

  • 限制为文件扩展名(我使用 SVN,不想搜索所有这些 .svn 目录)
  • 可以默认为当前目录,但能指定不同的目录就好了
  • 必须是递归的

更新:这是迄今为止我最好的解决方案:

grep -r 'sometext' * --include='*.txt'

更新 #2: 使用 grep 一段时间后,我意识到我更喜欢第一种方法的输出。所以,我听从了几个响应者的建议,简单地做了一个 shell 脚本,现在我用两个参数(扩展名和要查找的文本)调用它。

【问题讨论】:

    标签: bash unix shell


    【解决方案1】:

    grep 具有 -r(递归)和 --include(仅在与模式匹配的文件和目录中搜索)。

    【讨论】:

    • 值得注意的是,这些选项在 unice 之间是不可移植的。
    【解决方案2】:

    如果它太笨拙,请编写一个脚本来执行它并将其放入您的个人 bin 目录中。我有一个“fif”脚本,它可以在源文件中搜索文本,基本上只是像你在这里一样进行一次查找:

    #!/bin/bash
    
    set -f  # disable pathname expansion
    
    pattern="-iname *.[chsyl] -o -iname *.[ch]pp -o -iname *.hh -o -iname *.cc
    -o -iname *.java -o -iname *.inl"
    prune=""
    moreargs=true
    while $moreargs && [ $# -gt 0 ]; do
        case $1 in
        -h)
            pattern="-iname *.h -o -iname *.hpp -o -iname *.hh"
            shift
            ;;
        -prune)
            prune="-name $2 -prune -false -o $prune"
            shift
            shift
            ;;
        *)
            moreargs=false;
            ;;
        esac
    done
    
    find . $prune $pattern | sed 's/ /\\ /g' | xargs grep "$@"
    

    它最初是一个单行脚本,多年来根据我的需要添加了一些功能。

    【讨论】:

      【解决方案3】:

      这更有效,因为它调用grep 的次数更少,尽管很难说它更简洁:

      find . -name '*.txt' -print0 | xargs -0 grep 'sometext' /dev/null
      

      注意事项:

      /find -print0xargs -0 使带有嵌入空格的路径名正常工作。

      /dev/null 参数确保 grep 始终在文件名前添加。

      【讨论】:

      • -H (--with-filename) 代替那个解决方法怎么样?
      • find . -name '*.txt' -exec grep 'sometext' /dev/null {} + 将是一种更简洁有效的方式。
      【解决方案4】:

      安装ack并使用

      ack -aG'\.txt$' 'sometext'
      

      【讨论】:

      • 这个解决方案是最简洁的,但不幸的是需要安装 ack 并且比直接的 find 或 grep 命令更不便携。
      • ack 是一个单一的 Perl 脚本。您可以使用 wget 下载它并将其放在您的 ~/bin 目录中。仅此而已。
      • @Andy:无论获取和安装多么容易,它仍然是一个依赖项
      【解决方案5】:

      我第二个 ehemient 对 ack 的建议。我写这篇文章是为了强调一个特定的问题。

      响应 jgormley(在 cmets 中):ack 可作为单个文件使用,无论安装正确的 Perl 版本(无处不在)都可以使用。

      鉴于在非 Linux 平台上grep 通常不接受-R,可以说使用ack更多可移植的。

      【讨论】:

      • 另外,ack 将在 Windows 上运行,因此您可以在没有 find 和 grep 的地方执行此操作。
      【解决方案6】:

      我使用 zsh,它具有递归通配符。如果您需要查看特定的文件类型,以下内容等同于您的示例:

      grep 'sometext' **/*.txt
      

      如果你不关心文件类型,-r 选项会更好:

      grep -r 'sometext' *
      

      不过,对您的原始示例稍作调整就会得到您想要的:

      find . -name '*.txt' \! -wholename '*/.svn/*' -exec grep 'sometext' '{}' \; -print
      

      如果这是你经常做的事情,让它成为一个函数(把它放在你的 shell 配置中):

      function grep_no_svn {
          find . -name "${2:-*}" \! -wholename '*/.svn/*' -exec grep "$1" '{}' \; -print
      }
      

      函数的第一个参数是您要搜索的文本。所以:

      $ grep_here_no_svn "sometext"
      

      或者:

      $ grep_here_no_svn "sometext" "*.txt"
      

      【讨论】:

      • 一旦你的文件名列表变得太长,Globbing 就会遇到命令行大小的限制。 find 和 xargs 的组合不会有这个问题。
      【解决方案7】:

      您可以编写一个脚本(使用 bash 或其他方式——我在 Groovy 中有一个)并将其放置在路径上。例如

      $ myFind.sh txt targetString
      

      myFind.sh 在哪里:

      find . -name "*.$1" -exec grep $2 {} \; -print
      

      【讨论】:

        【解决方案8】:

        我通常使用grep $(find . -name "*,txt")来避免“找人”

        【讨论】:

          【解决方案9】:

          你说你更喜欢你的方法(使用 find)的输出。我可以看到它们之间的唯一区别是 grepping 多个文件会将文件名放在前面。

          您总是可以(在 GNU grep 中,但您必须使用它,否则 -r 和 --include 不起作用)使用 -h(--no-filename)关闭文件名。相反,对于任何想要文件名但出于其他原因必须使用 find 的人来说,是 -H (--with-filename)。

          【讨论】:

            猜你喜欢
            • 2015-04-03
            • 2020-11-12
            • 2011-08-17
            • 2020-10-17
            • 1970-01-01
            • 2018-12-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多