sh I/O redirection examples

If the annotated code on this page is confusing, read more about I/O redirection without leaving the site or look elsewhere.

Input Redirection Example

Input redirection can be used to temporarily divert the input stream to another source and then resume reading from the original source.

#!/bin/sh

exec 9<&0
exec 0<< EOF
one
two
three
four
five
EOF

for I in 1 2; do
  read LINE
  echo "number: ${LINE}"
done

exec 8<&0
exec 0<< EOF
a
b
c
EOF
while read LINE; do
  echo "letter: ${LINE}"
done

exec 0<&8
while read LINE; do
 echo "number: ${LINE}"
done

exec 0<&9
exec 8<&- 9<&-

echo -n "term: "
read TERMINAL
echo "${TERMINAL}"


Copy the TERMINAL input stream pointer to file descriptor 9.
Redirect standard input to read from a here-document.







Read 2 lines (from the here-document)

and echo them.


Copy the pointer to the here-document stream to file descriptor 8.
Read a different here-document as stdin.




Echo all of the letters.



Copy the numbers here-doc stream pointer back to stdin.
Echo the remaining numbers.



Copy the TERMINAL input stream pointer back to stdin.
Close file descriptors 8 and 9.



Echo the input from the terminal.

Example output

echo "alpha" | redirect-input.sh
number: one
number: two
letter: a
letter: b
letter: c
number: three
number: four
number: five
term: alpha

Output Redirection Example

Mirroring input redirection, output redirection can temporarily divert the output stream to another target and then resume writing to the original target.

#!/bin/sh

NUMBERS="$(tempfile)"
exec 9>&1
exec 1> "${NUMBERS}"

for WORD in "one" "two"; do
  echo "number: ${WORD}"
done

LETTERS="$(tempfile)"
exec 8>&1
exec 1> "${LETTERS}"

for WORD in "a" "b" "c"; do
  echo "letter: ${WORD}"
done

exec 1>&8
for WORD in "three" "four" "five"; do
 echo "number: ${WORD}"
done

exec 1>&9
exec 8>&- 9>&-

echo "--NUMBERS--"
cat "${NUMBERS}"

echo "--LETTERS--"
cat "${LETTERS}"

rm "${NUMBERS}" "${LETTERS}"


Create a temporary file to use as an output destination.
Copy the TERMINAL output stream pointer to file descriptor 9.
Redirect standard output to write to the NUMBERS temporary file.

Echo one and two into the file.



Create a second output file.
Copy the NUMBERS stream pointer to file descriptor 8.
Redirect standard output to write to the LETTERS temporary file.

Echo a, b, and c into the LETTERS file.



Copy the NUMBERS output stream pointer back to stdout.
Echo three, four, and five into the NUMBERS file.



Copy back the TERMINAL output stream pointer to stdout.
Close file descriptors 8 and 9.


Show the contents of the NUMBERS file on the TERMINAL.


Show the contents of the LETTERS file on the TERMINAL.

Delete the temporary files; they have served their purpose.

Example output

--NUMBERS--
number: one
number: two
number: three
number: four
number: five
--LETTERS--
letter: a
letter: b
letter: c

File descriptors do not store stream position

#!/bin/sh
exec 0<< EOF
one
two
three
four
five
EOF
exec 9<&0

for I in 1 2; do
  read LINE
  echo "number: ${LINE}"
done

exec 0<&9 9<&-
for I in 1 2; do
  read LINE
  echo "number: ${LINE}"
done















  This does not reset the stream; exec 0<&0 9<&- is an equivalent statement.

Example output

number: one
number: two
number: three
number: four

Notes

Reading the full contents of a stream twice

Copies of file descriptors are insufficient for reading a stream twice. Instead, temporary files may be used.

#!/bin/sh
exec 0<< EOF
one
two
three
four
five
EOF

TEMPFILE="$(tempfile)"

exec 9>&1
exec 1> "${TEMPFILE}"
cat
exec 1>&9 9>&-

exec 0< "${TEMPFILE}"
for I in 1 2; do
  read LINE
  echo "number: ${LINE}"
done

exec 0< "${TEMPFILE}"
for I in 1 2; do
  read LINE
  echo "number: ${LINE}"
done

rm "${TEMPFILE}"

Redirect standard input to read from a here-document.







Create a temporary file.

Copy the TERMINAL file descriptor to fd 9.
Redirect stdout to the temporary file.
Read stdin (the here-document) to stdout (the temporary file).
Copy the TERMINAL file descriptor back to stdout, and close fd 9.

Redirect stdin to point to the temporary file.





Once again redirect stdin to the temporary file. This resets the stream.





Remove the temporary file.

Example output

number: one
number: two
number: one
number: two

Note

The redirection into the temporary file…

exec 9>&1
exec 1> "${TEMPFILE}"
cat
exec 1>&9

is verbose. Using an inline redirect is more compact and more idiomatic:

cat > "${TEMPFILE}"

That said, inline redirects can always be rewritten using exec, but exec redirects cannot always be rewritten inline.

Found a mistake?

Submit a comment or correction

Updates

2013 Jan 08 Comments link
2011 Jan 22 link to TLDP diagram about file descriptors
2010 Dec 15 posted