-
Notifications
You must be signed in to change notification settings - Fork 6
/
secret-santa
executable file
·106 lines (90 loc) · 2.81 KB
/
secret-santa
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
#!/bin/sh
__usage () {
cat << EOH
$0 -i file [-n file] [-o file]
$0 generates a Secret Santa list, making sure that no one in the same
team gifts each other (e.g. spouses), and that no one gifts themselves. The
output is one single loop of santa-santee: A gifts B, B gifts C,... Z gifts A.
There are no checks for sanity of input files, and no protection against
infinite loops. Proceed with caution.
-i file:
input file; default: none
Input: full participant description, one per line
# email team Full Name with spaces
santa@cla.us XMas Santa Claus
moviuro@popho.be noob Moviuro
-o file:
output file; default: out.${0##*/}
this file will be overwritten if it exists
Output: pair information, one per line
# email_santa Full Santee Name
santa@cla.us Moviuro
moviuro@popho.be Santa Claus
-n file:
negate file; default: <none>
this file holds data that we do not wish to see in the output; this can be
used with previous results of $0 so that noone has
to gift the same person N years in a row.
Abusing this functionnality can cause an infinite loop if there are no valid
list of santa-santee left.
Negate: pair information, one per line, same as Output
# email_santa Full Santee Name
santa@cla.us Foo Bar
santa@cla.us Moviuro
moviuro@popho.be Santa Claus
foo@example.org Jane Doe
EOH
}
while getopts ":hi:n:o:" _opt; do
case "$_opt" in
h) __usage ; exit ;;
i) _in="$OPTARG" ;;
o) _out="$OPTARG" ;;
n) _nfile="$OPTARG" ;;
*) __usage >&2 ; exit 1 ;;
esac
done
[ -z "$_out" ] && _out="out.${0##*/}"
: "${_in?requires an input file}"
_tmp="$(mktemp)"
__cleanup() {
[ -e "$_tmp" ] && rm "$_tmp"
}
trap __cleanup INT TERM
printf '%s\n' "This might take a while." >&2
: > "$_out"
# Validate the tmp file: if the current line has the same team as the
# previous one, it's not OK.
__check() {
[ "$(wc -l < "$1")" -lt 2 ] && return 1
_prev_team=""
_ret=0
while read -r _email _team _fullname; do
case "$_team" in
"$_prev_team") _ret=1; break;;
*) _prev_team="$_team";;
esac
done < "$1"
return "$_ret"
}
# Output is only valid if it has as many lines as the input
while ! [ "$(wc -l < "$_out")" -eq "$(wc -l < "$_in")" ]; do
# Shuffle the input file until it passes the __check
# The first and last line are the same to avoid dealing with start/end
# conditions in __check
: > "$_tmp"
while ! __check "$_tmp"; do
sort -R "$_in" > "$_tmp"
head -n1 "$_tmp" >> "$_tmp"
done
# Create a usable out file from the (valid) tmp file
_prev_name=""
while read -r _email _ _fullname; do
if [ -n "$_prev_name" ]; then
echo "$_email $_prev_name"
fi
_prev_name="$_fullname"
done < "$_tmp" | grep -vxf "$_nfile" > "$_out"
# grep -vx rejects pairings we don't want to see in the output
done
__cleanup