web-dev-qa-db-ja.com

ターミナル/ bashスクリプトでフォルダを複数のサブフォルダに分割する

私はいくつかのフォルダを持っており、それぞれに15,000〜40,000枚の写真があります。これらのそれぞれをサブフォルダーに分割したいと思います-それぞれに2,000個のファイルがあります。

外出先で必要な各フォルダーを作成し、すべてのファイルを移動する、これを行う簡単な方法は何ですか?

現在、フォルダ内の最初のx個のアイテムを既存のディレクトリに移動する方法しか見つかりません。 20,000個のアイテムがあるフォルダーでこれを使用するには...手動で10個のフォルダーを作成し、コマンドを10回実行する必要があります。

ls -1  |  sort -n | head -2000| xargs -i mv "{}" /folder/

Forループに入れてみましたが、mkdirで適切にフォルダーを作成するのに問題があります。それを回避した後でも、20番目のファイル(新しいグループの開始)ごとにフォルダーを作成するだけのプログラムが必要です。ファイルごとに新しいフォルダを作成したいと考えています。

では、どうすれば、多数のファイルを、それぞれの任意の数のファイルのフォルダーに簡単に移動できますか?

どんな助けでも非常に...まあ...役に立ちます!

10
Brian C

このソリューションは、空白とワイルドカードを含む名前を処理でき、簡単に拡張して、それほど単純ではないツリー構造をサポートできます。作業ディレクトリのすべての直接サブディレクトリでファイルを検索し、それらをそれらの新しいサブディレクトリに分類します。新しいディレクトリの名前は01など:

#!/bin/bash

maxfilesperdir=20

# loop through all top level directories:
while IFS= read -r -d $'\0' topleveldir
do
        # enter top level subdirectory:
        cd "$topleveldir"

        declare -i filecount=0 # number of moved files per dir
        declare -i dircount=0  # number of subdirs created per top level dir

        # loop through all files in that directory and below
        while IFS= read -r -d $'\0' filename
        do
                # whenever file counter is 0, make a new dir:
                if [ "$filecount" -eq 0 ]
                then
                        mkdir "$dircount"
                fi

                # move the file into the current dir:
                mv "$filename" "${dircount}/"
                filecount+=1

                # whenever our file counter reaches its maximum, reset it, and
                # increase dir counter:
                if [ "$filecount" -ge "$maxfilesperdir" ]
                then
                        dircount+=1
                        filecount=0
                fi
        done < <(find -type f -print0)

        # go back to top level:
        cd ..
done < <(find -mindepth 1 -maxdepth 1 -type d -print0)

find -print0/readプロセス置換との組み合わせが 別の質問 から盗まれました。

単純なグロブは、あらゆる種類の奇妙なディレクトリ名とファイル名も処理できることに注意してください。ただし、複数レベルのディレクトリに簡単に拡張することはできません。

3
Michael Jaros

次のようなものを試してください。

for i in `seq 1 20`; do mkdir -p "folder$i"; find . -type f -maxdepth 1 | head -n 2000 | xargs -i mv "{}" "folder$i"; done

フルスクリプトバージョン:

#!/bin/bash

dir_size=2000
dir_name="folder"
n=$((`find . -maxdepth 1 -type f | wc -l`/$dir_size+1))
for i in `seq 1 $n`;
do
    mkdir -p "$dir_name$i";
    find . -maxdepth 1 -type f | head -n $dir_size | xargs -i mv "{}" "$dir_name$i"
done

ダミーの場合:

  1. 新しいファイルを作成します:vim split_files.sh
  2. dir_sizedir_nameの値を希望に合わせて更新します
    • dir_nameには番号が追加されることに注意してください
  3. 目的のフォルダに移動します:cd my_folder
  4. スクリプトを実行します:sh ../split_files.sh
11
tmp

以下のコードは、ファイル名に改行、スペース、タブ、一重引用符、二重引用符、または円記号が含まれておらず、ファイル名がダッシュで始まらないことを前提としています。また、IFSは_while read_の代わりに_while IFS= read_を使用し、変数が引用符で囲まれていないため、変更されていないことも前提としています。 Zshに_setopt shwordsplit_を追加します。

_i=1;while read l;do mkdir $i;mv $l $((i++));done< <(ls|xargs -n2000)
_

