-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhash.bash
executable file
·323 lines (255 loc) · 11.1 KB
/
hash.bash
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
#!/usr/bin/env bash
: <<'!COMMENT'
GGCOM - Bash - Utils - Hash v201507111357
Louis T. Getterman IV (@LTGIV)
www.GotGetLLC.com | www.opensour.cc/ggcom/hashbash
Example usage:
$] hash.bash sha1 ~/target/path/file
$] echo -n "Hello World" | ./hash.bash sha1
To-do:
* Fix STDIN bug
* Relating to STDIN bug, use different temporary location if mktemp's location doesn't have enough space
* OSX adding newline characters for hashing directories; temporary workaround is printf cat.
Thanks:
python - Linux: compute a single hash for a given folder & contents? - Stack Overflow
http://stackoverflow.com/questions/545387/linux-compute-a-single-hash-for-a-given-folder-contents
bash - sha1sum for a directory of directories - Super User
http://superuser.com/questions/458326/sha1sum-for-a-directory-of-directories
osx - execute a command in all subdirectories bash - Super User
http://superuser.com/questions/608286/execute-a-command-in-all-subdirectories-bash
file - Bash: Recursively adding subdirectories to the path - Stack Overflow
https://stackoverflow.com/questions/657108/bash-recursively-adding-subdirectories-to-the-path
Capturing output of find . -print0 into a bash array - Stack Overflow
http://stackoverflow.com/questions/1116992/capturing-output-of-find-print0-into-a-bash-array
linux - How to do for each file using find in shell/bash? - Stack Overflow
http://stackoverflow.com/questions/15065010/how-to-do-for-each-file-using-find-in-shell-bash
osx - Why does UTF-8 text sort in different order between OS X and Linux? - Stack Overflow
http://stackoverflow.com/questions/27395317/why-does-utf-8-text-sort-in-different-order-between-os-x-and-linux
shell - Sorting the output of "find"? - Unix & Linux Stack Exchange
http://unix.stackexchange.com/questions/34325/sorting-the-output-of-find
python - case-insensitive list sorting, without lowercasing the result? - Stack Overflow
http://stackoverflow.com/questions/10269701/case-insensitive-list-sorting-without-lowercasing-the-result
Recursive sub folder search and return files in a list python - Stack Overflow
http://stackoverflow.com/questions/18394147/recursive-sub-folder-search-and-return-files-in-a-list-python
!COMMENT
################################################################################
SOURCE="${BASH_SOURCE[0]}" # Dave Dopson, Thank You! - http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
SCRIPTPATH="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$SCRIPTPATH/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
################################################################################
SCRIPTPATH="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SCRIPTNAME=`basename "$SOURCE"`
LIBPATH="$( cd "$(dirname "${SCRIPTPATH}/../../")" ; pwd -P )/ggcom-bash-library"
################################################################################
source "${LIBPATH}/varsBash.bash"
source "${LIBPATH}/string.bash"
source "${LIBPATH}/version.bash"
################################################################################
source "${LIBPATH}/prompt.bash"
source "${LIBPATH}/crypto.bash"
source "${LIBPATH}/colors.bash"
################################################################################
#------------------------------ Options and Variables
# Array of arguments that we will pass and parse
ARGSN=$#
ARGSA=()
for var in "$@"; do ARGSA+=("$var"); done;
# Verbosity
[ ! -z "`parseArgs "${ARGSA[@]}" "-v"`" ] && valVerbose="`parseArgs "${ARGSA[@]}" "-v"`" || valVerbose="`parseArgs "${ARGSA[@]}" "--verbose"`"
# Algorithm
[ ! -z "`parseArgs "${ARGSA[@]}" "-a"`" ] && valAlg="`parseArgs "${ARGSA[@]}" "-a"`" || valAlg="`parseArgs "${ARGSA[@]}" "--algorithm"`"
# Target
[ ! -z "`parseArgs "${ARGSA[@]}" "-t"`" ] && valTarget="`parseArgs "${ARGSA[@]}" "-t"`" || valTarget="`parseArgs "${ARGSA[@]}" "--target"`"
# Version
if [ "`parseArgs "${ARGSA[@]}" "--version"`" = True ]; then valVersion=True; fi
# Help Menu
[ ! -z "`parseArgs "${ARGSA[@]}" "-h"`" ] && valHelp="`parseArgs "${ARGSA[@]}" "-h"`" || valHelp="`parseArgs "${ARGSA[@]}" "--help"`"
if [ "`parseArgs "${ARGSA[@]}" "-?"`" = True ]; then valHelp=True; fi
# Show header?
showHeader=False
if [ "$valVerbose" = True ] || [ "$valHelp" = True ]; then
showHeader=True
fi
#----- Default Values
TMPARGCOUNT=0
for var in "${ARGSA[@]}"; do
if [ "${var:0:1}" = '-' ]; then
continue
fi
((TMPARGCOUNT++))
case "$TMPARGCOUNT" in
1)
valAlg="$var"
;;
2)
valTarget="$var"
;;
esac
done;
unset TMPARGCOUNT var
#-----/Default Values
#----- NOTICE: VERSION
if [ "$valVersion" = True ]; then
echo "`getVersion $0 number`"
exit 0
fi
if [ "$showHeader" = True ]; then
echo "`getVersion $0 header`"
echo;
fi
#-----/NOTICE: VERSION
#----- VERBOSITY: ARGUMENTS
if [ "$valVerbose" = True ]; then
for var in "${ARGSA[@]}"; do echo -e "${ggcLightPurple}Supplied argument:${ggcNC} ${ggcLightBlue}${var}${ggcNC}"; done;
echo;
fi
unset var
#-----/VERBOSITY: ARGUMENTS
#----- NOTICE: HELP INFO
if [ "$valHelp" = True ]; then
echo -e "${ggcLightPurple}Hash candidate :${ggcNC} ${ggcLightBlue}`cryptoHashCandidate "$valAlg"`${ggcNC}"
echo -en "${ggcLightPurple}Post hash arguments :${ggcNC} ${ggcLightBlue}"
echo -n `cryptoHashCandidatePost "$valAlg"`
echo -e "${ggcNC}"
echo;
read -r -d '' HELPMENU <<EOF
Usage: $SCRIPTNAME [OPTIONS]... ALGORITHM TARGET
or $SCRIPTNAME [OPTIONS]
Options
-a, --algorithm hashing algorithm (Common are 'CK' (CRC-32), 'MD5', or 'SHA1')
-t, --target target to hash (file or directory)
-v, --verbose increase verbosity
--version print version number
(-h) --help show this help (-h is --help only if used alone)
EOF
echo "$HELPMENU"
exit 0
fi
#-----/NOTICE: HELP INFO
#----- CHECK FOR HASH
if [ -z "$valAlg" ]; then
echo -e "${ggcLightRed}No hash algorithm specified. Common uses are 'CK' (CRC-32), 'MD5', or 'SHA1'.${ggcNC}" >&2
exit 1
fi
TMPHASHCHECK="`cryptoHashCalc "$valAlg" test`"
if [ ! -z "$TMPHASHCHECK" ]; then
echo -e "${ggcLightRed}It appears that your hash selection ('$valAlg') is invalid. Common uses are 'CK' (CRC-32), 'MD5', or 'SHA1'.${ggcNC}" >&2
echo -e "The specific error message was: ${ggcLightPurple}$TMPHASHCHECK${ggcNC}" >&2
exit 1
fi
unset TMPHASHCHECK
#-----/CHECK FOR HASH
#------------------------------/Options and Variables
#----- HASH OUTPUT
# Checks STDIN vs FILE ARGUMENT
if [ -z "$valTarget" ]; then # STRING
# Failed Attempt #1
# read -r -t 1 STDINP1
# if [ ! -z "$STDINP1" ]; then
# stringInput="$STDINP1"
# stringInput+=`cat`
# else
# echo "No hash specified for file '$valAlg'. Common uses are MD5 or SHA1." >&2
# exit 1
# fi
# Failed Attempt #2
# stringInput=''
# while :; do
# read -r -t 1 STDINP
# stringInput+="$STDINP"
# if [ -z "$STDINP" ]; then
# break
# fi
# done
# echo "`cryptoHashCalc "$valAlg" string "$strInp"`"
# This is absolutely not the way that I want to do this, but others are experiencing similar problems with newlines in read, which provides timeouts since cat doesn't:
# http://www.dslreports.com/forum/r28406360-Reading-from-a-pipe-in-a-bash-script-with-timeout
TMPSTRINP=`mktemp 2>/dev/null || mktemp -t 'hash'`
cat > $TMPSTRINP
echo "`cryptoHashCalc "$valAlg" file "$TMPSTRINP"`"
rm -rf TMPSTRINP
else # FILE OR DIRECTORY
# TARGET DOES NOT EXIST
if [ ! -f "$valTarget" ] && [ ! -d "$valTarget" ]; then
echo -e "${ggcLightRed}Target does not exist.${ggcNC}" >&2
exit 1
# FILE
elif [ -f "$valTarget" ]; then
echo "`cryptoHashCalc "$valAlg" file "$valTarget"`"
exit 0
# DIRECTORY - this method is entirely up for debate
# the tar method would yield a completely different hash value, possibly on separate machines of same content
# I tried to stick with a method that's as reproducible as possible with minimal cache size (due to the aforementioned STDIN bug), regardless of system:
# List individual hashes of all files (except operating system files) in Key:Value CSV format
# Hash the cumulative output
elif [ -d "$valTarget" ]; then
ORIGPWD="$PWD"
cd "$valTarget"
# Create array of files to work through, and more importantly, show when the verbose flag is set
if [ "$valVerbose" = True ]; then echo -n -e "${ggcLightPurple}Scanning for entries:${ggcNC} "; fi
# Hideous sorting technique has to be done due to the way that -nix systems (e.g. OSX and Linux) differ from each other on UTF-8 and sorting
# Check git history to see the various attempts I was making with combinations of `find` piped to `sort`
unset fileItems i f
while IFS= read -r -d $'\0' f; do
# Skip: invalid file entry
if [ -z "${f}" ] || [ ! -f "${f}" ]; then continue; fi
fileItems[i++]="$f"
done < <( python -c "import os, sys; fdl=[os.path.join(dp, f) for dp, dn, filenames in os.walk('.') for f in filenames]; sys.stdout.write('\0'.join( sorted( [ x for x in fdl if os.path.basename(x) not in [ '.DS_Store', 'Icon\r' ] ], key=lambda s: s.lower() ) )+'\0' )" )
unset i f
# Total size of array
fileItemSize=${#fileItems[@]}
# Display count
if [ "$valVerbose" = True ]; then echo -e "${ggcLightBlue}${fileItemSize}${ggcNC} "; echo; fi
# Directory contains 0 (nested) files.
if [ "$fileItemSize" -eq 0 ]; then
echo -e "${ggcLightRed}There are no (nested) files in directory '${valTarget}' to check. Exiting.${ggcNC}" >&2
exit 1
fi
TMPSTRINP=`mktemp 2>/dev/null || mktemp -t 'hash'`
if [ "$valVerbose" = True ]; then
echo -e "${ggcLightPurple}Cumulative hash cache:${ggcNC} ${ggcLightBlue}${TMPSTRINP}${ggcNC}"
echo;
fi
if [ "$valVerbose" = True ]; then echo -e "${ggcLightPurple}Processing queue:${ggcNC} "; fi
countFile=1
for fileItem in "${fileItems[@]}"; do
if [ "$valVerbose" = True ]; then printf "($countFile/$fileItemSize = $(bc <<< "scale=1; ($countFile*100/$fileItemSize)") %%) ${fileItem}:"; fi
hashItem="$( "${SCRIPTPATH}/${SCRIPTNAME}" "$valAlg" "$fileItem" )"
if [ $? -ne 0 ]; then
(( fileItemSize-- ))
continue
fi
if [ "$valVerbose" = True ]; then printf "${hashItem}\n"; fi
printf "${fileItem}:${hashItem}" >> "$TMPSTRINP"
# Comma separator if not last entry
if [ $countFile -lt $fileItemSize ]; then printf "," >> "$TMPSTRINP"; fi
(( countFile++ ))
done; # END: for fileItem in "${fileItems[@]}"
unset fileItems fileItemSize countFile fileItem
# Dealing with OSX-based issue in erroneously adding newline, thus messing up the hash
# cache="$( cat "$TMPSTRINP" | tr -d '\r' | tr -d '\n' | sed 's/,$//' )" # Can be used to clean-up the cache file
hashCache="$( "${SCRIPTPATH}/${SCRIPTNAME}" "$valAlg" "$TMPSTRINP" )"
if [ "$valVerbose" = True ]; then
echo;
echo -e "${ggcLightPurple}Directory Cache:${ggcNC}"
cat "$TMPSTRINP"
echo;
echo;
echo -e "${ggcLightPurple}Directory '${valTarget}' Cache Hash:${ggcNC}"
echo -e "${ggcLightBlue}${hashCache}${ggcNC}"
else
echo "${hashCache}"
fi
cd "$ORIGPWD"
rm -rf "$TMPSTRINP"
exit 0
# UNKNOWN ERROR
else
echo -e "${ggcLightRed}An unknown error has occurred.${ggcNC}" >&2
exit 1
fi
fi
#-----/HASH OUTPUT