假设有一个目录包含300个数据文件。我想随机选择其中 200 个文件并将它们移动到另一个目录中。有没有办法在 Unix/Linux 下做到这一点?
答案1
如果您的系统有shuf
,您可以非常方便地使用它(甚至可以处理难看的文件名):
shuf -zen200 source/* | xargs -0 mv -t dest
如果你没有,shuf
但有一个sort
that take -R
,这应该有效:
find source -type f -print0 | sort -Rz | cut -d $'\0' -f-200 | xargs -0 mv -t dest
答案2
如果这需要统计随机,则不应使用RANDOM % ${#keys[@]}
.考虑:
$RANDOM
有 32768 个唯一值- 第一个选择是 300 个元素中的 1 个
- 32768 = 109 * 300 + 68
因此,当选择第一项时,前 68 个元素中的每一个都有 110/32768~=0.33569% 的机会被选择,而其他 232 个元素中的每一个都有 109/32768~=0.33264% 的机会被选择。以不同的机会重复拾取几次,但每当 时都会偏向第一个元素32768 % ${#keys[@]} -ne 0
,因此错误会复合。
这应该是公正的,并适用于任何文件名:
while IFS= read -r -d '' -u 9
do
mv -- "$REPLY" /target/dir
done 9< <(find /source/dir -mindepth 1 -print0 | shuf -n 200 -z)
答案3
files=(*)
for (( i=0; i<200; i++ )); do
keys=("${!files[@]}")
rnd=$(( RANDOM % ${#keys[@]} ))
key=${keys[$rnd]}
mv "${files[$key]}" "$otherdir"
unset files[$key]
done
答案4
将所有文件名放入 bash 中名为“files”的数组中:
files=( * )
数组大小:
echo ${#files[@]}
定义其中的 2/3 作为样本量:
take=$((2*${#files[@]}/3))
for i in $(seq 1 $take)
do
r=$((RANDOM%${#files[@]}))
echo ${files[r]}
done
这将选择重复项,并且是未使用带有空格等的文件名进行测试。
避免重复的最简单方法是迭代所有文件,并以 2/3 的机会选择每个文件,但这不一定会导致 200 个文件。
如果从列表中选择文件并满足您的要求,这将删除该文件:
#!/bin/bash
files=( * )
# define 2/3 of them as sample size:
take=$((2*${#files[@]}/3))
while (( i < $take ))
do
r=$((RANDOM%${#files[@]}))
f=${files[r]}
if [[ -n $f ]]
then
i=$((i+1))
echo ${files[r]}
unset files[r]
fi
done