-
Notifications
You must be signed in to change notification settings - Fork 0
/
pick_random_file
executable file
·114 lines (93 loc) · 3.03 KB
/
pick_random_file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/env perl
# Copyright (c) 2023 Ashlen <[email protected]>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
use v5.36;
use strict;
use warnings;
use autodie;
use File::Basename qw(fileparse);
use Getopt::Std;
use Scalar::Util qw(looks_like_number);
use List::Util qw(shuffle);
our ( $opt_h, $opt_n, $opt_v );
if ( $^O eq 'openbsd' ) {
use OpenBSD::Pledge;
sub pledge_or_die { pledge(@_) or die "Pledge failed: $!"; }
pledge_or_die(qw(rpath flock));
}
sub usage {
my $program_name = fileparse $0;
die <<EOF;
$program_name [-hv] [-n number_of_files] [file ...]
EOF
}
# Reads from STDIN or file(s).
#
# Returns a shuffled list containing each line of the input, excluding
# comments and blanks. If it's an empty list, return undef/false
# instead.
sub shuffled_lines_from_input {
my %line_list;
while ( <<>> ) {
next if /\A \s*? [#]/aaxx;
next if /\A \s* \z/aaxx;
chomp;
# Assume that this line is valid for now. It's more efficient to
# check file validity later (mainly because we may not need to
# check every single item since we bail on the first viable
# one).
++$line_list{$_};
}
scalar(%line_list) or return;
return shuffle keys %line_list;
}
# Accepts an array reference as an argument.
#
# Returns a number of readable files from an array. If there were no
# readable files, return undef/false.
sub valid_files_from_array_ref {
my $array_ref = shift or return;
my $number_of_files = shift // return;
my @files;
while ( my $file = shift @$array_ref ) {
chomp $file;
unless ( -e -r -f $file ) {
warn "Unable to read \"$file\"\n" if $opt_v;
next;
}
# The stat(2) is a more performant way to weed out files, but
# the most reliable indicator is opening the file.
eval { open my $fh, '<', $file; close $fh; };
if ($@) {
warn "Unable to read \"$file\"\n" if $opt_v;
next;
}
push @files, $file;
last if scalar(@files) == $number_of_files;
}
return @files;
}
getopts 'hn:v';
usage if $opt_h;
if ( defined $opt_n ) {
looks_like_number($opt_n) or die "-n only accepts integers.\n";
$opt_n >= 0 or die "-n value must be greater than or equal to 0.\n";
}
my @random_list_of_confs = shuffled_lines_from_input
or die "Failed to shuffle lines\n";
my @chosen_files =
valid_files_from_array_ref( \@random_list_of_confs, $opt_n // 1 )
or die "No valid files were found\n";
pledge_or_die() if $^O eq 'openbsd';
say join( "\n", @chosen_files );