【问题标题】:Count files and directories using shell script使用 shell 脚本计算文件和目录
【发布时间】:2012-12-05 15:49:09
【问题描述】:

我正在学习 bash 脚本,并编写了一个脚本来计算作为参数提供的目录中的文件和目录。我让它以一种对我来说很奇怪的方式工作,我想知道是否有更简单的方法。

我已经注释掉了可以工作的代码,但将其保留为比较。我试图让for-loop 工作,而不是在其中使用if 语句来检测给定位置中的项目是文件还是目录。

编辑:我刚刚发现注释代码也计算了给定位置的子目录中的所有文件和目录!有什么办法可以防止这种情况,只计算给定位置的文件和目录?

#!/bin/bash

LOCATION=$1
FILECOUNT=0
DIRCOUNT=0

if [ "$#" -lt "1" ]
then
    echo "Usage: ./test2.sh <directory>"
    exit 0
fi

#DIRS=$(find $LOCATION -type d)
#FILES=$(find $LOCATION -type f)

#for d in $DIRS
#do
#   DIRCOUNT=$[$DIRCOUNT+1]
#done

#for f in $FILES
#do
#   FILECOUNT=$[$FILECOUNT+1]
#done

for item in $LOCATION
do
if [ -f "$item" ]
    then
         FILECOUNT=$[$FILECOUNT+1]
    elif [ -d "$item" ]
        then
         DIRCOUNT=$[$DIRCOUNT+1]
fi
done

echo "File count: " $FILECOUNT
echo "Directory count: " $DIRCOUNT

由于某种原因,for-loop 的输出,无论我将位置指向何处,总是返回:

File count: 0 , Directory count: 1

【问题讨论】:

  • 如果文件名中有空格,你的整个脚本就会被破坏。正如您所了解的,我的第一个建议是:使用更多报价!
  • 另一条评论:我可以看出您学习 bash 的资源已经过时,没有显示出良好的做法。
  • 关于文件名扩展考虑阅读:bash.cyberciti.biz/bash-reference-manual/…
  • 你好,我不明白,文件名中的空格在哪里?
  • 文件名中可以​​有空格。试试这个:touch "hello Kitty",你会得到一个文件名中带有空格的文件。更糟糕的是,试试touch $'hello\nKitty',你会得到一个文件名,使用涉及find的方法将不容易处理。

标签: linux bash shell unix scripting


【解决方案1】:

使用find,如下所示。此解决方案将正确计算带有空格、换行符和点文件的文件名。

FILECOUNT="$(find . -type f -maxdepth 1 -printf x | wc -c)"
DIRCOUNT="$(find . -type d -maxdepth 1 -printf x | wc -c)"

请注意,DIRCOUNT 包括当前目录 (.)。如果你不想要这个,减 1。

((DIRCOUNT--)) # to exclude the current directory

【讨论】:

  • 在这种特殊情况下,这显然是最好的方法!正如 dogbane 所提到的,它处理所有可能的文件名称中包含有趣符号的情况,并且也适用于包含大量文件的目录(使用 globbing 的 bash 解决方案并非如此)。不过,有一件事,我的find 抱怨-maxdepth 1-type f 选项之后给出。 (另外一点,这里不需要双引号,但使用它们永远不会有坏处。
【解决方案2】:

要解决你可以使用的问题:

FILECOUNT=$(find $LOCATION -type f | wc -l)
DIRCOUNT=$(find $LOCATION -type d | wc -l)

find 将在$LOCATION 下递归查找所有文件(-type f)或目录(-type d); wc -l 将计算每种情况下写入标准输出的行数。

但是,如果您想学习,bash 脚本可能是更好的方法。一些cmets:

  • 如果您只想在$LOCATION 中查找文件/目录(而不是在其子目录下递归等),您可以使用for item in $LOCATION/*,其中* 将扩展为@ 中的文件/目录列表987654331@ 目录。缺少的 * 是原始脚本返回 0/1 的原因(因为 $LOCATION 目录本身是唯一计入的项目)。
  • 您可能需要先检查 $LOCATION 是否实际上是带有 [ -d $LOCATION ] 的目录。
  • 对于算术表达式,使用$(( ... )),例如FILECOUNT=$(( FILECOUNT + 1 ))
  • 如果要递归查找所有文件/目录,可以将find 与循环结合使用。

例子:

find $LOCATION | while read item; do
    # use $item here...
done

【讨论】:

  • 是的,$LOCATION/* 代码运行良好。为什么是 $(( ... )) 而不是 $[] ?性能相关?
  • @AlanSmith, $[...] 是不推荐使用的语法,我什至无法在 man bash 中找到它。现代版本是$(( ... ))(尽管它们的工作方式似乎相同)。对于您的任务,您也可以使用((++FILECOUNT))。见*.com/questions/2188199/…
【解决方案3】:

您没有遍历给定目录中的文件列表;在$LOCATION 之后添加/*。您的脚本应如下所示:

...
for item in $LOCATION/*
do
...

正如 dogbane 所指出的,仅添加 /* 将仅计算不以 . 开头的文件;为此,您应执行以下操作:

...
for item in $LOCATION/* $LOCATION/.*
do
...

【讨论】:

  • 这是脚本中真正的主要问题。
  • 这就是我要找的答案!谢谢。这里的其他解决方案也都计算了该位置的子目录的文件和目录,这是我不想要的。干杯。
  • 这不会计算点文件,即以.开头的文件名。
  • @dogbane 我在示例中添加了这种情况;感谢您指出。问候!
【解决方案4】:

... am wondering if there is a simpler way of doing it.

如果你这么说;)

或者,将您的脚本缩减为

find /path/to/directory | wc -l

对于当前目录,执行:

find . | wc -l

【讨论】:

  • 注意:它也将. 视为一个文件。因此,我猜您只需要计算两个常规文件,这将打印3
  • 他正在寻找一种方法来区分目录和文件的计数 - 这就是为什么他的代码中有 2 个计数器。您的查找给出了总数,而不是区分文件类型。
  • 如果我使用命令“ find . | wc -l ”,它会额外增加 1 个计数。就像目录中的文件数是 4 一样,对我来说,这个命令用 5 代替 4。
【解决方案5】:

使用

find $LOCATION -maxdepth 1 -type f | wc -l

对于文件的计数,以及

find $LOCATION -maxdepth 1 -type d | wc -l

用于计算目录

【讨论】: