![递归 bash 脚本用于收集目录结构中每个文件的信息](https://linux22.com/image/1003598/%E9%80%92%E5%BD%92%20bash%20%E8%84%9A%E6%9C%AC%E7%94%A8%E4%BA%8E%E6%94%B6%E9%9B%86%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84%E4%B8%AD%E6%AF%8F%E4%B8%AA%E6%96%87%E4%BB%B6%E7%9A%84%E4%BF%A1%E6%81%AF.png)
如何通过目录树递归工作并在每个文件上执行特定命令,并将路径、文件名、扩展名、文件大小和其他一些特定文本输出到 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
- 复制以下脚本内容并使用
nano
:Shift+Insert粘贴;Ctrl+O和Enter保存;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
...等等
在你的函数/脚本中,你可以使用一些简单测试挑选出您想要处理的文件并对它们进行任何您需要的操作。