100以上のファイルを含むフォルダを移動しますか?

100以上のファイルを含むフォルダを移動しますか?

多くのフォルダを含むディレクトリがあり、100を超えるファイルを含むフォルダを移動したいと思います。

私はこれを行うつもりです:

find . -type d | while read d; do if 

今これは私にとって難しい部分です。

各ディレクトリに移動して、100個以上のファイルが含まれていることを確認するためにforを実行しますか?では、どうすればよいですか?

for f in *; do cd $f; ll | wc; ?

ディレクトリ内のファイルの総数を取得し、そのディレクトリを移動する方法は少し混乱します(100を超えるファイルが含まれています)。

答え1

ディレクトリに隠されていない名前の数を数えるには、dir次のようにします。

set -- dir/*

これにより、*ディレクトリ内のglobが展開され、位置引数が結果名に設定されます。パターンが一致した場合何もないなら数はです$#

特定の最上位ディレクトリのすべてのディレクトリを繰り返すには、top-dir各ディレクトリの名前数を数え、100を超える名前のディレクトリに対していくつかの操作を実行します。

for subdir in top-dir/*/; do
    set -- "$subdir"/*
    if [ -e "$1" ] && [ "$#" -gt 100 ]; then
        # do something to "$subdir"
    fi
done

bashシェルでnullglobシェルオプションを設定すると、setコマンドが名前と一致するかどうかを確認する必要はありません。

shopt -s nullglob

for subdir in top-dir/*/; do
    set -- "$subdir"/*
    if [[ $# -gt 100 ]]; then
        # do something to "$subdir"
    fi
done

また、dotglobシェルオプションを設定すると、コード内のすべてのパターンが隠された名前と一致するようになります。

上記のコードスニペットでは、「do some to "$subdir"」コメントは、そのサブディレクトリで実行する必要がある操作に置き換えることができます。たとえば、邪魔にならない場所に移動するには、次のようにします。

mv "$subdir" some/other/dir

これにより、そのファイルがディレクトリに移動されますsome/other/dir

答え2

各ディレクトリを順番に循環し、含まれるファイルの数を数え、どこかに移動できます。例えば、

for dir in ./*/
do
    count=$(find "$dir" -maxdepth 1 -type f -printf "x\n" | wc -l)    # Count the number of files in this subdirectory
    [ $count -gt 100 ] && echo mv "$dir"                              # Output a message if we have enough
done

>プロンプトに直接入力するか(最初の行から最後の行までの補助プロンプトが表示されます)、スクリプトファイルに保存して実行できます。

答え3

そしてzsh

mv -- *(/Fe['()(($# > 100)) $REPLY/*(N^-/)']) /dest/

/dest/非表示にも型もない項目が 100 個以上含まれている現在の作業ディレクトリの非表示のサブディレクトリに移動します。 目次(私の考えでは、あなたが言っていると思います。文書)。

これは以下を利用します。zsh グローバル予選(/Fe...)異常(N...))名前以外の基準に従って一致するファイルを追加選択します。

  • /:ファイル形式の選択目次ただ。ここで(globとは対照的に*/)タイプが決まります。今後ここでは、シンボリックリンクを移動すると、シンボリックリンクが切断されることが多いため、シンボリックリンクの確認がより望ましい場合がある。
  • F:選択するいっぱい最適化されたファイル(ディレクトリの場合、これは空でないディレクトリを意味します)
  • e[code]:現在検討中のファイルを含む場所の解釈に基づいてcode選択します。$REPLY

