如何从bash中的目录中选择随机文件?

Mar*_*rie 122 random bash

我有一个约2000个文件的目录.如何N通过使用bash脚本或管道命令列表来选择随机的文件样本?

Jos*_*Lee 161

这是一个使用GNU sort的随机选项的脚本:

ls |sort -R |tail -$N |while read file; do
    # Something involving $file, or you can leave
    # off the while to just get the filenames
done
Run Code Online (Sandbox Code Playgroud)

  • sort:无效选项 - R尝试`sort --help'以获取更多信息. (5认同)
  • 酷,不知道 sort -R; 我以前使用过 bogosort :-p (3认同)
  • 似乎不适用于其中包含空格的文件. (2认同)
  • [为什么*不*解析 `ls`?](http://unix.stackexchange.com/questions/128985/why-not-parse-ls) (2认同)

Nor*_*ame 89

你可以使用shuf(来自GNU coreutils包).只需输入一个文件名列表,并要求它从随机排列中返回第一行:

ls dirname | shuf -n 1
# probably faster and more flexible:
find dirname -type f | shuf -n 1
# etc..
Run Code Online (Sandbox Code Playgroud)

调整该-n, --head-count=COUNT值以返回所需行数.例如,要返回5个随机文件名,您将使用:

find dirname -type f | shuf -n 5
Run Code Online (Sandbox Code Playgroud)

  • OP想要选择`N`随机文件,所以使用`1`有点误导. (4认同)
  • 但是`N`可以是'1` (4认同)
  • 如果你有带换行符的文件名:`find dirname -type f -print0 | shuf -zn1` (4认同)
  • 如果必须将这些随机选择的文件复制到另一个文件夹怎么办?如何对这些随机选择的文件执行操作? (3认同)
  • 请注意,如果您使用的是带有 OS X 和 `zsh` 的 Mac,则可能不会安装 GNU `shuf` 命令。所以你必须先安装 shuf,或者直接使用 Josh Lee 的方法。 (2认同)

gni*_*urf 18