以下のコードは、ファイル名に改行が含まれておらず、ダッシュで始まらないことを前提としています。 _-n2000_は一度に2000個の引数を取り、_{#}_はジョブのシーケンス番号です。 _{#}_を'{=$_=sprintf("%04d",$job->seq())=}'に置き換えて、数字を4桁に埋め込みます。

_ls|parallel -n2000 mkdir {#}\;mv {} {#}
_

以下のコマンドは、ファイル名に改行が含まれていないことを前提としています。これは、Homebrewのrename式であるAristotlePagaltzisによるrenameの実装を使用します。ここで、ディレクトリを作成するには_-p_が必要であり、パスを取得するには_--stdin_が必要です。 STDINから、ここで_$N_はファイルの番号です。他の実装では、_$._の代わりに_++$::i_または_$N_を使用できます。

_ls|rename --stdin -p 's,^,1+int(($N-1)/2000)."/",e'
_
5
nisetama

私はこのようなもので行きます:

#!/bin/bash
# outnum generates the name of the output directory
outnum=1
# n is the number of files we have moved
n=0

# Go through all JPG files in the current directory
for f in *.jpg; do
   # Create new output directory if first of new batch of 2000
   if [ $n -eq 0 ]; then
      outdir=folder$outnum
      mkdir $outdir
      ((outnum++))
   fi
   # Move the file to the new subdirectory
   mv "$f" "$outdir"

   # Count how many we have moved to there
   ((n++))

   # Start a new output directory if we have sent 2000
   [ $n -eq 2000 ] && n=0
done
4
Mark Setchell

この解決策 MacOSで私のために働いた:

i=0; for f in *; do d=dir_$(printf %03d $((i/100+1))); mkdir -p $d; mv "$f" $d; let i++; done

それぞれ100要素のサブフォルダーを作成します。

3

そのためのスクリプトを作成する必要があります。スクリプトに含めるべきことのヒント:

まず、ソースディレクトリ内のファイルの数を数えます

NBFiles=$(find . -type f -name *.jpg | wc -l)

この数を2000で割り、1を足して、作成するディレクトリの数を決定します

NBDIR=$(( $NBFILES / 2000 + 1 ))

最後に、ファイルをループして、サブディレクトリ間でファイルを移動します。 2つの暗黙のループを使用する必要があります。1つは宛先ディレクトリを選択して作成し、もう1つはこのサブディレクトリに2000ファイルを移動し、次のサブディレクトリを作成して次の2000ファイルを新しいものに移動します。

0
jderefinko

上記の答えは非常に便利ですが、Mac(10.13.6)端末には非常に重要なポイントがあります。 xargs "-i"引数が使用できないため、コマンドを上から下に変更しました。

ls -1 | sort -n | head -2000| xargs -I '{}' mv {} /folder/

次に、以下のシェルスクリプトを使用します(tmpの回答を参照)

#!/bin/bash

dir_size=500
dir_name="folder"
n=$((`find . -maxdepth 1 -type f | wc -l`/$dir_size+1))
for i in `seq 1 $n`;
do
    mkdir -p "$dir_name$i";
    find . -maxdepth 1 -type f | head -n $dir_size | xargs -I '{}' mv {} "$dir_name$i"
done
0
冯剑龙

これはMarkSetchellの微調整です

使用法:bash splitfiles.bash $ PWD/directoryoffiles splitsize

スクリプトを分割するファイルと同じディレクトリに配置する必要はありません。スクリプトは.jpgだけでなくすべてのファイルで動作し、引数として分割サイズを指定できます。

#!/bin/bash
# outnum generates the name of the output directory
outnum=1
# n is the number of files we have moved
n=0

if [ "$#" -ne 2 ]; then
    echo Wrong number of args
    echo Usage: bash splitfiles.bash $PWD/directoryoffiles splitsize
    exit 1
fi

# Go through all files in the specified directory
for f in $1/*; do
   # Create new output directory if first of new batch
   if [ $n -eq 0 ]; then
      outdir=$1/$outnum
      mkdir $outdir
      ((outnum++))
   fi
   # Move the file to the new subdirectory
   mv "$f" "$outdir"

   # Count how many we have moved to there
   ((n++))

   # Start a new output directory if current new dir is full
   [ $n -eq $2 ] && n=0
done
0
ezekiel