hashvrfy - Shell script to verify the hash
This page publishes the shell script to check multiple checksum files at once, also the Python script to create the featured image.
(There is the Japanese(日本語) page.)
(Last updated date: February 21, 2021)
Preface
When downloading a large file such as an OS ISO image file from a website, I check the hash value of all provided checksum files just in case. However, it is troublesome to manipulate the checksum files one by one, so I wrote the shell script to check multiple checksum files at once. The file is published below.
Moreover, the featured image placed at the top of this page is created with the Python script. The script, also, is published in the last half of this page.
Shell script to verify the hash
Files
I wrote the following four files. These files are licensed under the Creative Commons Zero 1.0 Universal License (CC0 1.0).
- hashvrfy (Shell script to verify the hash)
- hashvrfy-en.md (English manual in Pandoc’s Markdown format)
- hashvrfy-ja.md (Japanese manual in Pandoc’s Markdown format)
- convert-md.sh (Shell script to convert the above Pandoc’s Markdown files)
The following are files converted from the above Pandoc’s Markdown files with the shell script.
- hashvrfy-en.html (English manual in HTML format)
- hashvrfy-ja.html (Japanese manual in HTML format)
- hashvrfy.1.en (English manual in man page format)
- hashvrfy.1.ja (Japanese manual in man page format)
Source code of hashvrfy
Taking the usability into account, the source code of the above hashvrfy shell script file is shown below.
#!/bin/sh
# @(#) Shell script to verify the hash. Version 0.1
# This shell script and its associated files are licensed under
# the Creative Commons Zero 1.0 Universal License (CC0 1.0).
# https://creativecommons.org/publicdomain/zero/1.0/
# Get this command name (file name)
cmd_name=$(basename "$0")
# If the first argument is "--", skip it
if [ $# -ge 1 ] && [ "$1" = "--" ] ; then
shift
fi
# Check the number of remaining arguments
if [ $# -lt 2 ] ; then
echo "Usage: ${cmd_name} [--] target_file checksum_file [...]" 1>&2
exit 2
fi
# Get the target file name (pathname)
target_file="$1"
shift
# Check the target file
if [ ! -r "$target_file" ] ; then
echo "Cannot read \"${target_file}\" file" 1>&2
exit 2
fi
# Get the target basename
target_base=$(basename "$target_file")
# Process each checksum file name (pathname)
for checksum_file ; do
# Check the checksum file
if [ ! -r "$checksum_file" ] ; then
echo "Cannot read \"${checksum_file}\" file" 1>&2
exit 2
fi
# Get the checksum basename
checksum_base=$(basename "$checksum_file")
# Make a hint to search a type of hash algorithm
#
# It is made from the checksum basename.
# If it includes the target basename,
# the target basename will be deleted.
#
# Examples:
# Example 1 Example 2
# target_base os.iso os.iso
# checksum_base MD5SUMS os.iso.sha256
# algo_hint MD5SUMS .sha256
#
algo_hint=$(echo "$checksum_base" | sed -e "s/$target_base//")
# Search the hint for the type of hash algorithm
#
# If the type is found, a hash command will be made from it.
# If the type is not found, the hash command will be left empty string.
#
# Examples:
# Example 1 Example 2
# algo_hint MD5SUMS .sha256
# algo_type md5 sha256
# hash_cmd md5sum sha256sum
#
hash_cmd= # Set empty string
for algo_type in md5 sha1 sha224 sha256 sha384 sha512 ; do
if echo "$algo_hint" | grep -q -i "$algo_type" ; then
hash_cmd="${algo_type}sum"
break
fi
done
# Check whether the type of hash algorithm is found or not
if [ -z "$hash_cmd" ] ; then
echo "Cannot decide the hash algorithm of \"${checksum_file}\" file" 1>&2
exit 2
fi
# Check whether the hash command is found or not
if ! type "$hash_cmd" > /dev/null 2>&1 ; then
echo "Cannot find the \"${hash_cmd}\" command" 1>&2
exit 2
fi
# Get a count of the lines including the target pathname
# from the checksum file
#
# Format examples of the checksum file:
#
# Default-style
# 123...def readme.txt (for text file)
# 123...def *os-amd64.iso (for binary file)
#
# BSD-style
# SHA256 (os-amd64.iso) = 123...def
#
target_dot=$(echo "$target_file" | sed -e "s/\./[.]/g")
target_ptrn="[[:blank:]][*]?${target_dot}\$|\\(${target_dot}\\)"
target_cnt=$(grep -E -c "$target_ptrn" "$checksum_file")
# Check the count
#
# Continue processing only if the count is 1.
# Otherwise, it is an error.
#
if [ "$target_cnt" -lt 1 ] ; then
echo \
"Not find the line including \"${target_file}\" string in \"${checksum_file}\" file" \
1>&2
exit 2
elif [ "$target_cnt" -gt 1 ] ; then
echo \
"found the multiple lines including \"${target_file}\" string in \"${checksum_file}\" file" \
1>&2
exit 2
fi
# Extract the line including the target pathname
# from the checksum file
target_line=$(grep -E "$target_ptrn" "$checksum_file")
# Execute the hash command with the line including the target pathname
if echo "$target_line" | $hash_cmd --check --status ; then
echo "${checksum_file}: OK"
else
echo "${checksum_file}: Failed"
exit 1
fi
# Correspond to "for checksum_file ; do" line
done
# Exit successfully
exit 0
Examples
In the examples below, multiple lines are entered using a backslash (\) to prevent the line from becoming too long. However, even if you enter it on a single line, it works without problems.
Ubuntu
$ ls -1
MD5SUMS
SHA1SUMS
SHA256SUMS
ubuntu-18.04.3-desktop-amd64.iso
$ hashvrfy ubuntu-18.04.3-desktop-amd64.iso \
> MD5SUMS SHA1SUMS SHA256SUMS
MD5SUMS: OK
SHA1SUMS: OK
SHA256SUMS: OK
$
FreeBSD
$ ls -1
CHECKSUM.SHA256-FreeBSD-12.0-RELEASE-amd64
CHECKSUM.SHA512-FreeBSD-12.0-RELEASE-amd64
FreeBSD-12.0-RELEASE-amd64-dvd1.iso
$ hashvrfy FreeBSD-12.0-RELEASE-amd64-dvd1.iso \
> CHECKSUM.SHA256-FreeBSD-12.0-RELEASE-amd64 \
> CHECKSUM.SHA512-FreeBSD-12.0-RELEASE-amd64
CHECKSUM.SHA256-FreeBSD-12.0-RELEASE-amd64: OK
CHECKSUM.SHA512-FreeBSD-12.0-RELEASE-amd64: OK
$
Apache HTTP Server
$ ls -1
httpd-2.4.41.tar.gz
httpd-2.4.41.tar.gz.md5
httpd-2.4.41.tar.gz.sha1
httpd-2.4.41.tar.gz.sha256
$ hashvrfy httpd-2.4.41.tar.gz httpd-2.4.41.tar.gz.md5 \
> httpd-2.4.41.tar.gz.sha1 httpd-2.4.41.tar.gz.sha256
httpd-2.4.41.tar.gz.md5: OK
httpd-2.4.41.tar.gz.sha1: OK
httpd-2.4.41.tar.gz.sha256: OK
$
Python script to create the featured image
The featured image placed at the top of this page is created based on the Python script shown below.
#!/usr/bin/env python3
# Python script to make the featured-image (SVG format).
# This script is tested with Python 3.6.9, svgwrite 1.4 and noise 1.2.2
# on Ubuntu 18.04.
# Public Sans 1.008 is licensed under the SIL Open Font License 1.1
# https://public-sans.digital.gov/
import math
import svgwrite
import noise
# Attribute values of image
image_size = (image_width, image_height) = (960, 600) # 16:10
# Drawing object of svgwrite
dwg = svgwrite.Drawing(filename='feature-using-font.svg', \
size=image_size, profile='full')
# Background rectangle of image
dwg.add(dwg.rect(insert=(0, 0), size=image_size, fill='white'))
# Attribute value of lines
line_interval = 12
# Vertical lines
vlines = dwg.add(dwg.g(stroke=svgwrite.rgb(51, 51, 51)))
for line_x in range(0, (image_width +1), line_interval):
noise_input = line_x/(line_interval *7)
noise_value = noise.pnoise1(noise_input, base=9)
sin_input = (line_x/(line_interval *20)) * (2*math.pi)
sin_value = math.sin(sin_input + noise_value)
line_width = round(((4.0 * sin_value) +4.5), 4)
vlines.add(dwg.line(start=(line_x, 0), end=(line_x, image_height), \
stroke_width=line_width))
# Horizontal lines
hlines = dwg.add(dwg.g(stroke=svgwrite.rgb(50, 50, 255)))
for line_y in range(0, (image_height +1), line_interval):
noise_input = line_y/(line_interval *7)
noise_value = noise.pnoise1(noise_input, base=17)
sin_input = (line_y/(line_interval *16)) * (2*math.pi)
sin_value = math.sin(sin_input + noise_value)
line_width = round(((3.0 * sin_value) +3.5), 4)
hlines.add(dwg.line(start=(0, line_y), end=(image_width, line_y), \
stroke_width=line_width))
# Background rectangle of text
dwg.add(dwg.rect(insert=(220, 180), size=(520, 240), fill='white'))
# Text 'hashvrfy'
dwg.add(dwg.text('hashvrfy', fill='black', \
text_anchor='middle', insert=((image_width /2), 300), \
font_family='Public Sans', font_weight='bold', font_size=108))
# Text 'shell script'
dwg.add(dwg.text('shell script', fill='black', \
text_anchor='middle', insert=((image_width /2), 386), \
font_family='Public Sans', font_weight='bold', font_size=46))
# Save to the SVG file
dwg.save()
This script uses the following two external Python packages:
- svgwrite (in order to create the SVG file)
- noise (in order to use the Perlin noise)
The geometric pattern in the featured image consists only of lines (SVG lines). The intervals between all lines are equal. The widths of the lines are changed regularly based on the sine function. In the process of calculation, the value of Perlin noise is added to change the width of the line somewhat randomly.
When you run this Python script, it will create the SVG file shown below.
This SVG file uses the Public Sans font. If you view this SVG file in an environment where this font is not installed, the font will not be displayed as intended in the Python script above.
Accordingly, the SVG file whose font is outlined using Inkscape is shown below. With this file, the font part will be displayed as intended even in an environment where the Public Sans font is not installed.
Similarly, the file converted to PNG format using Inkscape is the image placed at the top of this page.
Comments
Post a Comment