Create and Verify SHA384 Checksums with sha384sum

sha384sum turns a file into a 96-character fingerprint you can check again months later. This guide covers hashing a file, saving the result as a manifest, and telling a genuine change apart from a missing or misnamed file. The examples use GNU coreutils 9.4, the version installed on this machine.

Allow about ten minutes. You need a shell and a readable input file. The normal workflow is unprivileged: do not use sudo unless the file or directory genuinely requires elevated access. This guide reads files and creates manifests; it does not alter the input.

1. Check the installed command

Confirm the binary and package before relying on a feature in a script. These are ordinary, read-only commands:

$ command -v sha384sum
/usr/bin/sha384sum
$ sha384sum --version | head -1
sha384sum (GNU coreutils) 9.4
$ dpkg-query -W -f='${Package} ${Version}\n' coreutils
coreutils 9.4-3ubuntu6.3

The installed manpage describes sha384sum as a tool to print or check 384-bit SHA384 checksums. With no file, or with -, it reads standard input. With a file argument it reads that file and prints one checksum line.

Checkpoint: If command -v finds a different copy, or the version differs, run sha384sum --help and check the local manpage before copying the examples into a long-lived script.

2. Hash a file without changing it

Make a small test file if you want a reproducible exercise. The redirection creates or replaces the destination, so choose a new path or confirm that replacing it is intended:

$ printf '%s\n' 'release candidate' > message.txt
$ sha384sum message.txt
bf7bb85ea84634605cfb0e6b304235ac1e62aec5500112e1393759da6061171c51b421e43637d7e6e7f35a09768f5611  message.txt

The first field is the 96-hex-digit digest. The final field is the file name. The two spaces between them are part of the normal output format: one separates the digest from a mode marker, and the next separates that marker from the name. GNU systems treat text and binary mode identically, although the output still records the mode marker.

For a pipeline, omit the file name or pass -:

$ printf '%s\n' 'release candidate' | sha384sum
bf7bb85ea84634605cfb0e6b304235ac1e62aec5500112e1393759da6061171c51b421e43637d7e6e7f35a09768f5611  -

This hashes exactly the bytes sent through the pipe. A final newline changes the digest, so make the producer of the bytes explicit when reproducing a value.

3. Save a manifest beside the file

A manifest is useful when the file will be copied, downloaded or checked by another person later. Save it with a deliberate name:

$ sha384sum message.txt > message.sha384
$ sed -n '1p' message.sha384
bf7bb85ea84634605cfb0e6b304235ac1e62aec5500112e1393759da6061171c51b421e43637d7e6e7f35a09768f5611  message.txt

Do not create the manifest by typing the digest. Let the command record the exact path and spacing that its checker expects. Keep the manifest separate from a file that an untrusted download could overwrite.

Safety boundary: > truncates an existing destination before sha384sum starts. If message.sha384 already contains useful evidence, use a new name such as message.sha384.new, inspect it, and then replace the old manifest deliberately. If a failed command leaves a partial new file, discard that new file and retain the original manifest.

4. Verify the manifest

Use --check to read the manifest and hash the named file again:

$ sha384sum --check message.sha384
message.txt: OK

Exit status matters more than the wording. Check it directly in a script, or use the shell's conditional form:

$ if sha384sum --check message.sha384; then
>     printf '%s\n' 'checksum verified'
> else
>     printf '%s\n' 'checksum failed' >&2
> fi
message.txt: OK
checksum verified

A successful check means the bytes currently at the recorded path match the recorded digest. It does not prove that the manifest came from a trusted source, that the file is safe to open, or that the file has the meaning you expect.

For automation that needs no normal output, combine --status and --check:

$ sha384sum --status --check message.sha384
$ printf 'exit status: %s\n' "$?"
exit status: 0

--status is meaningful only while verifying checksums. Using it while creating a digest is an error.

5. Diagnose a failed check

Change one byte in the input, then check again. This is a test of the failure path, not a step to perform on evidence you need to preserve:

$ printf '%s\n' 'changed release candidate' > message.txt
$ sha384sum --check message.sha384
message.txt: FAILED
sha384sum: WARNING: 1 computed checksum did NOT match
$ printf 'exit status: %s\n' "$?"
exit status: 1

Restore the original file from a trusted copy, or generate a new manifest only after confirming that the change was authorised. There is no repair operation hidden in sha384sum: verification reports whether the bytes match; it does not recover old bytes.

A missing file is a different failure:

$ sha384sum --check message.sha384
sha384sum: message.txt: No such file or directory
sha384sum: WARNING: 1 listed file could not be read

Check the path, current directory and read permission before using sudo. If a manifest covers several files and missing files are expected, --ignore-missing suppresses a failure for absent entries. Use it only when absence is genuinely acceptable, because it can hide an incomplete download.

6. Choose output formats deliberately

The default format is designed to be read back by --check. --tag produces a BSD-style line instead:

$ sha384sum --tag message.txt
SHA384 (message.txt) = bf7bb85ea84634605cfb0e6b304235ac1e62aec5500112e1393759da6061171c51b421e43637d7e6e7f35a09768f5611

GNU coreutils 9.4 can check that tagged output too, but do not mix formats manually in one manifest. Use --quiet to suppress the OK line while retaining the exit status, or --strict to make improperly formatted checksum lines an error. --warn reports malformed lines while checking.

--zero ends each generated line with a NUL byte instead of a newline and disables file-name escaping. That is useful for a program that explicitly consumes NUL-delimited records, not for ordinary terminal output. Do not pipe it into a line-oriented tool and assume the records are unchanged.

Done means