再帰関数が機能しない

再帰関数が機能しない

言うべきではないと思いますが、ファイルシステムのすべてのディレクトリに分岐するスクリプトを取得しようとしています。ファイル名は「Everywhere.sh」です。コードは次のとおりです。

#!/bin/bash

recurse(){
    cd $1   
    for INDEX in $(echo *)
    do
        recurse $INDEX
    done
}

recurse /

動作させるにはどうすれば変更できますか(su root -c "./Everywhere.sh"を除く)?

編集:この問題を解決する必要があります。他の実行方法は必要ありません。

答え1

(あなたの質問に対する答えになったようです。その質問の他の側面に対処したいと思います。)

私はあなたが「これは言うべきことではないことを知っています」と言いましたが、ファイルシステムのすべてのディレクトリで特定のコマンドを実行することは、次のように実行できることに言及したいと思います。

find / -type d -exec sh -c 'cd "$1" && some_command' sh {} \;

これは権限の問題を解決しませんが、再帰シェル関数を書くよりもはるかに簡単です。

答え2

元のスクリプトの主な問題の1つは、変更されたことです。入力するディレクトリを終了しないでください。

recurse ()
(
  recurse2 ()
  {
    [ $_recurse_stop -eq 1 ] && return
    cd "./$1" || return
    pwd ## do whatever you want in the pwd
    for entry in * .*;
    do
        [ "." = "$entry" -o ".." = "$entry" ] && continue;
        [ -d "$entry" -a ! -h "$entry" ] && recurse2 "$entry";
    done
    cd ..
  }

  _recurse_stop=0
  trap '_recurse_stop=1' 2
  recurse2 "$1"
)

$(echo *)もう1つの変更は、単純なグローブと交換することです*

また、簡単な修正を行い、再帰ディレクトリのみを試しました(-dテスト)。

@Wildcardと@mikeservのいくつかの洞察力のあるコメントに基づいて、このスクリプトは次のようになります。

  • cd周囲のすべての要素を分離するために最上位のサブシェルを作成します。
  • cdシンボリックリンクディレクトリ()のエントリを拒否します! -h
  • ^C(割り込み)信号を受信すると、再帰を停止するようにトラップを設定します(信号変数を介して)。

答え3

簡単な例:

cdtree()
    if    OLDPWD=${1-.} cd -P - &&
          set . ./.[!.]*/ ./..?*/ ./*/ "" "${1%"${1#.}"}."
    then  while [ "${1:+1}" ]   && shift
          do    [ ! -d "$1" -o  -h "${1%/}" ]|| cdtree "$1"
          done; cd  -P "$2"
    else  printf %s\\n "$PWD/${1#./}"
    fi

先にドットと名付けられたディレクトリを処理し、シンボリックリンクに従うことを.拒否し、シェルの現在の作業ディレクトリをシェルが起動されたディレクトリに復元しようとする合理的な試みをします。

最初の引数を除くすべての引数を無視します。それ以外の場合は、引数なしで呼び出すとツリーのルートに再帰されます.

次の機能があります。

{    find / -type d | wc -l
     cdtree /       | wc -l
}    2>/dev/null

23928
23928

そしてかなり高速です。find約0.6秒で私のルートツリーに移動します。 1.6秒で完了しますcdtree()dashこれは本質的に以前のバージョンと同じ完了時間です。以前のバージョンの実行時間bashは極端なレベルでした。約20倍も長くなりましたdash。今回の編集で5秒未満だと耐えられますが、それでも犬のようです。bash人気が本当に理解できません。

実際の例:

cdtree()
    if    OLDPWD=${1-.} cd -P - &&
          "${cd_tree_callback-:}" "$PWD" &&
          set . ./.[!.]*/ ./..?*/ ./*/ "" "${1%"${1#.}"}."
    then  while [ "${1:+1}" ]   && shift
          do    [ ! -d "$1" -o  -h "${1%/}" ]|| cdtree "$1"
          done; cd  -P "$2"
    else  printf %s\\n "$PWD/${1#./}"
    fi

現在のシェルプロセスでこのような操作を実行する唯一の理由は、どのような方法で現在のシェルの状態に影響を与えるか、または各ディレクトリに対して現在のシェル固有のコマンドを実行することだと思うからです。この例は実際に可能です。

$cd_tree_callbackコマンド名に設定すると、cd_tree直接変更できるすべてのディレクトリに対して実行されます。その結果、現在の作業ディレクトリが変更されると、上記の内容が奇妙に見えることがあります。ただし、これは単なる警告であるため、必要に応じて行う必要があります。そうでなければ、もしそうなら未設定コールバックは機能せず、設定されているが空であるか有効なコマンドでない場合、または呼び出されたコマンドがfalseを返す場合、ツリーの再帰はその時点で破棄され、ディレクトリ名のみがそのレベルでstdoutに印刷されます。ツリーの再帰は以前に戻ります。

'とプリミティブの組み合わせで考えてみてくださいcd_tree_callbackfind-exec-prune

しかも非常に再帰的な例...

cdtree(){
        set '
#       \eval " \shift 0${3+1};'\
'               $1$1\\${1##* } \"\$PWD\" \"\$@\";'\
'       \eval   \"\${1#??}\";}; return"
        cdtree()
                if      OLDPWD=${1%/}   \cd -P -
                then    for  d  in      ./.[!.]*/ ./..?*/ ./*/
                        do      \[ ! -d "$d" -o -h "${d%/}" ] ||
                                \cdtree "${PWD%/}/${d#?/}" "$PWD"
                        done;   \cd -P  "${2:-.}"
                else    \printf %s\\n   "${PWD%/}/${1#?/}"
                fi
        while   \[  "${2+1}"  ] &&  \shift
        do      \[ "${1##/*}" ] &&  \set -- "$PWD$@"
                \[  / = "$1"  ] &&  \set -- "/$@"
                d=   \command eval "\cdtree \"\$1\" \"\$PWD\""
        done
        cdtree(){ \set '\' "$PWD" "$@"
        eval "${1#??}"
}

プログラムが実行時に自分のソースコードを完全に再現できるとき、これを何と呼ぶのか忘れてしまいました…名前がありますが…それは私から抜け出します。とにかくそうなります。まず、基本的に関数の本文全体を保存し、その値を使用して各引数に対してループ内で呼び出すことがcdtree()できる新しい関数を定義します。whileこれにより、必要な数の引数を提供でき、各引数に対してツリーが完全に繰り返されます。これらすべての操作が完了すると、元の状態に戻り、次回の呼び出し時に同じ操作が実行されます。

明らかにこれクイーン...しかし、これはソースコードを出力として印刷するのではなく、評価して変更し、再生成して復元します。

関連情報