Extract line and byte ranges from a file — a Python-slice cheatsheet

TL;DR: slice selects line or byte ranges from a file or stdin using Python's start:end:step notation. The line and byte ranges you would reach for head, tail, sed, awk, or dd to produce all collapse into one syntax. Indices are 0-based and end-exclusive, exactly like Python.

Install with cargo install slice-command, brew install chantsune/tap/slice, or grab a prebuilt binary. Source and docs: github.com/ChanTsune/slice · crates.io/crates/slice-command.

Not sure what a range selects? Run slice --explain <range> to print the 0-based indices, 1-based positions, and element count without reading any input.

Print a range of lines (head, tail, sed, awk)

Taskhead · tail / sed · awk / ddslice
First 5 lineshead -n 5slice :5
Last 5 linestail -n 5slice -5:
All but the last 5 lineshead -n -5slice :-5
All but the last linesed '$d' / head -n -1slice :-1
POSIX spelling is `sed '$d'`; `head -n -1` is GNU-only.
All but the first linesed '1d' / tail -n +2slice 1:
From line N to the endtail -n +3slice 2:
`tail -n +3` is 1-based, so line 3 onward maps to `2:`.
Lines 2 through 5sed -n '2,5p' / awk 'NR>=2&&NR<=5'slice 1:5
Line 7 onlysed -n '7p' / awk 'NR==7'slice 6:7
From line 10 to the endsed -n '10,$p'slice 9:

Byte ranges from a file (head -c, tail -c, dd without dd)

Taskhead · tail / sed · awk / ddslice
First 5 byteshead -c 5slice -b :5
Last 5 bytestail -c 5slice -b -5:
All but the last 5 byteshead -c -5slice -b :-5
From byte 6 to the endtail -c +6slice -b 5:
`tail -c +6` is 1-based, so byte 6 onward maps to `5:`.
Bytes 5 through 14dd bs=1 skip=5 count=10slice -b 5:15
First 4 bytesdd bs=1 count=4slice -b 0:4
From byte 10 to the enddd bs=1 skip=10slice -b 10:
A block range (bs=4 skip=1 count=2)dd bs=4 skip=1 count=2slice -b 4:12

Every Nth line (sed/awk only — slice does it too)

Taskhead · tail / sed · awk / ddslice
Odd lines (1, 3, 5, ...)sed -n '1~2p' / awk 'NR%2==1'slice ::2
`sed -n '1~2p'` is GNU-only; the awk form is portable, which is what `check` runs.
Even lines (2, 4, 6, ...)sed -n '2~2p' / awk 'NR%2==0'slice 1::2
`sed -n '2~2p'` is GNU-only; the awk form is portable, which is what `check` runs.

NUL-delimited records and other special cases

Taskhead · tail / sed · awk / ddslice
Last NUL-delimited record (find -print0 style)slice -z -1:
No single coreutils command extracts the last NUL record; slice does it with `-z`.

When NOT to use slice

slice selects positional ranges and copies them through unchanged. These jobs need a different tool:

JobUse this insteadWhy not slice
Columns within a linecut -c 1-3 / cut -b 1-3`cut` works per line; `slice -b` indexes the whole stream, not each line.
Field extractioncut -f 2,4 -d,slice has no notion of fields or delimited columns.
Substitutionsed 's/foo/bar/g'slice only selects ranges; it never transforms content.
Aggregationawk '{sum+=$1} END {print sum}'slice does not compute; it copies the selected bytes through.
Several disjoint rangessed -n '1,3p;8,10p'one slice invocation selects one range.
Reverse or sorttac / sort / tail -rslice preserves input order; it cannot reorder.
Pattern addressessed -n '/START/,/END/p'slice addresses by numeric position only, never by regex.

Mapping sed/awk line numbers to slice

sed and awk count from line 1 and include both endpoints; slice counts from 0 and excludes the end. So sed -n 'a,bp' becomes slice (a-1):b. The tail -n +N and tail -c +N forms are also 1-based, so "from line N onward" is slice (N-1):.

Caveats