递归 bash 脚本用于收集目录结构中每个文件的信息

递归 bash 脚本用于收集目录结构中每个文件的信息

如何通过目录树递归工作并在每个文件上执行特定命令,并将路径、文件名、扩展名、文件大小和其他一些特定文本输出到 bash 中的单个文件。

答案1

虽然find解决方案简单而强大,但我决定创建一个更复杂的解决方案,该解决方案基于这个有趣的功能,我几天前就看过了。

  • 提供更多解释和另外两个基于当前的脚本这里

1.创建名为 的可执行脚本文件walk,该文件位于/usr/local/bin可以作为 shell 命令访问:

sudo touch /usr/local/bin/walk
sudo chmod +x /usr/local/bin/walk
sudo nano /usr/local/bin/walk
  • 复制以下脚本内容并使用nanoShift+Insert粘贴;Ctrl+OEnter保存;Ctrl+X退出。

2.脚本内容walk如下:

#!/bin/bash

# Colourise the output
RED='\033[0;31m'        # Red
GRE='\033[0;32m'        # Green
YEL='\033[1;33m'        # Yellow
NCL='\033[0m'           # No Color

file_specification() {
        FILE_NAME="$(basename "${entry}")"
        DIR="$(dirname "${entry}")"
        NAME="${FILE_NAME%.*}"
        EXT="${FILE_NAME##*.}"
        SIZE="$(du -sh "${entry}" | cut -f1)"

        printf "%*s${GRE}%s${NCL}\n"                    $((indent+4)) '' "${entry}"
        printf "%*s\tFile name:\t${YEL}%s${NCL}\n"      $((indent+4)) '' "$FILE_NAME"
        printf "%*s\tDirectory:\t${YEL}%s${NCL}\n"      $((indent+4)) '' "$DIR"
        printf "%*s\tName only:\t${YEL}%s${NCL}\n"      $((indent+4)) '' "$NAME"
        printf "%*s\tExtension:\t${YEL}%s${NCL}\n"      $((indent+4)) '' "$EXT"
        printf "%*s\tFile size:\t${YEL}%s${NCL}\n"      $((indent+4)) '' "$SIZE"
}

walk() {
        local indent="${2:-0}"
        printf "\n%*s${RED}%s${NCL}\n\n" "$indent" '' "$1"
        # If the entry is a file do some operations
        for entry in "$1"/*; do [[ -f "$entry" ]] && file_specification; done
        # If the entry is a directory call walk() == create recursion
        for entry in "$1"/*; do [[ -d "$entry" ]] && walk "$entry" $((indent+4)); done
}

# If the path is empty use the current, otherwise convert relative to absolute; Exec walk()
[[ -z "${1}" ]] && ABS_PATH="${PWD}" || cd "${1}" && ABS_PATH="${PWD}"
walk "${ABS_PATH}"      
echo                    

3.解释:

  • walk()Zanna 在她的文章中很好地描述了该功能的主要机制回答。所以我只描述新的部分。

  • walk()函数中我添加了这个循环:

    for entry in "$1"/*; do [[ -f "$entry" ]] && file_specification; done
    

    这意味着每个$entry文件都将执行该函数file_specification()

  • 该函数file_specification()有两个部分。第一部分获取与文件相关的数据 - 名称、路径、大小等。第二部分以格式良好的形式输出数据。要格式化数据,请使用命令printf。如果您想调整脚本,您应该阅读有关此命令的信息 - 例如本文

  • 函数file_specification()是您可以放置应该为每个文件执行的特定命令. 使用以下格式:

    命令“${entry}”

    或者您可以将命令的输出保存为变量,然后将此printf变量保存为变量,等等:

    MY_VAR="$(命令“${entry}”)”
    printf "%*s\t文件大小:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$MY_VAR"

    或者直接执行printf该命令的输出:

    printf "%*s\t文件大小:\t${YEL}%s${NCL}\n" $((indent+4)) '' "$(命令“${entry}”)”

  • 最开始的部分称为Colourise the output,它初始化命令中用于printf为输出着色的几个变量。您可以找到有关此内容的更多信息这里

  • 脚本底部添加了处理绝对路径和相对路径的附加条件。

4.使用示例:

  • walk针对当前目录运行:

    walk      # You shouldn't use any argument, 
    walk ./   # but you can use also this format
    
  • walk对任何子目录运行:

    walk <directory name>
    walk ./<directory name>
    walk <directory name>/<sub directory>
    
  • walk要对任何其他目录运行:

    walk /full/path/to/<directory name>
    
  • 根据输出创建文本文件walk

    walk > output.file
    
  • 要创建没有颜色代码的输出文件(来源):

    walk | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g" > output.file
    

5.使用演示:

在此处输入图片描述

答案2

我有点困惑为什么还没有人发布它,但如果你启用选项并使用glob,它确实bash具有递归功能。因此,你可以编写(几乎)纯 脚本,使用递归 globstar,如下所示:globstar**bash

#!/usr/bin/env bash

shopt -s globstar

for i in ./**/*
do
    if [ -f "$i" ];
    then
        printf "Path: %s\n" "${i%/*}" # shortest suffix removal
        printf "Filename: %s\n" "${i##*/}" # longest prefix removal
        printf "Extension: %s\n"  "${i##*.}"
        printf "Filesize: %s\n" "$(du -b "$i" | awk '{print $1}')"
        # some other command can go here
        printf "\n\n"
    fi
done

请注意,这里我们使用参数扩展来获取所需的文件名部分,并且除了获取文件大小du和清理输出之外,我们不依赖外部命令awk

当它遍历你的目录树时,你的输出应该是这样的:

Path: ./glibc/glibc-2.23/benchtests
Filename: sprintf-source.c
Extension: c
Filesize: 326

脚本使用的标准规则适用:确保它可以执行chmod +x ./myscript.sh并通过从当前目录运行它./myscript.sh或将其放在~/bin并运行中source ~/.profile

答案3

你可以用它find来做这项工作

find /path/ -type f -exec ls -alh {} \;

如果您只想列出所有文件及其大小,这将对您有所帮助。

-exec将允许您对 \;用于逐个解析文件的每个文件执行自定义命令或脚本,+;如果您想连接它们(即文件名),则可以使用它。

答案4

如果您知道树的深度,最简单的方法就是使用通配符*

将你想做的每件事写成一个 shell 脚本或函数

function thing() { ... }

然后运行for i in *; do thing "$i"; done,,for i in */*; do thing "$i"; done...等等

在你的函数/脚本中,你可以使用一些简单测试挑选出您想要处理的文件并对它们进行任何您需要的操作。

相关内容