如何在Unix/Linux操作系统中移动指定数量的随机文件

36

我面临着一个简单的任务,但是我也在想最简单和最短的方法是什么。

我的建议是将指定数量的随机文件从一个目录移动到另一个目录。这个任务是我为机器学习需要创建两个数据集的一部分:一个训练集和一个测试集。我的目标是从一个目录中移除10%的文件,以获得可用于测试分类器的数据集,并从源目录获取训练集。

那么,这个“移动n个随机文件”的任务最紧凑的打法是什么?

提前感谢您的帮助 - 像往常一样 -


1
如果您的文件具有统一、均匀分布的数字文件名模式,“mv *0 otherdir”将移动10%——它不是随机的,但它是均匀分布的,这甚至可能更接近您实际想要的;而且,它很容易记录和复制。 - tripleee
1
不幸的是这不是我的情况,但你的建议真的很好。 - Max
8个回答

68

使用 shufxargs 的组合(最好查看它们的文档,使用 man 命令):

shuf -n 10 -e * | xargs -i mv {} path-to-new-folder

以上命令选择当前文件夹(*部分)中的10个随机文件,然后将它们移动到新文件夹。

更新

虽然较长,但此版本可能更易于理解:

ls | shuf -n 10 | xargs -i mv {} path-to-new-folder

shuf 只是生成标准输入的随机排列,并将结果限制为10个(就像使用head一样,但可能更快)。


该命令已在Debian/Ubuntu上进行了测试,shuf的版本为8.25 - boechat107
@Mithril 当文件夹中的文件过多时(在我的情况下超过了20万个),我遇到了相同的“-bash:/usr/bin/shuf:参数列表太长”的错误。 - Katerina
@mrwheet,你可以尝试在ls之后应用head -n <N>tail -n <N>,例如。这将为shuf产生一个较短的参数列表。如果需要,您可以重复此过程。 - boechat107
太好了,这对我有帮助。谢谢。 - Kalle
3
这在 Mac 上无法工作。在 Mac 上,您需要执行 ls | gshuf -n 10 | xargs -I {} mv {} path-to-new-folder - Ibukun Muyide
@boechat107,我喜欢第二个选项,因为它在参数列表很长的情况下也能正常工作。 - Satya Prakash Dash

