web-dev-qa-db-ja.com

ReadyNAS Duov1でファイルをアルファベット順のフォルダーに並べ替えるBashスクリプト

未知の種類のLinuxを実行している古いReadyNASDuo v1(Sparc)があります。

1,000以上のファイルを含むフォルダ構造で、ファイル名の最初の文字に基づいてフォルダ構造に移動したい(大文字と小文字は区別されない)。

理想的には、ファイル構造を次のようにしたいと思います。

myfiles-+
        +-A
          + Apple.txt
          + avocado.txt
        +-B
          + Banana.txt
          + broccoli.txt
etc. etc.

私はGoogleを持っていましたが、GUIツールしか見つかりませんでした。

これは、コマンドラインまたはスクリプトを介して実行できますか?

5
BaronGrivet

ここにあなたが望むことをするワンライナーのビットがあります:

$ mkdir -p output/{A..Z}; for i in tstdir/*; do export FILE=$(basename "$i");  LTR=$(echo" ${FILE:0:1}" | tr [a-z] [A-Z]); mv "$i" "output/$LTR/$FILE" ; done

これが展開された形式の同じコマンドで、何が起こっているかを確認できます。

$ mkdir -p output/{A..Z}
$ for i in tstdir/*; do 
    FILE=$(basename "$i")  
    LTR=$(echo "${FILE:0:1}" | tr [a-z] [A-Z])
    mv "$i" "output/$LTR/$FILE"
  done

詳細

上記は最初に、文字だけの出力ディレクトリが存在しないことを前提としているため、それを作成します。

$ mkdir -p output/{A..Z}

forループは次のように機能し、tstdir/*内のすべてのファイルをループします。次に、このパスのbasenameを判別し、変数$FILEに格納します。ループの各反復は、変数$iに格納されます。

FILE=$(basename "$i")

次に、Bashes機能を使用して名前付き変数$FILEの最初の文字を返し、次にtrを使用して小文字を大文字に変換します。

LTR=$(echo "${FILE:0:1}" | tr [a-z] [A-Z])

これをもう少し分解します:

$ echo "${FILE:0:1}"
s
$ echo "${FILE:0:1}"
T

trコードを使用すると、何が起こっているかを確認できます。

$ echo "${FILE:0:1}" | tr [a-z] [A-Z]
S
$ echo "${FILE:0:1}" | tr [a-z] [A-Z]
T

コマンドの残りの部分は、ファイルを対応する最初の文字のディレクトリに移動するだけです。

次のファイルディレクトリがあるとします。

$ touch {a-z}file {A-Z}file

$ tree tstdir/ | head -10
tstdir/
|-- afile
|-- Afile
|-- bfile
|-- Bfile
|-- cfile
|-- Cfile
|-- dfile
|-- Dfile
|-- efile
...

ワンライナーを実行した後:

$ tree output/ | head -10
output/
|-- A
|   |-- afile
|   `-- Afile
|-- B
|   |-- bfile
|   `-- Bfile
|-- C
|   |-- cfile
|   `-- Cfile
...
8
slm

zsh

_mkmv() {mkdir -p -- $argv[-1]:h && mv "$@"}
autoload zmv
zmodload zsh/files

zmv -Qp mkmv '(?)*(^-/)' '${(U)1}/$f'
_
  • zmv 自動ロード可能な関数として、zshの強力なパターンマッチングおよび拡張演算子を使用してファイルを安全にバッチで名前変更する関数。
  • mkmvmvのように機能する関数ですが、必要に応じてターゲットの親ディレクトリも作成する点が異なります。
  • _$argv[-1]_:_$argv_は_$*_のように位置パラメータのリストであり、_$argv[-1]_は最後のパラメータです。ここでは、_$3_と同じようにzmvはそれを_mkmv -- source destination_と呼びます
  • _$var:h_:cshのように、 変数のheadを取得します。つまり、dir名です
  • _zmodload zsh/files_:mkdirmvを含むいくつかのファイル処理ユーティリティの組み込みバージョンを有効にする module をロードします。ここでは、パフォーマンスが大幅に向上しています。 '各ファイルに対して両方を呼び出しています。
  • _-Q_:パターン内のbareグロブ修飾子を有効にします。最近では、_(^-/)_を_(#q^-/)_に書き換えることもできます。
  • _-p mkmv_、zmvの代わりに、名前を変更するプログラムとしてmkmv関数を使用するようにmvに指示します。
  • _(?)*(^-/)_:パターン_(?)*_は、名前を変更するファイルを照合するために使用されるグロブ修飾子を使用します。括弧内の_?_(単一の文字に一致する)はcapturedであるため、置換では_$1_と呼ぶことができます。 。
  • _(^-/)_: glob qualifiers 名前だけでなく、より多くの基準に基づいてファイルを照合するために使用されます:
    • _^_:次の修飾子を無効にします
    • _-_:次の修飾子の場合、シンボリックリンクの場合は、リンク自体ではなく、リンクのターゲットの属性を考慮してください。
    • _/_:ディレクトリタイプのファイルを選択します。前の2つの修飾子を使用すると、ディレクトリでもディレクトリへのシンボリックリンクでもないファイルが必要になります。
  • ${(U)1}:一致したファイルのキャプチャされた最初の文字。Uで大文字に変換されます パラメータ展開フラグ
  • 置換の_$f_は、一致したファイルの完全パスを指します。
3

このスクリプトで試してください:

for first in $(ls -1 | sed 's/^\(.\).*$/\1/' | tr '[a-z0-9]' '[A-Z0-9]' | uniq)
do
    mkdir tmp
    mv "$first"* tmp/
    lower=$(echo $first | tr '[A-Z]' '[a-z]')
    mv "$lower"* tmp/
    mv tmp/ "$first";
done

ロジックは次のとおりです。

  1. 現在のディレクトリ内のすべてのファイルを一覧表示します。
  2. sedを使用して最初の文字を抽出します。
  3. trを使用して、最初の文字を小文字に変更します。これは、比較の数を減らすためです。
  4. uniqで重複を削除します。
  5. 一時ディレクトリを作成し、文字で始まるすべてのファイルをこのフォルダに移動します。
  6. 一時ファイルの名前を変更します。

これらのファイルを使用してスクリプトをテストしました。

Apple.txt
avocado.txt
banana.txt
broccoli.txt
car.txt
dddd.txt
delete.txt
zaad.txt
zdfa.txt

結果は次のとおりです。

.
├── A
│   ├── Apple.txt
│   └── avocado.txt
├── B
│   ├── banana.txt
│   └── broccoli.txt
├── C
│   └── car.txt
├── D
│   ├── dddd.txt
│   └── delete.txt
└── Z
    ├── zaad.txt
    └── zdfa.txt

5 directories, 10 files
1
rendon

ここにブルートフォースアプローチがあります。

for i in {a..z}; do 
 upper=$(printf "%s\n" $i | tr '[a-z]' '[A-Z]'); 
 ls -1 | grep -i "^$i" 2>/dev/null 1>&2 && mkdir -p $upper && 
 mv "$i*" "$u*" $u/ 2>/dev/null; 
done 

これはすべての文字({a..z})、大文字に変換します(tr '[a-z]' '[A-Z]')、その文字で始まるファイルがあるかどうかを確認します(ls -1 | grep -i $i)その場合は、大文字を名前として使用してディレクトリを作成します(mkdir -p $upper)次に、問題の文字(小文字と大文字の両方)で始まるすべてのファイルを対応するフォルダーに移動します。

これをNASで実行していて、おそらく最小限のシェルがインストールされているので、{a..z}形式が機能しない可能性があります。もしそうなら、代わりにこれを行います(I saidこれはブルートフォースアプローチでした):

for i in a b c d e f g h i j k l m n o p q r s t u v w x y z; do 
 upper=$(printf "%s\n" $i | tr '[a-z]' '[A-Z]'); 
 ls -1 | grep -i "^$i" 2>/dev/null 1>&2 && mkdir -p $upper && 
 mv "$i*" "$u*" $u/ 2>/dev/null; 
done 
0
terdon

SLMの回答を改善するために、0から9までのディレクトリーを作成することにより、数字で始まるファイルに対してこれを機能させることもできます。

mkdir -p output/{A..Z}; mkdir -p output/{0..9}; for i in tstdir/*; do export FILE=$(basename "$i"); LTR=$(echo "${FILE:0:1}" | tr [a-z] [A-Z]); mv "$i" "output/$LTR/$FILE" ; done
0
johnavp1989