递归bash脚本以收集有关目录结构中每个文件的信息


14

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


大声笑,谢谢你的编辑;我将是第一个承认我过于复杂的人,因为我习惯于在流氓世界中被问过800个不相关的问题;因此,我尝试回答问题中最明显的问题;我会学习的:-)
SPooKYiNeSS

1
好的,我认为问题很明确,应该做什么,遍历目录树并输出有关每个文件的信息。这个问题很清楚,从已经得到的答案来看,人们已经很了解了。不清楚的3票确实不值得这个问题
Sergiy Kolodyazhnyy 17-10-25

Answers:


16

尽管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.用法说明:

在此处输入图片说明


这是很多工作,但是看起来不错。做得好 !
Sergiy Kolodyazhnyy

您使用什么过程制作这些gifs @ pa4080?
pbhj

@pbhj,在Ubuntu下,我正在使用Peek,它既简单又好用,但有时会崩溃,并且没有编辑功能。我的大多数GIF都是在Windows下创建的,我正在其中记录VNC连接的窗口。我有一台主要用于MS Office和GIF创建的台式机:)我使用的工具是ScreenToGif。它是开源的,免费的,并具有强大的编辑器和处理机制。不幸的是,我找不到适用于Ubuntu的ScreenToGif之类的工具。
pa4080

13

对于为什么没有人发布它,我有些困惑,但是bash如果启用globstaroption并使用**glob ,则确实具有递归功能。这样,您可以编写(几乎)bash 使用该递归globstar的纯脚本,如下所示:

#!/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或将其放置在~/binrun中source ~/.profile


如果要打印完整文件名,“扩展名”还能给您带来什么?也许您真的想要"$(file "$i")"(在上面的脚本中作为printf的第二部分)返回的MIME信息?
pbhj

1
@pbhj对我个人而言?没有。但是问了问题的OP要求了 output the path, filename, extension, filesize ,因此答案与所要的相匹配。:)
Sergiy Kolodyazhnyy

12

你可以find用来做这项工作

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

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

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


这很好,但是不能满足上述所有要求。
αғsнιη

1
@αғsнιη我刚刚给了他一个工作模板。我知道,这并不是对这个问题的完整答案,因为我认为这个问题本身涉及面很广。
Rajesh Rajendran

6

随着find而已。

find /path/ -type f -printf "path:%h  fileName:%f  size:%kKB Some Text\n" > to_single_file

或者,您可以改用以下代码:

find -type f -not -name "to_single_file"  -execdir sh -c '
    printf "%s %s %s %s Some Text\n" "$PWD" "${1#./}" "${1##*.}" $(stat -c %s "$1")
' _ {} \; > to_single_file

2
优雅而简单(如果您了解find -printf)。+1
大卫·佛斯特

1

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

写出您想作为shell脚本或函数执行的所有操作

function thing() { ... }

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

在函数/脚本中,您可以使用一些简单的测试来选择要使用的文件,并对它们进行任何处理。


“如果您的文件名中有空格,这将不起作用”……因为您忘记了引用变量!使用“ $ i”代替$i
muru 17-10-25,9

@muru不,它不起作用的原因是因为“ for”循环在空格上分割-“ / '被扩展为所有文件的以空格分隔的列表。您可以解决此问题,例如,将IFS弄乱,但
那时候

@ pa4080与这个答案无关,但是反正看起来超级有用,谢谢!
贝努伯(Binubird)

我认为您不了解其for i in */*运作方式。在这里进行测试:for i in */*; do printf "|%s|\n" "$i"; done
大师

这是引号重要性的证据:i.stack.imgur.com/oYSj2.png
pa4080

1

find 可以做到这一点:

find ./ -type f -printf 'Size:%s\nPath:%H\nName:%f\n'

看一下 man find其他文件属性。

如果您确实需要扩展名,可以添加以下内容:

find ./ -type f -printf 'Size:%s\nPath:%H\nName:%f\nExtension:' -exec sh -c 'echo "${0##*.}\n"' {} \;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.