Join Related Lines Side by Side with paste
Two lists that belong in one table, and paste lines them up in a single pass instead of you hand-copying columns. You'll finish with a reliable way to combine line-oriented files using GNU paste: parallel columns, serial joining, custom separators, standard input and NUL-delimited data. The examples use coreutils 9.4, the version installed on this machine.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about ten minutes. You need a shell and two or more small text files, or data piped to standard input. The workflow is unprivileged: don't reach for sudo to run paste, fix the file permissions or pick a permitted working directory instead.
1. Check the installed command
Confirm which executable your shell will run and record its version, both read-only checks:
$ command -v paste
/usr/bin/paste
$ paste --version
paste (GNU coreutils) 9.4
The copyright and warranty text after the version line can vary. The useful checkpoint is that it's GNU paste reporting the expected major version. The option names and behaviour below are from the installed manual.
2. Combine files as parallel columns
Create two short files in a scratch directory so the inputs are obvious. This only touches files in that directory:
$ mkdir -p /tmp/paste-demo
$ printf '%s\n' alice Bob > /tmp/paste-demo/names
$ printf '%s\n' admin user > /tmp/paste-demo/roles
Pass the files in the order they should appear left to right:
$ paste /tmp/paste-demo/names /tmp/paste-demo/roles
alice admin
Bob user
By default, paste writes one output line per input row, separating the corresponding fields with a tab: first line of each file forms the first output line, then the second, and so on. Handy for a quick two-column report, but the output is still plain text, not a CSV file.
Checkpoint
Inspect tabs directly rather than guessing that spaces are present:
$ paste /tmp/paste-demo/names /tmp/paste-demo/roles | od -An -c
a l i c e \t a d m i n \n
B o b \t u s e r \n
Don't use this form on two unrelated lists. It pairs rows by position, so an inserted or missing line silently shifts every later pairing.
3. Choose the separator explicitly
Reach for -d or --delimiters when a tab isn't the format you need. A one-character list is the least surprising choice:
$ paste --delimiters=: /tmp/paste-demo/names /tmp/paste-demo/roles
alice:admin
Bob:user
The argument is called LIST because it can hold more than one character. Pasting several files reuses those characters as separators, cycling through the list. That's easy to miss in a script, so stick to a single delimiter unless you've tested the exact format.
A colon-separated result isn't automatically valid CSV, TSV or any other interchange format. If a field can contain the delimiter itself, quoting and escaping are still on you. For dependable machine-to-machine data, use a format-aware tool instead.
4. Paste one file serially
Parallel mode is the default. Add -s or --serial to join one file's lines into a single output line:
$ printf '%s\n' first second > /tmp/paste-demo/values
$ paste --serial --delimiters=, /tmp/paste-demo/values
first,second
Serial mode processes each file separately. Given more than one file, it emits one joined line per file rather than one wide row across all of them, a distinction that matters when you're turning lists into arguments or compact records.
There's no persistent state to undo, but redirecting the result opens the destination before paste runs, so a failed command can leave a truncated file behind. Write to a new name and swap it in only after checking it:
$ paste --serial --delimiters=, /tmp/paste-demo/values > /tmp/paste-demo/values.new
$ test -s /tmp/paste-demo/values.new && mv /tmp/paste-demo/values.new /tmp/paste-demo/values.joined
$ cat /tmp/paste-demo/values.joined
first,second
If the check fails, leave the original alone and inspect the error. Don't bolt a blind rm cleanup step onto a batch command when the output path could be wrong.
5. Read standard input
A file argument of -, or no file arguments at all, makes paste read standard input, so you can join another command's output without an intermediate file:
$ printf '%s\n' red blue | paste --serial --delimiters= -
redblue
Here the delimiter list is empty, so the two lines join with nothing between them. For a comma, write -d, and keep the lone - as the input file:
$ printf '%s\n' red blue | paste -s -d, -
red,blue
Tip
Keep the hyphen visible. Write paste -s -d, alone and the command correctly waits for terminal input rather than doing nothing; press Ctrl-D to end it, or supply a pipe or file.
6. Preserve NUL-delimited records when needed
Newline is the normal line delimiter, but filenames and other records can contain newlines themselves. For a pipeline already using NUL-terminated records, add -z or --zero-terminated:
$ printf 'one\0two\0' > /tmp/paste-demo/nul-records
$ paste -z -d: /tmp/paste-demo/nul-records | od -An -t x1
6f 6e 65 00 74 77 6f 00
The hex output shows the text followed by byte 00 rather than newline byte 0a. This one-file example doesn't show the delimiter since there's only one input stream; with multiple NUL-delimited files, -d sets the separator between corresponding records while -z keeps the output terminator as NUL.
Don't mix newline-oriented and NUL-oriented stages casually. A later command needs to understand the same record convention, or it may treat the whole stream as one record or split data inside a field.
Common traps
- Wrong pairing: compare input files line by line and check for blank lines.
pastenever matches on a key or skips a header for you; remove or handle headers before the join and check each list's row count. - Can't open a file: check the path and read permission without escalating privileges first:
$ test -r /tmp/paste-demo/names && echo readable readable $ ls -l /tmp/paste-demo/names - Wrong delimiter: the default is a tab, not a comma, and
-dtakes a list of characters. For a stable interchange format, verify bytes withodor a format-aware parser rather than trusting terminal alignment. - Hidden pipeline failure: check the exit status after a pipeline when it matters. Without a shell setting such as
pipefail, a later successful command can hide an earlier failure.pasteitself never modifies its input files, services or system configuration.
Done means
- Confirmed the installed GNU coreutils version and executable.
- Know that ordinary mode combines corresponding rows with tab separators.
- Used
-donly with an explicit, tested separator. - Used
-sfor one-file-at-a-time joining, with standard input used deliberately. - Preserved NUL-delimited records with
-zwhen newline was unsafe. - Wrote redirected output to a new path before replacing a file that mattered.