【问题标题】:how to navigate to subdirectories in a shell script如何导航到 shell 脚本中的子目录
【发布时间】:2010-11-25 13:56:03
【问题描述】:

我有一个目录。这个目录有很多子目录,其中包含 html 页面和一些 c 源代码文件、Makefile 等。我要编写的脚本将从一个包含所有这些子目录的目录中执行。 以下是您可以看到的一组目录和文件

ls
delete.sh  lddbus   Makefile      misc-progs  sbull  scullc  scullp  short       simple  snull  usb
include    LICENSE  misc-modules  pci         scull  sculld  scullv  shortprint  skull   tty

其中一些是目录,一些是上面子目录中的文件,还有更多的子目录和 html 页面,我也想删除它们。 手动方式是转到每个目录并通过以下命令删除页面

rm *.html*

由于 html 页面的名称以 ?=/something 结尾。 所以我决定写一个shell脚本。 但我不清楚的是我将如何将目录名称作为我的 shell 脚本中的参数。如果我决定使用 for 循环或类似的东西。 在这种情况下,我应该怎么做? 我不想使用

find . -name '*.html*' -exec rm -f {} \;

因为我这样做是为了学习。

【问题讨论】:

  • 最简单的方法:使用递归函数。
  • 我不太清楚这个你能举个例子吗?

标签: shell


【解决方案1】:

使用 bash 4(或 zsh),您可以使用 globstar "**" 进行递归匹配。

shopt -s globstar
echo **/*html*

使用这样的目录设置:

mkdir -p {a..b}/{c..d}
touch {a..b}/{c..d}/{e..f}.{htmlx,other}

这将导致:

a/c/e.htmlx a/c/f.htmlx a/d/e.htmlx a/d/f.htmlx b/c/e.htmlx b/c/f.htmlx b/d/e.htmlx b/d/f.htmlx

【讨论】:

    【解决方案2】:

    这段代码 sn-p 将为当前目录中的每个子目录运行ls

    for d in * .[!.]* ..?*; do
        test -d "${d}" && ls "${d}"
    done
    

    您可以调整它以运行您喜欢的每个子目录的命令。

    如果您想深入了解目录层次结构,可以将此代码包装在一个函数中,然后为每个子目录重新运行它。

    function f {
        cd "$1"
        # do something in this dir
        for d in * .[!.]* ..?*; do
            cd "$1"
            test -d "$1/$d" && f "$1/$d"
        done
    }
    
    f "`pwd`"
    

    对于 Zsh,您可能希望设置 NULL_GLOB 选项(-G 开关),因此如果没有隐藏的目录,它不会报告错误,在 Bash 中它默认工作。

    【讨论】:

    • 哇,太棒了。我从来没有遇到过这样的事情。我在想一些非常非常大的逻辑。 ls -1 这个命令是怎么写的?你从哪里学来的?
    • 解析ls的输出是个很糟糕的主意。阅读Why you shouldn't parse the output of ls 了解原因。
    • 出于学习目的,我现在不关心解析 ls 的输出,但无论如何感谢您提供这些信息,我会看看它。
    • @Bond:学习好的编程实践永远不会太早
    • `ls -1` 替换为* 以在极端情况下获得正确的行为。
    【解决方案3】:

    find 是要走的路。选择错误的方法来解决问题,你不会学到任何有用的东西。

    打印要删除的文件列表

    find /your/dir -type f -iname '*.html*'
    

    并删除它们

    find /your/dir -type f -iname '*.html*' -delete
    

    find 是一个强大的命令,学习使用它。

    另一种改进你不想使用的命令的方法:

    find /your/dir -type f -name '*.html*' -exec rm -f {} +
    

    (提示:man find 了解 -exec ;-exec + 的不同之处)

    请注意,shell 脚本只是以最有用的方式使用findcatlswc 等小程序。彻底了解这些实用程序是学习 shell 脚本的必要条件。

    【讨论】:

      【解决方案4】:

      像这样创建一个函数:

      myfunc()
      {
        for dir; do
            if [[ -d "$dir" ]]; then
                echo rm -rf "${dir}/*.html*"
            else
                echo "No such dir '$dir'"
            fi
        done
      }
      

      然后通过传递要从中删除 .html 的目录来调用函数

      输出

      $ myfunc /foo /etc /root
      No such dir '/foo'
      rm -rf /etc/*.html*
      rm -rf /root/*.html*
      

      一旦您对输出感到满意,请删除 echo 以使其真正删除文件。

      【讨论】:

      • 我如何测试给定的东西是否是一个目录?
      • 已更新以检查每个 arg 的目录状态
      【解决方案5】:

      这是基于maarons' answer,但有两个优点:

      • for 循环中使用 */ 而不是 * 意味着它只迭代目录而不是所有文件,以实现潜在的大幅加速
      • 使用pushdpopd 可以轻松支持. 或不使用参数来表示当前目录。

      函数如下:

      g () {
          local d bb=/dev/null # bit-bucket
          pushd "$1" > $bb
          pwd    # do something (pwd is a placeholder)
          for d in */
          do
              test -d "$PWD/$d" && g "$PWD/$d"    # you could use $(pwd) instead of $PWD
          done
          popd > $bb
      }
      

      当然,将find-execdir 一起使用更简单,并且结果相同。:

      find -mindepth 1 -type d -execdir sh -c 'cd {}; pwd' \;    # (pwd` is a placeholder)
      

      【讨论】:

      • @Gilles:我发布的函数确实如此。 find 版本没有(除非您添加 -L)。
      • 使用*/ 而不是*,函数将递归到目录的符号链接。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-12
      • 2018-05-27
      • 1970-01-01
      • 2016-12-08
      • 2021-03-05
      相关资源
      最近更新 更多