ディレクトリ構造内の各ファイルに関する情報を収集する再帰的bashスクリプト


14

ディレクトリツリーを再帰的に操作して各ファイルで特定のコマンドを実行し、パス、ファイル名、拡張子、ファイルサイズ、およびその他の特定のテキストをbashの単一ファイルに出力するにはどうすればよいですか?


笑、編集してくれてありがとう。私はフーマンの世界で800件の無関係な質問をされることに慣れているので、私は物事を過度に複雑にしたことを認める最初の人になります。だから私は質問で明白なものに答えようとします。私はしかし学びます:-)
SPooKYiNeSS

1
OK、私は質問が何をすべきかについてかなり明確であると思い、ディレクトリツリーを調べて、各ファイルに関する情報を出力します。質問はかなり明確で、すでに回答の量から判断すると、人々はそれをかなりよく理解しています。不明瞭であるための3票は本当にこの質問に値するされていません
Sergiy Kolodyazhnyy

回答:


15

一方でfindソリューションはシンプルかつ強力で、私は基づいている、より複雑なソリューション、作成することを決めたこの興味深い機能私は数日前に見ました、。

  • 現在の内容に基づいた詳細な説明と他の2つのスクリプトをここに示します

1.と呼ばれる実行可能スクリプトファイルを作成します。このファイルは、シェルコマンドとしてアクセスできるようにwalk配置され/usr/local/binています。

sudo touch /usr/local/bin/walk
sudo chmod +x /usr/local/bin/walk
sudo nano /usr/local/bin/walk
  • 内のスクリプトの内容と使用の下にコピーしますnanoShift+ 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()は2つの部分があります。最初の部分は、ファイルに関連するデータ(名前、パス、サイズなど)を取得します。2番目の部分は、適切にフォーマットされた形式でデータを出力します。データをフォーマットするには、コマンドを使用し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)) '' "$(command " $ {entry} ")"

  • と呼ばれる物sectionいのセクション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.使用法のデモンストレーション:

ここに画像の説明を入力してください


これは非常に多くの作業ですが、見栄えはよくなります。よくやった !
セルギーコロディアズニー

これらのgifを@ pa4080にするためにどのプロセスを使用していますか?
-pbhj

@ pbhj、UbuntuでPeekを使用していますが、シンプルで便利ですが、時々クラッシュし、編集機能がありません。ほとんどのGIFはWindowsで作成され、VNC接続のウィンドウを記録しています。主にMS OfficeとGIFの作成に使用している別のデスクトップマシンがあります:)私が使用しているツールはScreenToGifです。オープンソースで無料であり、強力なエディターと処理メカニズムを備えています。残念ながら、Ubuntu用のScreenToGifのようなツールは見つかりません。
pa4080

13

まだ誰も投稿していない理由については少し困惑していbashますが、globstarオプションを有効にして**glob を使用すると、実際には再帰的な機能があります。そのため、次の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ます。


完全なファイル名を印刷している場合、「拡張子」は何を提供しますか?おそらく、"$(file "$i")"(上記のスクリプトでprintfの2番目の部分として)返されるMIME情報が本当に必要ですか?
pbhj

1
@pbhj個人的に私には?なし。しかし、質問をしたOPがを求めた output the path, filename, extension, filesize ので、答えは求められたものと一致します。:)
セルギーコロディアズニー

12

あなたがfind仕事をするのに使うことができます

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

これは、すべてのファイルをサイズでリストするだけの場合に役立ちます。

-exec\;ファイルを1つずつ解析するために使用される各ファイルに対してカスタムコマンドまたはスクリプトを実行できます +;。それらを連結する場合に使用できます(ファイル名を意味します)。


これはいいことですが、OPが述べたすべての要件に答えるわけではありません。
αғsнιη

1
@αғsнιη作業するためのテンプレートを彼に渡しました。質問自体の範囲は広いと思うので、これはこの質問に対する完全な答えではありません。
ラジェシュラジェンドラン

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

ツリーの深さがわかっている場合、最も簡単な方法はワイルドカードを使用すること*です。

シェルスクリプトまたは関数として、やりたいことをすべて書きます。

function thing() { ... }

その後、実行for i in *; do thing "$i"; donefor i in */*; do thing "$i"; done、...など

関数/スクリプト内で、いくつかの簡単なテストを使用して、操作したいファイルを選び出し、必要なことを実行できます。


「ファイル名にスペースが含まれている場合、これは機能しません」...変数を引用するのを忘れたためです!の代わりに「$ i」を使用し$iます。
ムル

@muru no、それが機能しない理由は、「for」ループがスペースで分割されるためです-「/」は、スペースで区切られたすべてのファイルのリストに展開されます。これを回避するには、例えば、ただし、その時点でfindを使用することもできます。
Benubird

@ pa4080はこの答えには関係ありませんが、とにかくとても便利に見えます、ありがとう!
ベヌバード

どのようにfor i in */*機能するか理解していないと思います。ここで、それをテストしますfor i in */*; do printf "|%s|\n" "$i"; done
。– muru

ここでは引用符の重要性の証拠は、次のとおりです。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"' {} \;
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.