Join Related Lines Safely with paste
You will finish with a reliable way to combine line-oriented files using GNU paste, including 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 that can be piped to standard input. The normal workflow is unprivileged. Do not use sudo to run paste; if you cannot read an input file or write the destination, fix the file permissions or choose a permitted working directory deliberately.
1. Check the installed command
Confirm which executable your shell will run and record its version. These are read-only checks:
$ command -v paste
/usr/bin/paste
$ paste --version
paste (GNU coreutils) 9.4
The exact copyright and warranty text after the version can vary. The useful checkpoint is that the command is GNU paste and reports the expected major version. Option names and the behaviour described 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 changes only 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 that should appear from left to right:
$ paste /tmp/paste-demo/names /tmp/paste-demo/roles
alice admin
Bob user
By default, paste writes one output line for each input row and separates the corresponding fields with a tab. The first line from each file forms the first output line, then the second line from each file forms the second. This is useful for making a quick two-column report, but the output is still plain text, not a CSV file.
Checkpoint: inspect tabs 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
Do not use this form when the two files are unrelated lists. It pairs rows by position, so an inserted or missing line shifts every later pairing without warning.
3. Choose the separator explicitly
Use -d or --delimiters when a tab is not 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 contain more than one character. When several input files are being pasted, the characters from the list are reused as separators. That cycling behaviour is easy to miss in a script, so use a single delimiter unless you have tested the exact format.
A colon-separated result is not automatically valid CSV, TSV or any other interchange format. If a field can itself contain the delimiter, quoting and escaping are still your responsibility. For robust machine-to-machine data, use a format-aware tool or generate the format with an application that understands its rules.
4. Paste one file serially
Parallel mode is the default. Add -s or --serial when you want one input file's lines joined 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. With more than one file, it emits one joined line for the first file, then one for the second, rather than making one wide row from all files. That distinction matters when turning lists into arguments or compact records.
There is no persistent state to undo. If you redirect the result into a file, however, the shell opens the destination before paste runs. A failed command can therefore leave a truncated destination. Write to a new name and replace the old file 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 file alone and inspect the error. Do not add a blind rm cleanup step to a batch command when the output path might be wrong.
5. Read standard input
A file argument of -, or no file arguments at all, makes paste read standard input. This lets you join the output of another command without creating an intermediate file:
$ printf '%s\n' red blue | paste --serial --delimiters= -
redblue
Here the delimiter list is empty, so the two lines are joined with no character. If you want a comma, write -d, and keep the lone - as the input file:
$ printf '%s\n' red blue | paste -s -d, -
red,blue
Keep the hyphen visible. A common distraction error is to write paste -s -d, and then wonder why the command waits: without another input source, it is correctly waiting for terminal input. Press Ctrl-D to end interactive input, or provide 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. For a pipeline that already uses 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 hexadecimal output shows the text followed by byte 00 rather than newline byte 0a. In this one-file example, the delimiter is not visible because there is only one input stream. With multiple NUL-delimited files, -d controls the separator between corresponding records, while -z keeps the output record terminator as NUL.
Do not mix newline-oriented and NUL-oriented stages casually. A later command must understand the same record convention, or it may treat the entire stream as one record or split data inside a field.
7. Diagnose the likely mistakes
If the output pairs the wrong values, compare the input files line by line and check for blank lines. paste does not match on a key or skip a header automatically. Remove or handle headers before the join, and make sure each list has the intended row count.
If the command says it cannot open a file, check the path and read permission without escalating privileges:
$ test -r /tmp/paste-demo/names && echo readable
readable
$ ls -l /tmp/paste-demo/names
If the delimiter looks wrong, remember that the default is a tab, not a comma, and that -d receives a list of delimiter characters. If a script needs a stable interchange format, verify the bytes with od or a format-aware parser rather than trusting terminal alignment.
Finally, check the exit status after a pipeline when failure matters. Without a shell setting such as pipefail, a later successful command can hide an earlier failure. The paste process itself does not modify its input files, services or system configuration.
Done means
- You confirmed the installed GNU coreutils version and executable.
- You know that ordinary mode combines corresponding rows with tab separators.
- You used
-donly when the required separator was explicit and tested. - You used
-sfor one-file-at-a-time joining and kept standard input deliberate. - You preserved NUL-delimited records with
-zwhen newline was unsafe. - You wrote redirected output to a new path before replacing a useful file.