Home / Alt manpages / xxd(1)

  • xxd(1)
  • User command
  • linux

Inspect, Convert and Safely Round-Trip Binary Data with xxd

You will use xxd to inspect a binary file, make a plain hexadecimal representation, recover the original bytes, and generate a C array when that is useful. You will also see why reverse conversion deserves a backup and a byte-for-byte check.

The examples match xxd 2:9.1.0016-1ubuntu7.20, supplied by the Ubuntu xxd package on this machine. Allow about fifteen minutes. You need a shell and a file that you are allowed to read. None of the normal inspection commands needs elevated privileges.

Checkpoint

This guide works on a disposable sample first. Replace /path/to/input.bin with a real path only after you understand which command writes a file.

1. Confirm the installed command

Check both the executable and package version. This catches the common distraction of reading documentation for one installation while running another:

$ command -v xxd
/usr/bin/xxd
$ dpkg-query -W -f='${Package} ${Version}\n' xxd
xxd 2:9.1.0016-1ubuntu7.20
$ xxd -version
xxd 2023-10-25 by Juergen Weigert et al.

The local manual describes xxd 1.7, but the installed executable reports the newer Vim-era build shown above. Use xxd -h and the installed binary as the final authority when a detail matters. Option names can be abbreviated, but full names make scripts easier to review.

2. Read a normal hex dump

Run xxd with an input file and no output file. It writes the dump to standard output, leaving the source untouched:

$ printf 'hello\0\0world\n' > /tmp/xxd-sample
$ xxd /tmp/xxd-sample
00000000: 6865 6c6c 6f00 0077 6f72 6c64 0a         hello..world.

Each line has a hexadecimal byte offset, hexadecimal bytes grouped for readability, and a character column. Non-printable bytes appear as dots in the character column. That right-hand text is a viewing aid, not another copy of the data.

Use -c to control bytes per line, and -l to stop after a bounded number of bytes:

$ xxd -c 8 -l 8 /tmp/xxd-sample
00000000: 6865 6c6c 6f00 0077  hello..w

Checkpoint

Use cmp, not visual inspection, when you need to know whether two binary files are equal:

$ cmp --silent /tmp/xxd-sample /path/to/other.bin
$ printf 'cmp status: %s\n' "$?"
cmp status: 1

Status 0 means the files match. Status 1 means they differ, and another non-zero status can indicate an error. The example deliberately compares different files, so its status is 1.

3. Make a compact plain-hex representation

For interchange or a review that does not need offsets and character columns, use -ps, also called plain or PostScript style. Save it explicitly if you need a file:

$ xxd -ps -c 8 /tmp/xxd-sample
68656c6c6f000077
6f726c640a
$ xxd -ps /tmp/xxd-sample > /tmp/xxd-sample.hex
$ wc -c /tmp/xxd-sample.hex
23 /tmp/xxd-sample.hex

Plain output contains hexadecimal characters and line breaks. The -c 8 setting affects presentation only. With plain input, xxd's reverse mode accepts whitespace and line breaks, so do not mistake line length for a record boundary.

Upper-case digits are available with -u. That changes how the hex is displayed, not the bytes it represents.

4. Recover bytes and verify the round trip

Warning

xxd -r writes binary output. Never redirect it over your only copy of an input file. Write a new path first, then compare the result:

$ xxd -r -ps /tmp/xxd-sample.hex > /tmp/xxd-sample.recovered
$ cmp --silent /tmp/xxd-sample /tmp/xxd-sample.recovered
$ printf 'round-trip status: %s\n' "$?"
round-trip status: 0

The -r option reverses a dump and -ps tells it to read plain hexadecimal. A status of 0 confirms that the recovered file is byte-for-byte identical. Keep the original until this check succeeds and you have inspected the destination.

Do not treat a successful exit status as proof that arbitrary input was valid. The manual warns that xxd's reverse parser silently skips garbage and does not report parse errors. Validate the source format separately when the hex comes from a person, a ticket or an untrusted system.

5. Generate a C include file

If a small binary asset must be compiled into a C program, -i emits a complete static array. Give it a deliberate name with -n rather than relying on the input filename:

$ xxd -i -n sample_bytes /tmp/xxd-sample
unsigned char sample_bytes[] = {
  0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00, 0x00, 0x77, 0x6f, 0x72, 0x6c, 0x64,
  0x0a
};
unsigned int sample_bytes_len = 13;

Redirect this output to a new .h file only after checking it. -C capitalises the generated variable names. A subtle default matters here: xxd -i file can derive the array name from the filename, while xxd -i < file reads standard input and cannot use that filename.

6. Inspect a region without extracting it

-s seeks to a byte offset and -l limits the view. Numeric parameters may be decimal, hexadecimal or octal; the 0x prefix makes the unit clear:

$ xxd -s 0x8 -l 5 /tmp/xxd-sample
00000008: 6f72 6c64 0a                             orld.

A negative seek counts from the end of a regular file. This is useful for examining a trailer, but it is easy to confuse with a negative shell option, so keep the argument attached to -s as shown:

$ xxd -s -0x5 -l 5 /tmp/xxd-sample
00000008: 6f72 6c64 0a                             orld.

Relative seeks from standard input are a specialised case. The manual distinguishes -s +offset from -s offset because the current input position can already have changed. Prefer a regular input path for repeatable scripts.

7. Understand patching before using it

xxd can patch a file with a normal dump, but this is a destructive operation. Make a preserved copy, use a temporary destination when practical, and verify the changed bytes before replacing anything. Do not test a patch against a system binary or live service file.

$ cp --preserve=all /path/to/input.bin /path/to/input.bin.bak
$ printf '00000008: 4142\n' | xxd -r - /path/to/input.bin
$ xxd -s 0x8 -l 2 /path/to/input.bin
00000008: 4142                                     AB

In this form, xxd reads the patch from standard input. Reverse conversion does not truncate a named output file, which is useful for in-place patches but also means old trailing bytes can remain. The parser uses the hexadecimal columns and ignores the printable character column. It also skips garbage silently, so keep the patch tiny and inspect the exact result.

If the patch is wrong, restore the backup before doing anything else:

$ cp --preserve=all /path/to/input.bin.bak /path/to/input.bin
$ cmp --silent /path/to/input.bin.bak /path/to/input.bin
$ printf 'restored status: %s\n' "$?"
restored status: 0

Remove the backup only after the replacement has been tested and another recovery copy exists. That removal is irreversible.

Done means

  • You confirmed the executable and package version actually in use.
  • You can distinguish a formatted dump from plain hexadecimal output.
  • You reverse a plain dump with -r -ps and verify it with cmp.
  • You know that reverse mode silently skips malformed input.
  • You write new output paths before replacing valuable files.
  • Any patch has a preserved backup, a byte-level check and a restoration command.