递归shell脚本来列出文件

Bor*_*357 4 shell directory

我正在尝试编写一个 shell 脚本,列出目录(和子目录)中的某些类型的文件。我正在努力解决递归部分。

这是我所拥有的:

#!/bin/sh

#download dir
DOWNLOADING_DIR=/Users/richard/Downloads

echo "Starting Script..."

for FILE in $DOWNLOADING_DIR/*
do
    if [ -d "$FILE" ]
    then
        echo "...Checking Directory "$FILE
        for DFILE in $FILE/*
        do
            echo "Found file ... $DFILE"
        done
    else
        echo "Found file ... $FILE"
        echo ""
    fi
done
Run Code Online (Sandbox Code Playgroud)

问题是当它找到目录时,它在目录中找不到任何文件名。它只列出子目录名称而不是子目录中的文件。它适用于第一个目录中的文件。

我需要这个脚本来搜索 .txt 或 .doc 文件并将它们移动到另一个目录。

Kus*_*nda 15

您的脚本不是递归的,因为它不会调用自己。

这是一个变体,它实现了类似于您递归的内容:

#!/bin/bash

walk_dir () {
    shopt -s nullglob dotglob

    for pathname in "$1"/*; do
        if [ -d "$pathname" ]; then
            walk_dir "$pathname"
        else
            printf '%s\n' "$pathname"
        fi
    done
}

DOWNLOADING_DIR=/Users/richard/Downloads

walk_dir "$DOWNLOADING_DIR"
Run Code Online (Sandbox Code Playgroud)

该函数walk_dir将目录路径名作为其唯一参数并迭代其内容。如果找到目录,它会递归调用自身以遍历该子目录。

修改它以查找文件名后缀为.txtor的文件.doc:

#!/bin/bash

walk_dir () {    
    shopt -s nullglob dotglob

    for pathname in "$1"/*; do
        if [ -d "$pathname" ]; then
            walk_dir "$pathname"
        else
            case "$pathname" in
                *.txt|*.doc)
                    printf '%s\n' "$pathname"
            esac
        fi
    done
}

DOWNLOADING_DIR=/Users/richard/Downloads

walk_dir "$DOWNLOADING_DIR"
Run Code Online (Sandbox Code Playgroud)

请注意,上面的“文件”实际上是指任何不是目录或指向目录的符号链接的东西,这可能与常规文件不同。通过在 中设置dotglob和nullglobshell 选项bash,我们能够找到隐藏的路径名,而不必专门测试可能为空的目录。

一个变体/bin/sh不关心隐藏的名字:

#!/bin/sh

walk_dir () {
    for pathname in "$1"/*; do
        if [ -d "$pathname" ]; then
            walk_dir "$pathname"
        elif [ -e "$pathname" ]; then
            case "$pathname" in
                *.txt|*.doc)
                    printf '%s\n' "$pathname"
            esac
        fi
    done
}

DOWNLOADING_DIR=/Users/richard/Downloads

walk_dir "$DOWNLOADING_DIR"
Run Code Online (Sandbox Code Playgroud)

使用 中的globstar和exglobshell 选项bash,甚至可以执行以下操作(无需递归)来移动文件:

shopt -s globstar extglob

mv "$DOWNLOADING_DIR"/**/*.@(txt|doc) "$destdir"
Run Code Online (Sandbox Code Playgroud)

...除非结果文件列表太长。所述**跨斜线在路径名匹配(通过启用globstar)和*.@(txt|doc)任何文件名匹配以端部或者.txt或.doc(通过启用extglob)。


一个更为高效和可移植的方式来找到的无论是文件名后缀的常规文件.txt或.doc在或某些顶层目录下$topdir,并将其移动到其他目录$destdir:

find "$topdir" -type f \( -name '*.txt' -o -name '*.doc' \) \
    -exec mv {} "$destdir" \;
Run Code Online (Sandbox Code Playgroud)

使用 GNUmv你可以让它更有效率,

find "$topdir" -type f \( -name '*.txt' -o -name '*.doc' \) \
    -exec mv -t "$destdir" {} +
Run Code Online (Sandbox Code Playgroud)

这种变体将批量移动文件而不是一次移动一个文件。使用mvwith-v查看移动的内容,或添加-printbefore-exec以获取mv调用 with的路径名列表。