Remove Fixed Character Columns Safely with colrm
You have a fixed-width dump and you want columns 9 to 16 gone from every line: colrm does exactly that, and nothing more. This guide removes a numbered range from each input line, writes the result to a separate file, and checks that the source is unchanged. The examples use the installed colrm from util-linux 2.39.3, provided by the Ubuntu bsdextrautils package version 2.39.3-9ubuntu6.6.
The route
Jump straight to the step you need, or tick off Done means at the end.
- Time: about ten minutes.
- You need: a shell, a readable text file, and permission to create a new file in the destination directory.
- Quirk: the command reads standard input and writes standard output. It does not accept an input filename as a positional argument, so redirect the file or use a pipeline.
1. Check the installed command
Confirm which binary your shell will run and check its version. These commands are read-only and need no elevated privileges:
$ command -v colrm
/usr/bin/colrm
$ colrm --version
colrm from util-linux 2.39.3
$ colrm --help
Usage:
colrm [startcol [endcol]]
Filter out the specified columns from standard input.
The manual's syntax is colrm first last. Column numbering starts at 1.
- One number. Removal starts at that column and runs to the end of each line.
- Two numbers. Only that inclusive range is removed.
Checkpoint
If command -v colrm points somewhere unexpected, stop and decide whether that is the version you intend to use. Different util-linux releases can be installed side by side, and the command in your PATH is the one that will run.
2. Try it without touching a file
Start with literal input so the column positions are obvious. This removes columns 2 through 4 from both lines:
$ printf '%s\n' 'abcdef' '123456' | colrm 2 4
aef
156
The first column is a, columns 2 through 4 are bcd, and columns 5 and 6 are ef. The same rule is applied independently to each input line. The trailing newline is kept because it is a line separator, not a removable column.
To remove everything from column 4 onwards, leave out the second number:
$ printf '%s\n' 'abcdef' '123456' | colrm 4
abc
123
There is no persistent state to undo here. The process reads the pipe and exits, and neither command changes a file or a service.
3. Remove columns from a real file into a new file
Use input redirection and choose a new destination. This example removes columns 9 through 16 from /path/to/input.txt:
$ colrm 9 16 < /path/to/input.txt > /path/to/output.txt
Run it as your normal user. You need read permission on the input and write permission on the destination directory, but not root. A successful run is normally silent, so check both the exit status and the resulting file:
$ printf 'exit status: %s\n' "$?"
exit status: 0
$ test -s /path/to/output.txt && echo 'output is non-empty'
output is non-empty
$ sed -n '1,3p' /path/to/output.txt
The sed command is only a preview. Compare a few known lines with the source before using the output in a later command. If a line has fewer than 16 columns, colrm removes only the columns that exist on that line.
4. Protect an existing destination
Warning
Shell redirection with > truncates an existing destination before colrm starts. A failed conversion can therefore destroy the old output. Write to a temporary name in the same directory, inspect it, then replace the destination only when you are satisfied:
$ colrm 9 16 < /path/to/input.txt > /path/to/output.txt.new
$ test -s /path/to/output.txt.new
$ sed -n '1,3p' /path/to/output.txt.new
$ mv /path/to/output.txt.new /path/to/output.txt
The final mv changes the destination name. If validation fails, leave the original output alone and remove the temporary file explicitly:
$ rm /path/to/output.txt.new
Recovery
That rm is destructive, so check the exact path before running it. If a direct redirection already overwrote the old output, colrm has no recovery facility. Restore from your backup or version-control system.
5. Treat columns as display positions
Column positions are not necessarily bytes in a file. Tabs and multibyte text can make visual counting differ from counting bytes in a hex dump. A tab occupies a tab stop when the command processes it, and the installed util-linux build handles multibyte characters as characters rather than blindly splitting their encoded bytes.
When exact alignment matters, inspect the input with a tool that makes whitespace visible, and try a small representative sample first:
$ sed -n '1,5l' /path/to/input.txt
$ sed -n '1,5l' /path/to/output.txt
The $ at the end of each sed -n l line marks the newline, while tabs are shown visibly. Do not infer a safe column range from a text editor's proportional display.
Tip
If your data is delimited rather than fixed-width, use a delimiter-aware tool instead of deleting character columns.
6. Diagnose the common failures
A blank result can be correct. Removing from column 1 to a number beyond the longest line removes all visible characters but leaves the line endings. Test the input and the range independently:
$ test -r /path/to/input.txt && echo 'input is readable'
input is readable
$ wc -L /path/to/input.txt
42 /path/to/input.txt
- Empty output. Check whether the first column is too small or the input lines are shorter than you expected.
- Cannot open input or destination. Inspect the path and directory permissions.
- Do not add
sudoautomatically. Elevated access can hide an ownership problem and create root-owned output that your normal account cannot later replace.
The -h and --help options display help. The -V and --version options print the version. Neither processes standard input, so use them to check the installation, not as part of a transformation.
Done means
- Command checked. You confirmed the version and binary path.
- Range chosen properly. Column 1 is the first position, and you tested the input shape.
- Source untouched. Input came through standard input and the source file was not modified.
- Output verified. You wrote to a new or temporary path and checked it before any replacement.
- Width considered. You thought about whitespace, tabs and multibyte text before applying the range to a larger dataset.