【问题标题】:Looping over files in UNIX在 UNIX 中循环文件
【发布时间】:2016-05-21 03:13:01
【问题描述】:

我试图遍历目录中的所有文件,其名称作为命令行参数给出(例如 myfolder)。对于每个文件,grep 命令应在文件夹上运行,并计算在文本文件中找到短语(例如 myphrase)的次数。

当我运行我的代码时,类似于下面,我收到错误“没有这样的文件或目录”。我尝试使用./myscript.sh myfolder./myscript.sh /[fullpath]/myfolder 调用脚本,它们都导致相同的错误。

for f in "$1"
do
  echo "processing $f file"
  grep -o '<myphrase>' "$f" | wc -l
done

关于出了什么问题有什么想法吗? 如果它有助于脚本从与文本文件相同的文件夹中运行,并且必须使用文件夹名称作为参数调用命令 - 我必须遵循这两个烦人的要求。

编辑:为此文件夹运行 ls -ld 会得到drwxr-xr-x@ 829 user staff 28186 7 Feb 17:19 my folder

【问题讨论】:

  • 使用:for f in "$1"/*,然后检查$f是否是使用[[ -f $f ]]的文件
  • 我认为这一定与参数有关,因为我添加了您的建议,但它仍然返回没有文件/目录错误
  • 你的脚本应该放在myfolder的父目录中
  • 实际上——你确定这不是一个 DOS 格式的文本文件(SO bash 标签 wiki 中列出的第一个“在你询问之前”要检查的东西)吗?如果您的 shebang(即顶部的 #!/bin/sh#!/bin/bash 行)告诉系统使用名为 /bin/bash$'\r' 的解释器运行它,因为它有一个 CRLF 换行符,那么它会给出运行时“找不到文件”。
  • ...re:那个标签wiki,见stackoverflow.com/tags/bash/info

标签: bash unix grep directory


【解决方案1】:

正如上面提到的anubhava

在你的 for 循环中使用:

for f in "$1"/*
do
...
done

然后你可以检查 f 是否是一个文件:

[[ -f $f ]]

并在循环内执行您的必要逻辑:

[[ -f $f ]] && grep -o '<myphrase>' "$f" | wc -l

总之:

for f in "$1"/*
do
 echo "processing $f file"
 [[ -f $f ]] && grep -o '<myphrase>' "$f" | wc -l
done

【讨论】:

  • 你是说上面的[[ -f $f ]] &amp;&amp; grep -o '&lt;myphrase&gt;' "$f" | wc -l吗?你的 if 很好,但为什么要放弃 [[ 以支持 [ 呢?
  • 恐怕还是没有运气!这是我运行脚本的方式吗? ./scriptname.sh 我的文件夹
  • @jsc,如果你运行bash -x ./scriptname myfolder,输出有启发吗?
  • @jsc 你目前在用什么shebang?
  • @user3704230, ...具体来说,最后带有 DOS 换行符的 shebang 会尝试读取 /bin/sh$'\r'/bin/bash$'\r' 等,因此是“找不到文件”。如果它 CRLF 换行符,OP 可能不知道它的存在。
【解决方案2】:

您需要将通配符匹配添加到目标目录。
比较 foo.sh 中的两个 for 循环(“原始”被注释掉)。

我还将 grep 命令修改为只是一个回显,所以你有一个
预览将要尝试执行的内容的简便方法。

foo.sh 的示例输出

编辑:我添加了 -r(下面,在 foo.sh 中)来检查 dir $1 的读取权限。那将很难解决;当我针对一个未读目录进行测试时,它看起来就像目录中没有任何东西一样(即使它有一个 *.java 和一个 *.class 文件,如上所示)。

更清楚地说,针对不可读的目录运行看起来像这样(这没有 -r 检查):

$ chmod a-r tmp
$ ./foo.sh tmp
$1="tmp"
processing tmp/* file
grep -o '<myphrase>' "tmp/*" | wc -l
$ 

请注意上面的“处理 tmp/* 文件”行。 for 循环将文字字符“tmp/*”输入到 $f 变量中。
这很好,如果模式不匹配任何内容,这正是通配符扩展的工作方式。
但我们不会检查 grep 中的错误代码,所以这可能很难
注意 grep 抱怨 "file tmp/* not found"

foo.sh 输出(修订)

$ chmod a+r tmp
$ ./foo.sh
$1=""
Error: no directory specified.
$ ./foo.sh foo.sh
$1="foo.sh"
Error: "foo.sh" is not a dir.
$ ./foo.sh tmp
$1="tmp"
processing tmp/Foo.class file
grep -o '<myphrase>' "tmp/Foo.class" | wc -l
processing tmp/Foo.java file
grep -o '<myphrase>' "tmp/Foo.java" | wc -l
$ chmod a-r tmp
$ ./foo.sh tmp
$1="tmp"
Error: no read permissions on dir "tmp".
$ 

foo.sh

#!/bin/bash

echo "\$1=\"$1\""
if [ -z "$1" ]; then
   echo "Error: no directory specified."
   exit 1
fi
if [ ! -e "$1" ]; then
   echo "Error: dir \"$1\" does not exist."
   exit 1
fi
if [ ! -d "$1" ]; then
   echo "Error: \"$1\" is not a dir."
   exit 1
fi
if [ ! -r "$1" ]; then
   echo "Error: no read permissions on dir \"$1\"."
   exit 1
fi
# maybe default to "." if $1 is empty ?
# original: for f in "$1"
for f in "$1"/*
do
  echo "processing $f file"
  echo "grep -o '<myphrase>' \"$f\" | wc -l"
  # maybe  change myphrase to $2 ?
done

此外,正如其他地方所指出的,您正在编写自己的“查找”命令版本。还有一种可能……

那么 find 呢?

我添加这个只是为了鼓励你在 some 点查看 find 命令。

免责声明:编写自己的脚本完全没问题。 研究和了解如何自己动手是很有价值的。

find 确实很复杂,但从长远来看,它值得攀登 find 的学习曲线。

请注意,以下 99% 只是 cmets。

ezfind.sh(一个例子)

#!/bin/bash

# example ussage:
# ezfind.sh "$HOME/my_dir"  "foo.*bar"
#  "$1" is the start point, any directory path (relative or absolute).
#  -type f limits matches to regular files (e.g. probably dont
#  want to run grep dirs or devices).
#  exec args ar funky, see below.
#  Optional: see bottom of this script for notes about -depth
#  to limit how deep find will search.
#------------------------------------------------------
find "$1" -type f -exec grep -i -o   "$2" '{}' ';'
#                       \________/  \___/ \__/ \_/
#                           |        |     |   |
#   command ----------------+        |     |   |
#   pattern for grep ----------------+     |   |
#   find replaces {} w/filename------------+   |
#   find exepects a semicolon for end-of-cmd---+

# Quoting is funky for -exec arguments.
# The values have to to survive current bash interpretation,
# so they can be passed to find's argument list.
# Then find turns around and passes them to grep's argument list.
# The semicolon is normally a bash statement separator so we
# need to quote it (or escape it) so it gets passed to find
# as part of the arg list.

# find -exec will replace {} with current filename.
# find gives you a crazy amount of file name, file type and date range options.
#    search for all file names in /what/ever matching "*.txt"
#    search for all file names matching "*.sh" modified in the last hour.
# for more on find, see here:
# http://www.softpanorama.org/Tools/Find/using_exec_option_and_xargs_in_find.shtml
# 
# What if I don't want to search every sub-folder, all the way down?
# Also about not traversing subdirectories, consider the -depth modifer.
# To just search the target directory, modify the above to read:
#     find "$1" -maxdepth 0  ....
# To just search the target directory and immediate subdirectories...
#     find "$1" -maxdepth 1  ....
# For a nice summary of -depth, see here:
#     http://www.tech-recipes.com/rx/31/limit-the-depth-of-search-using-find/

【讨论】:

  • 感谢您的客气话。我不想破坏你的脚本工作,绝对继续插上它并让它工作。你可以在一天后阅读 find 。
  • 非常好的全面回复-谢谢!我会寻找,只是我将此方法用于此任务的另一部分,因此尽可能保持相同。现在已经设法获得识别文件夹的命令,但是收到此错误,似乎它几乎试图将* 作为文件读取? processing myfolder/* filegrep: myfolder/*: No such file or directory0
  • 很抱歉删除评论并重新回复,新来的这个网站并没有意识到返回会发布而不是让我开始新的一行:p
  • 啊,有趣的是你提到“myfolder/*:没有这样的文件或目录”。我刚刚添加了一些代码来检查目录上是否没有读取权限。您能否执行“ls -ld myfolder”并编辑您的问题以显示结果。 (-l 用于长列表,显示权限,-d 用于防止 ls 显示 myfolder 的内容...我想从 myfolder 本身开始)。
  • 您也可以尝试使用 myfolder 的完整路径,例如/home/jsc/myfolder 或任何位置。调用脚本时以这种方式运行可能更稳定,不需要调用者先 cd 到特定位置。
【解决方案3】:

从Directory details看,目录名好像有空格:

drwxr-xr-x@ 829 用户员工 28186 2 月 7 日 17:19 我的文件夹

尝试如下运行脚本或删除目录名称中的空格:)

./myscript.sh "我的文件夹"

【讨论】:

    【解决方案4】:

    你可以这样做:

    find <directory> -type f -exec egrep -c '<regular expression>' {} +
    

    请注意,无需检查文件是否存在,因为find 找不到不存在的文件。另请注意,find 遍历找到的子目录(-maxdepth 1 的可调行为)。

    例如:

    find /usr/include -type f -exec egrep -c 'int128' {} +
    

    【讨论】:

    • 如果存在不应该遍历的子目录,则使用find 是错误的。这可以解决,但最简单的解决方法是不使用find。此外,您随意将grep -o | wc -l 替换为egrep -c;他们不做同样的事情(grep -c 计算匹配的 行数, 不是实际字符串出现的次数,egrep 还使用不同的正则表达式方言)。因此,我对太多不相关且可能错误的更改投了反对票。
    • @tripleee RTFM, -maxdepth 1.
    • 就像我说的,这很容易修复,但最简单的修复是恢复到 OP 的内容。
    猜你喜欢
    • 2022-07-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-02
    • 2015-02-13
    相关资源
    最近更新 更多