【问题标题】:Bash: Whats the correct way to loop through a directory and sub-directories including hidden files?Bash:遍历目录和子目录(包括隐藏文件)的正确方法是什么?
【发布时间】:2020-02-06 23:15:37
【问题描述】:

对于学校作业,我试图递归地遍历目录和子目录以总结文件的大小。我遇到的问题是构造:

for f in ./* ./.*; do
  # summing logic here
done

卡在f = ./. 上它可以很好地进入每个目录,但是一旦它进入一个完全处理的目录,在最后一个文件之后,f 被设置为./.。我有逻辑来检查f 是否是一个目录,它确实如此,然后进入f 来处理它。并永远循环在那里。

我尝试包含代码来检查字符串f 是否匹配“./.””./..”,但它永远不会评估为真。我犯了什么错误?

主要问题:为什么if [[ "$f" != "./." ]] || [[ "$f" != "./.." ]]; then 不起作用,我该怎么做才能获得相同的结果?此外,如果我尝试for f in ./* ./.* ; do echo $f done 之类的东西,我看不到./../.. 被打印出来。 f 如何在我的脚本中设置为这些值?

我见过类似问题的答案,涉及 bash-builtin shopt,但我使用 zsh 而学校的测试服务器使用 csh。我真的希望有一些与平台无关的东西。

小提示:由于代码现在是这样,因此分配已完成。我们只需要对当前工作目录中文件的大小求和,不包括子目录。我对使脚本递归感到好奇,我只是为了满足我的兴趣而做这部分。感谢您的帮助。

#!bin/bash

total_size=0

get_file_size() {
    stat --printf="%s" "$1"
}

add_file_sizes() {
    for f in ./* ./.*; do
        echo "Currently processing: $f"
        if [ -d "$f" ] && [ "$1" == -r ]; then
            echo "$f is a directory"
            if [ "$f" !=  "./." ] || [ "$f" != "./.." ]; then
                echo "$f is not ./. or ./.."
                cd "$f"
                pwd
                add_file_sizes "-r"
                echo "$total_size"
                cd ../
            fi
        fi
        if [ ! -d "$f" ]; then
            echo "$f is not a directory"
            total_size=$((total_size + $(get_file_size "$f")))
            echo "$total_size"
        fi
    done
}

add_file_sizes $1

echo "$total_size"

编辑:这是一些输出:

Currently processing: list_size.sh
list_size.sh is not a directory
625
Currently processing: output.txt
output.txt is not a directory
759
Currently processing: test_dir
test_dir is a directory
test_dir is not ./. or ./..
/home/joe/dev/csc60/test_dir
Currently processing: file1
file1 is not a directory
759
Currently processing: file2
file2 is not a directory
759
Currently processing: test_subdir
test_subdir is a directory
test_subdir is not ./. or ./..
/home/joe/dev/csc60/test_dir/test_subdir
Currently processing: file3
file3 is not a directory
759
Currently processing: ./.
./. is a directory
./. is not ./. or ./..
/home/joe/dev/csc60/test_dir/test_subdir
Currently processing: file3
file3 is not a directory
759
Currently processing: ./.
./. is a directory
./. is not ./. or ./..
/home/joe/dev/csc60/test_dir/test_subdir
Currently processing: file3
file3 is not a directory
759
Currently processing: ./.
./. is a directory
./. is not ./. or ./..
/home/joe/dev/csc60/test_dir/test_subdir
Currently processing: file3
file3 is not a directory
759
Currently processing: ./.
./. is a directory
./. is not ./. or ./..
/home/joe/dev/csc60/test_dir/test_subdir
Currently processing: file3
file3 is not a directory
759
Currently processing: ./.
./. is a directory
./. is not ./. or ./..
/home/joe/dev/csc60/test_dir/test_subdir
Currently processing: file3
file3 is not a directory
759
Currently processing: ./.
./. is a directory
./. is not ./. or ./..
/home/joe/dev/csc60/test_dir/test_subdir
Currently processing: file3
file3 is not a directory
759

编辑 2:调整了初始 for 循环并普遍改进了脚本以响应答案中的建议。

当我将 for 循环更改为 for f in * .[!.]* 时的输出:

Currently processing: list_size.sh
list_size.sh is not a directory
578
Currently processing: list_size_tweaked.sh
list_size_tweaked.sh is not a directory
1156
Currently processing: output_tweaked.txt
output_tweaked.txt is not a directory
1394
Currently processing: output.txt
output.txt is not a directory
1394
Currently processing: test_dir
test_dir is a directory
test_dir is not ./. or ./..
/home/joe/dev/csc60/test_dir
Currently processing: file1
file1 is not a directory
1394
Currently processing: file2
file2 is not a directory
1394
Currently processing: test_subdir
test_subdir is a directory
test_subdir is not ./. or ./..
/home/joe/dev/csc60/test_dir/test_subdir
Currently processing: file3
file3 is not a directory
1394
Currently processing: .[!.]*
.[!.]* is not a directory
1394
stat: cannot stat '.[!.]*': No such file or directory
./list_size_tweaked.sh: line 25: total_size + : syntax error: operand expected (error token is "+ ")
7670

这似乎是因为目录中没有点文件,所以 glob 没有展开。

【问题讨论】:

标签: bash shell recursion


【解决方案1】:

做:

for f in * .[!.]*; do

我认为它应该适用于任何 posix 兼容的 shell。该文档可以在posix Shell Command Language 2.13 Pattern Matching Notation 中找到。 . 匹配一个点,然后[!.] 是一个模式括号表达式,它匹配除点之外的所有内容,因此它有效地将. 当前目录和.. 父目录从匹配中排除。

注意事项:

  • 出色的脚本,出色的编码,继续努力!
  • 引用您的变量扩展名,尤其是当它们是文件名时。不要get_file_size $f,要get_file_size "$f"When to wrap quotes aroung a shell variable?
  • 不要使用反引号`,不鼓励使用它们。请在任何地方使用$(...)Obsolete and deprecated syntax bash hackers wiki
  • 不要使用function name(),它是两种shell 符号的混合。只需 name() { .. } 定义一个函数,该函数与 posix 兼容,并且可以在任何地方工作。
  • 只需get_file_size() { stat --printf="%s" "$1"; }。不需要变量和echo
  • [[ 是一个 bash 扩展。所以在csh 上使用[。请记住引用您的变量扩展。
  • 我想我会find . -type f -printf "%s\n" | awk '{ sum+=$1 } END{print sum}'

【讨论】:

  • 非常感谢您提供的所有提示!这正是我正在寻找的。我只是在学习 bash,很难说出什么是贬低/bash 特定/不好的做法,而且一些教授在这方面可能没有帮助。当我将 for 循环更改为您的时收到错误消息,可能是由于您指出的错误。我将更新脚本,并根据结果编辑我的问题。
  • it’s difficult to tell what’s depriciated/bash specific/bad practice - 完全同意。
  • 好吧,更新了问题,似乎 for 循环中的 regex(?) 没有在 bash 或 zsh 中扩展。我弄错了还是 f 被逐字设置为 .[!.]*
  • 在我看来 bizzare 是,如果我不使用 -r 参数启用递归,则存在的 for 循环(使用 ./* ./.*)不会触摸 ./.或 ./.. 在调用该脚本的目录中。
  • 是的,有问题。如果找不到匹配的文件,则 glob 不会扩展。所以就像echo does_not_exists* 会直接输出does_not_exists*。前任。见shopt -s nullglob。因此,如果没有点文件,.[!.]* 将扩展为 .[!.]*(字面意思)。而不是if [ ! -d "$f" ]if [ -e "$f" ]if [ -f "$f" ] - 只需检查结果是否真的是一个文件。
猜你喜欢
  • 1970-01-01
  • 2020-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-10
  • 2013-09-20
相关资源
最近更新 更多