web-dev-qa-db-ja.com

ディレクトリ内のファイル名をランダムにシャッフルする方法

ファイル名のディレクトリがあるとしましょう:

  • file_010.ext
  • file_011.ext
  • ...
  • file_99.ext

各ファイルを取得してランダムにディレクトリ内の他の名前にランダムに名前を変更する最も簡単な方法は何ですか?重複がなく、検査でディレクトリは同じに見えますが、内容が入れ替えられていますか?

2
PedsB

GNU coreutilsからのshufを使用したものを以下に示します。

_paste <(printf "%s\n" *) <(printf "%s\n" * | shuf) |
  while IFS=$'\t' read -r from to; do mv -- "$from" "$to.new"; done

for f in *.new; do mv -- "$f" "${f%.new}"; done 
_

_printf "%s\n" *_はファイル名のリストを作成し、shufはそれらをシャッフルします。 pasteは、2つのリストを結合して、1つの列にファイル名のリストがあり、別の列にシャッフルされた順序になります。 (上記のように プロセス置換 を使用しない場合は、一時ファイルにリストを作成できます。また、シェルがそうである場合は、_IFS=$'\t'_をIFS=$(printf '\t')に置き換えます_$''_はサポートされません。)

次に、これは単に_while read_ループに供給され、リストに基づいてファイルの名前を変更し、古いファイルを上書きしないようにサフィックスを追加します。 2番目のループは、サフィックスを削除するだけです。

上記は、ファイル名にタブや改行が含まれていないことを前提としています。

(シェルで手動で shuffle algorithm を実装することもできますが、少し面倒になると思います。)

3
ilkkachu

シンボリックリンクのアイデア ctrl-alt-delor を使用して、shuf from GNU coreutils(このコードでもreadlink -f from GNU coreutils、シンボリックリンクを作成する目的で、特定のファイルの絶対パス名を取得する):

#!/bin/sh -e

shufdir=shufdir  # directory at this path will be deleted and recreated

rm -rf "$shufdir"
mkdir -p "$shufdir"

printf '%s\n' "$@" | shuf |
while read -r fname; do
    ln -s "$( readlink -f "$1" )" "$shufdir/$fname"
    shift
done

スクリプトはファイル名を取り、コマンドラインでシャッフルします。 $shufdirをサブディレクトリとして作成し、指定されたすべてのファイル名をシャッフルします。次に、シャッフルされたファイル名は、$shufdirの下で、コマンドラインで指定されたファイル名の元のリストにシンボリックにリンクされます。

コードは、改行を含まないパス名を想定しています。

テスト:

$ ls -l
total 4
-rw-r--r--  1 kk  wheel    0 Mar 14 18:13 file-1
-rw-r--r--  1 kk  wheel    0 Mar 14 18:13 file-10
-rw-r--r--  1 kk  wheel    0 Mar 14 18:13 file-2
-rw-r--r--  1 kk  wheel    0 Mar 14 18:13 file-3
-rw-r--r--  1 kk  wheel    0 Mar 14 18:13 file-4
-rw-r--r--  1 kk  wheel    0 Mar 14 18:13 file-5
-rw-r--r--  1 kk  wheel    0 Mar 14 18:13 file-6
-rw-r--r--  1 kk  wheel    0 Mar 14 18:13 file-7
-rw-r--r--  1 kk  wheel    0 Mar 14 18:13 file-8
-rw-r--r--  1 kk  wheel    0 Mar 14 18:13 file-9
-rw-r--r--  1 kk  wheel  247 Mar 14 18:39 script.sh
$ sh script.sh file-*
$ ls -l shufdir/
total 0
lrwxr-xr-x  1 kk  wheel  31 Mar 14 18:39 file-1 -> /tmp/Shell-yash.WkdNi1Gd/file-8
lrwxr-xr-x  1 kk  wheel  31 Mar 14 18:39 file-10 -> /tmp/Shell-yash.WkdNi1Gd/file-9
lrwxr-xr-x  1 kk  wheel  31 Mar 14 18:39 file-2 -> /tmp/Shell-yash.WkdNi1Gd/file-4
lrwxr-xr-x  1 kk  wheel  31 Mar 14 18:39 file-3 -> /tmp/Shell-yash.WkdNi1Gd/file-1
lrwxr-xr-x  1 kk  wheel  31 Mar 14 18:39 file-4 -> /tmp/Shell-yash.WkdNi1Gd/file-3
lrwxr-xr-x  1 kk  wheel  31 Mar 14 18:39 file-5 -> /tmp/Shell-yash.WkdNi1Gd/file-2
lrwxr-xr-x  1 kk  wheel  31 Mar 14 18:39 file-6 -> /tmp/Shell-yash.WkdNi1Gd/file-6
lrwxr-xr-x  1 kk  wheel  31 Mar 14 18:39 file-7 -> /tmp/Shell-yash.WkdNi1Gd/file-7
lrwxr-xr-x  1 kk  wheel  31 Mar 14 18:39 file-8 -> /tmp/Shell-yash.WkdNi1Gd/file-5
lrwxr-xr-x  1 kk  wheel  32 Mar 14 18:39 file-9 -> /tmp/Shell-yash.WkdNi1Gd/file-10

