hashvrfy - ハッシュを検証するシェルスクリプト

複数のチェックサムファイルを、まとめて確認するためのシェルスクリプトを公開します。また、アイキャッチ画像を作成するための、Python スクリプトも公開します。

(There is the English(英語) page.)

(最終更新日: 2021年2月21日)

前書き

OSのISOファイルのような、大きなファイルをWebサイトからダウンロードするとき、念の為、提供されている全てのチェックサムファイルで、ハッシュ値を確認するようにしています。しかし、チェックサムファイルを一つずつ操作するのは面倒なため、複数のチェックサムファイルを、まとめて確認するためのシェルスクリプトを書きました。そのファイルを 以下 に公開します。

また、このページの先頭に配置したアイキャッチ画像は、Python スクリプトで作成しています。そのスクリプトも、このページの 後半 で公開します。

ハッシュを検証するシェルスクリプト

ファイル

以下の4つのファイルを作成しました。これらのファイルは、クリエイティブ・コモンズ ゼロ 1.0 ユニバーサル ライセンス (CC0 1.0) の下でライセンスされています。

  • hashvrfy (ハッシュを検証するシェルスクリプト)
  • hashvrfy-en.md (Pandoc Markdown 形式の英語マニュアル)
  • hashvrfy-ja.md (Pandoc Markdown 形式の日本語マニュアル)
  • convert-md.sh (上記の Pandoc Markdown ファイルを変換するシェルスクリプト)

また、以下は、上記の Pandoc Markdown ファイルを、シェルスクリプトで変換したファイルです。

hashvrfy のソースコード

ユーザーの使い勝手を考慮して、上記の hashvrfy シェルスクリプト ファイルのソースコードを、以下に示します。

#!/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

以下の例では、一行が長くなりすぎるのを防ぐため、バックスラッシュ(\)を使って、複数行で入力しています。しかし、一行で入力しても、問題なく動作します。

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 スクリプト

このページの先頭に配置したアイキャッチ画像 (featured image) は、以下に示す、Python スクリプトを元に作成しています。

#!/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()

このスクリプトでは、以下の、2つの外部 Python パッケージを使用しています。

アイキャッチ画像の中の幾何学模様は、線 (SVG line) だけで構成されています。全ての線の間隔は、等しくなっています。線の幅は、正弦関数 (サイン関数、sin) を元に、規則的に変化させています。その計算過程で、パーリンノイズの値を加えて、線の幅を、多少、ランダムに変化させています。

この Python スクリプトを実行すると、以下に示す、SVG ファイルが作成されます。

この SVG ファイルでは、Public Sans フォントを使用しています。このフォントがインストールされていない環境で、この SVG ファイルを表示すると、上記の Python スクリプトで意図したように、フォントが表示されません。

そこで、Inkscape を使って、フォントをアウトライン化した SVG ファイルを、以下に示します。このファイルであれば、Public Sans フォントがインストールされていない環境でも、フォント部分が意図した通りに表示されます。

また、同様に、Inkscape を使って、PNG 形式に変換したファイルが、このページの先頭に配置した画像です。

コメント

このブログの人気の投稿