ここ codeにあります()(($# > 100)) $REPLY/*(N^-/)

() <body> <args>インライン関数です。ここでbody((($# > 100)))は、パラメータの数が100より大きいことを確認します。パラメータは$REPLY/*(N^-/)globの拡張です。もう一度使用してください。グローバル予選:

  • N:nullglob:globが次に展開されます。いいえ一致するファイルがない場合、エラーの代わりに引数がまったくありません。
  • ^:次の修飾子を無効にします。
  • -//-次の修飾子(ここ/)を適用する点を除いて、上記と同様です。後ろにシンボリックリンクの解決。だからここで私たちはファイルの数を数えます。いいえタイプ目次シンボリックリンクが解決された後。^-/変更して.計算してください。定期的なファイル(ソケット、fifo、ディレクトリ、シンボリックリンクなど、他のすべての種類のファイルを除く)、または-.一般ファイルと一般ファイルへのシンボリックリンクのみが適用されます。

隠しディレクトリ/ファイルも考慮するには、D修飾子(外部および内部globのいずれかまたは両方)を追加してください。

サブディレクトリのファイル数を繰り返し計算するには、2番目のディレクトリを次のように置き換えます***/*または***/*ディレクトリツリーを下げるときにシンボリックリンクを通過します)。

次に変更してさらに最適化できますcode

()(($#)) $REPLY/*(NoN^-/[101])

oNこれは順序を気にせず、globが101番目の一致ファイルにのみ拡張されるため、ファイルの並べ替えを無効にするために使用されます。私たちはファイルが存在するか(($#))(ゼロ以外の引数の数)テストを行います。


¹そこにある複数のエントリが同じファイルを参照できることに注意してください。たとえば、ハードリンクまたはシンボリックリンクが一緒に接続されている場合です。固有数量の計算文書別の運動だろ

答え4

シェルのアプローチに加えて、慎重に作成されたコマンドのパイプラインを使用してスキャンするfindフォルダ/ファイルを選択し、最終結果をフィルタリングするAwkスクリプトに供給して、できるだけ少ない実行で実行できます。実際のコマンドはxargsmvシェルスクリプトかもしれませんが、通常、Awkはテキスト処理に適しており、高速です。

以下は、改行を含むファイル名をサポートするために、NULLで区切られたI / O GNUツールを処理するためのガイドラインを使用しています。

find . -maxdepth 2 \( -regex '^./[^/]+' -o -type f \) ! -name '.*' -print0 \
    | LC_ALL=C gawk -F/ -v RS='\0' -v ORS='\0' \
        '{if (NF==2) {d=1; n=0} else if (d && ++n>100) {d=0; print $2}}' \
    | xargs -r0 mv -t dest/

このパイプラインは処理中にバッファリングを実行しないため、デフォルトではフォルダとファイルの数に影響されないため、純粋なシェルソリューションよりもリソースが少なくなります。

n>100スクリプトの比較を参照してくださいawk。ここでは、必要に応じてしきい値を調整できます。

パイプラインは「naked」を使用するため、確認するフォルダが含まれているディレクトリで実行されることが予想されますfind .。ただし、デフォルトでは、現在のディレクトリを使用しているカスタムシェル変数を使用して起動ディレクトリを提供できるように、コードの一部を前に追加してfind .簡単に汎用ディレクトリにすることができます。cd -- "${topdir:-.}" &&$topdir.

BSDツールを使用するこれらのパイプと同等の項目は次のとおりです(BSDツールの固有の制限のため、ファイル名への改行サポートを除く)。

find -E . -maxdepth 2 \( -regex '^./[^/]+' -o -type f \) ! -name '.*' \
    | LC_ALL=C awk -F/ -v q=\' \
        '{if (NF==2) {d=1; n=0} else if (d && ++n>100) {d=0; gsub(q, q"\\"q q, $2); print q$2q}}' \
    | xargs sh -c '${1:+mv -- "$@" dest/}' --

POSIXが必要とするファイル名に存在できる区切り文字(文字)を参照するためのさまざまなオプション、および-print0スクリプト-zの追加操作を除いて、デフォルトではGNUツールのバージョンと同じです。-0gsub()awk" ' <space>xargs

後者のパイプは、確認されたパス(フォルダとファイル)に改行文字が含まれていないと仮定すると、すべてのBSDシステムで正しく機能します。

POSIX準拠については、BSDツールパイプラインはコマンドを除くすべてのPOSIXシステムでも機能する必要があります。 POSIXにはAND句がfindないためです。 POSIXに対応する内容は次のとおりです。-maxdepth-regexfind

# replace the find command of the BSD tools version, up to and including the trailing backslash character
find . \( -path '*/*/*' ! -path '*/*/*/*' -type f \) -o \( -path '*/*' ! -path '*/*/*' -type d \) ! -name '.*' \

このfindawkはまた、スクリプト操作を簡素化し、.ディレクトリ階層の3番目のレベル(レベル1があります)と2番目のレベルのディレクトリから一般ファイルを選択するように設計されています。 BSDとGNUにはより強力な条項がないため、find条項ゲームでも同じ結果が得られました-path

最後に、これらのパイプラインは! -name '.*'to 句を通して隠されたファイルを明示的に無視しfind一般ファイルpass-type f句(たとえば、シンボリックリンクを除く)は、あなたの質問によると、最も賢いオプションのように見えますが、隠されたファイルやサブフォルダ、シンボリックリンク、特殊ファイル(名前がパイプ、名前がソケットなど)を本当に考慮したい場合は)フォルダ内に存在し、その句を削除するか、findコマンドの追加句を使用して微調整することができます。後者の場合、makeはfind常に別々のフォルダ名1awkを生成します。これらの別々の名前は、スクリプトが後続の名前が以前の名前2とは異なるフォルダにあることを検出するために使用する「シグナル」であるためです。


1. レベル2のみ

2.パフォーマンスを向上させるために、文字列比較の代わりに真/偽のテストを使用しました。スクリプトdの変数を参照してください。awk

関連情報