3
您可以使用Bash随机生成器,它会生成0到32767之间的整数,以决定文件是否应该放置在set1或set2中。代码如下:
for file in ./*; do
  val=$RANDOM
  if test $val -gt 3276; then
    mv "$file" ../set1
  else
    mv "$file" ../set2
  fi
done

2

你可以使用shuf或sort -R来随机打乱文件列表。 但你仍然需要取一个子集,可以使用head/tail命令来实现。


1
所以 ls $source_dir | sort -R | head -${tobemoved} | xargs -i mv $target_dir/{} - 或者类似的指令。 - Mats Petersson
在某些Unix变体中,例如OSX,不支持sort -R - Martin Baulig

2

使用find命令的替代版本以避免与文件夹相关的问题。它将31415个随机选择的文件复制到/home/user/dir/中。

find . -maxdepth 1 -type f | sort -R | head -31415 | xargs cp -t /home/user/dir/

这将复制文件而不是移动它们,因此根据用户的需求,这不会将数据集分离为训练集和测试集,但如果我错了,请纠正我。 - Jack Avante

2

你也可以使用Python来完成这个任务。我认为这更容易。

以下是我使用的Python脚本,用于移动随机百分比的图像,并获取通常在计算机视觉图像数据集中所需的关联标签数据集。请注意,此脚本会移动文件,因为我不希望我的测试训练数据集在我的训练数据集中。

对于Yolo训练集,我将标签和图像放在同一个目录中,并且标签是txt文件,因此我使用以下代码:

import numpy as np
import os
import random

#set directories
directory = str('/MauiData/maui_complete_sf_train')
target_directory = str('/MauiData/maui_complete_sf_test')
data_set_percent_size = float(0.07)

#print(os.listdir(directory))

# list all files in dir that are an image
files = [f for f in os.listdir(directory) if f.endswith('.jpg')]

#print(files)

# select a percent of the files randomly 
random_files = random.sample(files, int(len(files)*data_set_percent_size))
#random_files = np.random.choice(files, int(len(files)*data_set_percent_size))

#print(random_files)

# move the randomly selected images by renaming directory 

for random_file_name in random_files:      
    #print(directory+'/'+random_file_name)
    #print(target_directory+'/'+random_file_name)
    os.rename(directory+'/'+random_file_name, target_directory+'/'+random_file_name)
    continue

# move the relevant labels for the randomly selected images

for image_labels in random_files:
    # strip extension and add .txt to find corellating label file then rename directory. 
    os.rename(directory+'/'+(os.path.splitext(image_labels)[0]+'.txt'), target_directory+'/'+(os.path.splitext(image_labels)[0]+'.txt'))

    continue

2
最初的回答:问题很久以前就提出了,但为记录起见,在OSX上可以解决这个问题。
你需要使用brew install coreutils安装gshuf,然后使用以下命令:
tenpercent=$((`ls | wc -l` * 10/100))

ls | gshuf -n $tenpercent | xargs -I {} mv {} destination/path/

0

使用zsh shell:

mv -- *(oe['REPLY=$RANDOM'][1,10]) /path/to/destination/

我们在编写代码时使用oe全局限定符来根据给定代码的evaluation(这里返回一个随机值)对全局扩展进行排序,并选择前10个。

在最近的GNU系统中,以及支持ksh-style进程替换的shell(ksh93、zsh、bash),您可以执行以下操作:

xargs -r0a <(ls -U --zero | shuf -zn10) mv -t /path/to/destination --

ls -U --zero 可以被替换为 printf '%s\0' *,因为 GNU 的 ls 版本太旧不支持 --zero。不同的是,如果当前目录中没有非隐藏文件,则会出现有关无法移动名为 * 的文件的错误。

xargs -r0a <(shuf -zen10 -- *) mv -t /path/to/destination --

可以作为 @boechat107's answer 更正确/可靠/高效的变体,但需要注意的是,如果当前工作目录中有大量非隐藏文件,则在尝试执行 shuf 时可能会遇到 参数列表过长 错误。上面基于printf的方法在这方面应该是可以正常运行的,因为printf通常内置于shell中,所以不受execve()系统调用的限制。

要移动10%而不是10,在zsh中:

files=(*(Noe['REPLY=$RANDOM']))
mv -- $files[1,$#files/10] /path/to/destination/

(向下取整)的10%。


0
通常,我们使用Python脚本或Java程序来完成此操作。这两种方法都可以使用适当的随机数生成器来做出随机决策,然后调用必要的函数来移动文件。

有趣,所以你建议使用随机数生成器...当然,给xarg设置限制并移动文件不会得到适当的随机选择。我会考虑一下。 - Max
没有 RNG,你怎么可能实现“随机”? - bmargulies
当然,这是不可能的。我只是在考虑从一个目录中取一些文件,做类似于ls | head -20的操作。我想如果对同一个包含数百个文件的目录重复执行此任务数百次,结果可能并不总是相同。也许我错了吗? - Max
使用bash随机数生成器是否被认为是好的建议?-您可以从选择要移动的文件的随机数量开始,然后-您可以将文件列出到tmp.file中-并读取随机行以获取文件并将其移动; `#!/bin/bash#获取范围 file_list_no=ls -1 | wc -lnumber=$RANDOM let "number %= $file_list_no"echo "选择要移动的随机文件数:$number of $file_list_no"` - java_xof
哇,我不知道bash有随机生成器。无论如何,我需要获取一定数量的随机文件。 - Max
ls | head 的顺序总是相同的。 - bmargulies

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接