如何在目录中随机打乱文件名

如何在目录中随机打乱文件名

假设我有一个文件名目录:

  • 文件_010.ext
  • 文件_011.ext
  • ...
  • 文件_99.ext

获取每个文件并将其随机重命名为目录中的任何其他名称而不存在任何重复的最简单方法是什么,以便在检查时目录看起来相同但内容已重新排列?

答案1

这是shufGNU coreutils 中的一个使用:

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组合两个列表,以便在一列中存在按顺序排列的文件名列表,在另一列中存在按打乱顺序排列的文件名列表。 (如果您不想使用,可以在临时文件中构建列表流程替代如上所述;另外,如果您的 shell 不支持,请替换IFS=$'\t'为。)IFS=$(printf '\t')$''

然后将其输入循环while read,我们根据列表重命名文件,添加后缀以不覆盖任何旧文件。第二个循环只是删除后缀。

上面假设文件名不包含制表符或换行符。

(您可以实施洗牌算法手动在 shell 中,但我认为这有点混乱。)

答案2

描述

这不是解决问题的最短方法,但我认为它很容易理解。

用原始文件制作 tarball 是个好主意

tar -cvzf backup.tar.gz file*

能够在测试后恢复它们(或重复测试)

tar -xvf backup.tar.gz
  • 我使用了一些临时文件
  • 为了使用复制命令构建文件,
  • 打乱文件名,"$tmpdir"/tg3.
  • 运行这些命令时原始文件仍然存在,并且
  • 最后它们被覆盖。
  • 该过程已完成,可以删除临时目录。

shell脚本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*
    
  • 将 shellscript 从此处复制到文本编辑器,并使用该名称将其保存shuffler在同一目录中。

  • 使 shell 脚本可执行

    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是相同的。所有其他文件都有新内容。就像一副牌一样,你可以洗两次或三次......

您还可以从 tarball 中重置文件名并再次执行此操作,每次都会有新的结果,因为shuf将获得新的起始值。

答案3

遵循符号链接的想法ctrl-alt-delor,使用shufGNU coreutils (此代码还使用readlink -fGNU 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

没有什么可以阻止你复制或者移动将文件复制到新目录,或者使用硬链接而不是符号链接(如果$shufdir目录与原始文件位于同一分区)。您只需使用命令更改行即可ln -s

答案4

这是我使用 Fisher–Yates 洗牌算法的脚本:

#!/bin/bash
function swapnames() {
    if [ ! "$2" == "$1" ]; then
        tf=$(mktemp "$1.XXXXXX") && mv "$1" "$tf" && mv "$2" "$1" &&  mv "$tf" "$2" 
    fi
}

function shufflenames() {
    workdir=$1
    pattern=$2
    if [ "$workdir" == "" ]; then workdir="."; fi
    if [ "$pattern" == "" ]; then pattern="*"; fi
    fs=$(printf "%s\n" "$workdir"/$pattern)
    IFS=$'\n' fs=($fs)
    fslen=${#fs[@]}
    ind=1
    while [ $ind -lt $fslen ]; do
        ran=$(($RANDOM%($ind+1)))
        echo swapnames "${fs[$ran]}" "${fs[$ind]}"
        swapnames "${fs[$ran]}" "${fs[$ind]}"
        code=$?
        if [ ! $code -eq 0 ]; then return $code; fi
        ind=$(($ind+1))
    done
}

shufflenames $1 $2

然后你可以:

./shufflenames picsdir

或者

./shufflenames picsdir *.png

相关内容