以下是一些不解析输出的可能性,ls对于名称中带有空格和滑稽符号的文件,这些可能性是100%安全的.所有这些都将randf使用随机文件列表填充数组.printf '%s\n' "${randf[@]}"如果需要,可以轻松打印此阵列.

  • 这个可能会多次输出相同的文件,N需要事先知道.在这里我选择N = 42.

    a=( * )
    randf=( "${a[RANDOM%${#a[@]}]"{1..42}"}" )
    
    Run Code Online (Sandbox Code Playgroud)

    此功能没有很好的记录.

  • 如果事先不知道N,但你真的很喜欢以前的可能性,你可以使用eval.但它是邪恶的,你必须确保N不经过彻底检查就直接来自用户输入!

    N=42
    a=( * )
    eval randf=( \"\${a[RANDOM%\${#a[@]}]\"\{1..$N\}\"}\" )
    
    Run Code Online (Sandbox Code Playgroud)

    我个人不喜欢eval这个答案!

  • 使用更简单的方法(循环):

    N=42
    a=( * )
    randf=()
    for((i=0;i<N;++i)); do
        randf+=( "${a[RANDOM%${#a[@]}]}" )
    done
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果您不希望多次使用同一个文件:

    N=42
    a=( * )
    randf=()
    for((i=0;i<N && ${#a[@]};++i)); do
        ((j=RANDOM%${#a[@]}))
        randf+=( "${a[j]}" )
        a=( "${a[@]:0:j}" "${a[@]:j+1}" )
    done
    
    Run Code Online (Sandbox Code Playgroud)

注意.这是旧帖子的迟到答案,但是接受的答案链接到显示可怕的练习的外部页面,而另一个答案并不是更好,因为它也解析了输出ls.对接受的答案的评论指出了Lhunath的一个很好的答案,它显然表现出良好的实践,但并没有完全回答OP.


sil*_*gon 9

ls | shuf -n 10 # ten random files
Run Code Online (Sandbox Code Playgroud)

  • 你不应该依赖 `ls` 的输出。如果例如文件名包含换行符,这将不起作用。 (3认同)
  • @CiprianTomoiaga这是你可能遇到的问题的一个例子.`ls`不能保证给你"干净"的文件名,所以你不应该依赖它,句号.这些问题罕见或不寻常的事实并没有改变问题; 特别是考虑到有更好的解决方案. (3认同)
  • @bfontaine你似乎被文件名中的换行符所困扰:).他们真的很常见吗?换句话说,是否有一些工具可以创建名称中带换行符的文件?由于作为用户,创建这样的文件名非常困难.来自互联网的文件也是如此 (2认同)

sca*_*cai 8

一种简单的解决方案,用于选择5随机文件,同时避免解析ls.它还适用于包含空格,换行符和其他特殊字符的文件:

shuf -ezn 5 * | xargs -0 -n1 echo
Run Code Online (Sandbox Code Playgroud)

替换echo为要为文件执行的命令.

  • 你是对的。我以前的解决方案不适用于包含换行符的文件名,并且可能还会破坏带有某些特殊字符的其他文件名。我已经更新了答案,使用空终止而不是换行符。 (2认同)

Ken*_*Ken 6

这是对@gniourf_gniourf 迟到的回答的更晚的回应,我只是赞成,因为它是迄今为止最好的答案,两次。(一次是为了避免eval,一次是为了安全的文件名处理。)

但是我花了几分钟来解开这个答案使用的“没有很好记录的”功能。如果您的 Bash 技能足够扎实,可以立即看到它是如何工作的,那么请跳过此评论。但我没有,并且解开它我认为值得解释。

功能#1是shell 自己的文件globbing。a=(*)创建一个数组,$a,其成员是当前目录中的文件。Bash 理解文件名的所有奇怪之处,因此可以保证列表正确,保证已转义等。无需担心正确解析ls.返回的文本文件名。

功能#2是击参数扩展阵列,一个嵌套在另一个。这以 开头${#ARRAY[@]},它扩展到 的长度$ARRAY

然后使用该扩展为数组添加下标。在 1 和 N 之间找到随机数的标准方法是取随机数的值对 N 取模。我们想要一个介于 0 和数组长度之间的随机数。这是方法,为清楚起见分为两行:

LENGTH=${#ARRAY[@]}
RANDOM=${a[RANDOM%$LENGTH]}
Run Code Online (Sandbox Code Playgroud)

但是这个解决方案在一行中完成,删除了不必要的变量赋值。

功能 #3Bash 大括号扩展,尽管我必须承认我并不完全理解它。括号扩展使用,例如,产生的25个文件命名列表filename1.txtfilename2.txt等等:echo "filename"{1..25}".txt"

上面子shell 中的表达式"${a[RANDOM%${#a[@]}]"{1..42}"}",使用这个技巧产生了42 个单独的扩展。大括号扩展在 the]和 the之间放置一个数字},起初我认为它是数组的下标,但如果是这样,它前面会有一个冒号。(它也会从数组中的一个随机位置返回 42 个连续的项目,这与从数组中返回 42 个随机项目完全不同。)我认为这只是让 shell 运行扩展 42 次,从而返回数组中的 42 个随机项。(但如果有人能更全面地解释它,我很想听听。)

N 必须硬编码(到 42)的原因是大括号扩展发生在变量扩展之前。

最后,这是Feature #4,如果您想对目录层次结构递归执行此操作:

shopt -s globstar
a=( ** )
Run Code Online (Sandbox Code Playgroud)

这将打开导致递归匹配的shell 选项**。现在您的$a数组包含整个层次结构中的每个文件。


Mar*_*ark 5

如果您安装了Python(适用于Python 2或Python 3):

要选择一个文件(或任意命令中的一行),请使用

ls -1 | python -c "import sys; import random; print(random.choice(sys.stdin.readlines()).rstrip())"
Run Code Online (Sandbox Code Playgroud)

要选择N文件/行,请使用(注意N在命令末尾,用数字代替)

ls -1 | python -c "import sys; import random; print(''.join(random.sample(sys.stdin.readlines(), int(sys.argv[1]))).rstrip())" N
Run Code Online (Sandbox Code Playgroud)


NaW*_*eeD 5

如果要将这些文件的示例复制到另一个文件夹:

ls | shuf -n 100 | xargs -I % cp % ../samples/
Run Code Online (Sandbox Code Playgroud)

显然首先要创建示例目录。