copyingまたはmovingから新しいディレクトリ、またはシンボリックリンクの代わりにハードリンクを使用する($shufdirディレクトリが元のファイルと同じパーティションにある場合) ln -sコマンドで行を変更するだけです。

1
Kusalananda

zshの場合:

zmodload zsh/files
Rand() REPLY=$RANDOM
autoload zargs zmv

files=(*.ext)

# shuffle the list by using our custom "Rand" order function
new_files=(*.ext(o+Rand))

# append a ".new" suffix
new_files=($^new_files.new)

# rename files
zargs -l2 -- ${files:^new_files} -- mv --

# remove the .new extension once all the files are renamed
zmv '(*.ext).new' '$1'

@ilkkachuと同じ考えですが、ファイル名に含まれる可能性のある文字を想定しておらず、外部ユーティリティ(zsh/filesは組み込みのmvコマンドを組み込みます)。

0

説明

これは問題を解決するための最短の方法ではありませんが、私は理解するのはかなり簡単だと思います。

元のファイルでtarballを作成することをお勧めします

tar -cvzf backup.tar.gz file*

テスト後にそれらを復元できるようにする(またはテストを繰り返す)

tar -xvf backup.tar.gz
  • いくつかの一時ファイルを使用します
  • コピーコマンドでファイルを作成するには、
  • ファイル名をシャッフルする"$tmpdir"/tg3
  • これらのコマンドを実行している間、元のファイルは残ります。
  • 最後に、それらは上書きされます。
  • プロセスが完了し、一時ディレクトリを削除できます。

シェルスクリプトshuffler

#!/bin/bash

tmpdir=$(mktemp -d)
curdir=$(pwd)

ls -1 file* > "$tmpdir"/orig  # list the file names in a file
sed -i -e 's/^/"/' -e 's/$/"/' "$tmpdir"/orig  # quote to allow special chars

cd "$tmpdir"

shuf orig > shuf           # shuffle the names

paste orig shuf > tg1      # build commands ...
sed 's/^/cp /' tg1 > tg2   # build commands
sed 's/"$/xtmp"/' tg2 > tg3  # make temporary names
#less tg3
cd "$curdir"

bash "$tmpdir"/tg3         # copy the files to shuffled temporary names
rename -f 's/xtmp$//' file*  # overwrite the original files

rm -r "$tmpdir"

デモ例

  • ディレクトリを作成/選択

  • ファイル名と一致する内容のテストファイルを作成する

    for ((i=1;i<100;i++));do echo $i>file_$i;done
    
  • ファイルをtarballに保存します

    tar -cvzf backup.tar.gz file*
    
  • ここからシェルスクリプトをテキストエディターにコピーし、shufflerという名前で同じディレクトリに保存します。

  • シェルスクリプトを実行可能にします

    chmod +x shuffler
    
  • やれ

    ./shuffler
    

これで、次のコマンドラインでファイルが実際にシャッフルされていることを確認できます(この特定のテスト例でのみ有効)

$ for ((i=1;i<100;i++));do echo -n "file_$i: ";j=$(cat file_$i);if [ "$i" == "$j" ]; then echo "$j same";else echo "$j";fi;done
file_1: 98
file_2: 45
file_3: 1
file_4: 5
file_5: 93
file_6: 31
file_7: 52
file_8: 84
file_9: 57
file_10: 44
file_11: 2
file_12: 92
file_13: 32
file_14: 12
file_15: 38
file_16: 10
file_17: 64
file_18: 75
file_19: 30
file_20: 68
file_21: 87
file_22: 26
file_23: 36
file_24: 53
file_25: 50
file_26: 51
file_27: 41
file_28: 49
file_29: 21
file_30: 17
file_31: 61
file_32: 73
file_33: 9
file_34: 16
file_35: 55
file_36: 85
file_37: 24
file_38: 83
file_39: 59
file_40: 18
file_41: 20
file_42: 29
file_43: 66
file_44: 82
file_45: 56
file_46: 48
file_47: 71
file_48: 79
file_49: 14
file_50: 86
file_51: 60
file_52: 43
file_53: 22
file_54: 54 same
file_55: 19
file_56: 89
file_57: 28
file_58: 34
file_59: 77
file_60: 88
file_61: 58
file_62: 4
file_63: 96
file_64: 94
file_65: 39
file_66: 69
file_67: 65
file_68: 7
file_69: 90
file_70: 6
file_71: 8
file_72: 47
file_73: 80
file_74: 25
file_75: 97
file_76: 33
file_77: 13
file_78: 15
file_79: 81
file_80: 37
file_81: 42
file_82: 78
file_83: 74
file_84: 3
file_85: 95
file_86: 76
file_87: 40
file_88: 70
file_89: 99
file_90: 27
file_91: 23
file_92: 11
file_93: 91
file_94: 62
file_95: 35
file_96: 63
file_97: 46
file_98: 72
file_99: 67

下にスクロールして、file_54 同じです。他のすべてのファイルには新しいコンテンツがあります。カードのデッキと同様に、2〜3回シャッフルできます...

Tarballからファイル名をリセットして再度実行することもできます。shufは新しい開始値を取得するため、毎回新しい結果が得られます。

0
sudodus