It doesn't matter because both 4>&1
and 4<&1
do the same thing: dup2(1, 4)
which is the system call to duplicate a fd onto another. The duplicated fd automatically inherits the I/O direction of the original fd. (same for 4>&-
vs 4<&-
which both resolve to close(4)
, and 4>&1-
which is the dup2(1, 4)
followed by close(1)
).
However, the 4<&1
syntax is confusing unless for some reason the fd 1 was explicitly open for reading (which would be even more confusing), so in my mind should be avoided.
The duplicated fd
shares the same open file description which means they share the same offset in the file (for those file types where it makes sense) and same associated flags (I/O redirection/opening mode, O_APPEND and so on).
On Linux, there's another way to duplicate a fd
(which is not really a duplication) and create a new open file description for the same resource but with possibly different flags.
exec 3> /dev/fd/4
While on Solaris and probably most other Unices, that is more or less equivalent to dup2(4, 3)
, on Linux, that opens the same resource as that pointed to by the fd 4 from scratch.
That is an important difference, because for instance, for a regular file, the offset of fd 3 will be 0 (the beginning of the file) and the file will be truncated (which is why for instance on Linux you need to write tee -a /dev/stderr
instead of tee /dev/stderr
).
And the I/O mode can be different.
Interestingly, if fd 4 pointed to the reading end of a pipe, then fd 3 now points to the writing end (/dev/fd/3
behaves like a named pipe):
$ echo a+a | { echo a-a > /dev/fd/0; tr a b; }
b+b
b-b
$ echo a+a | { echo a-a >&0; tr a b; }
bash: echo: write error: Bad file descriptor
b+b
In addition to the documentation jordanm points to, I want to make sure to correct a misconception illustrated in your question—the executed program does not handle redirects. It is barely even aware of them. The shell handles redirects.
A program is started with three files open: stdin (#0), stdout (#1), and stderr (#2). If you just run a program from your shell prompt, these will be connected to your terminal device, so the program reads what you type (stdin), and prints output (stdout) and errors (stderr) to your terminal.
As an example, I just run cat
in a terminal (which tty
says is /dev/pts/31
). I can check which files it has open with lsof
:
$ lsof -a -p `pidof cat` -d0,1,2
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
cat 21257 anthony 0u CHR 136,31 0t0 34 /dev/pts/31
cat 21257 anthony 1u CHR 136,31 0t0 34 /dev/pts/31
cat 21257 anthony 2u CHR 136,31 0t0 34 /dev/pts/31
Indeed, we can see that it has the terminal open for all three. Now, instead, let's try a rather silly cat invocation: cat < /dev/zero > /dev/null 2>/dev/full
, which is redirecting all three:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
cat 21838 anthony 0r CHR 1,5 0t0 1030 /dev/zero
cat 21838 anthony 1w CHR 1,3 0t0 1028 /dev/null
cat 21838 anthony 2w CHR 1,7 0t0 1031 /dev/full
The shell implemented those redirections by passing the three devices as stdin, stdout, and stderr (instead of the terminal). The shell similarly implements pipes. Let's try cat | dd > /dev/null
(a rather silly pipe, indeed):
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
cat 22507 anthony 0u CHR 136,31 0t0 34 /dev/pts/31
cat 22507 anthony 1w FIFO 0,8 0t0 56081395 pipe
cat 22507 anthony 2u CHR 136,31 0t0 34 /dev/pts/31
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
dd 22508 anthony 0r FIFO 0,8 0t0 56081395 pipe
dd 22508 anthony 1u CHR 136,31 0t0 34 /dev/null
dd 22508 anthony 2u CHR 136,31 0t0 34 /dev/pts/31
Notice how the shell has opened a pipe, and it has used it to connect the stdout of cat
to the stdin of dd
. And further how it has connected dd
's stdout to /dev/null
.
The commands being run aren't really aware of the redirections. They just use stdin, stdout, stderr as normal. Those could all be the terminal, or they could be redirected to/from a file, a device, or maybe a pipe to another program. Or even a network socket, if your shell supports that.
Even the most ridiculously complicated pipelines are actually just instructions to the shell on how to connect those three file handles before executing the program.
(NOTE: Some programs behave differently in the case where one of those is attached to a terminal, but that's normally to be more user-friendly in interactive use For example, ls
switches to single-column output and no color when stdout isn't a terminal—which is usually what you want if you're about to pass it to another program. Some programs handle prompting differently if stdin isn't a terminal. And so on.)
Best Answer
These are called shell operators and yes, there are more of them. I will give a brief overview of the most common among the two major classes, control operators and redirection operators, and how they work with respect to the bash shell.
A. Control operators
POSIX definition
And
|&
in bash.A
!
is not a control operator but a Reserved Word. It becomes a logical NOT [negation operator] inside Arithmetic Expressions and inside test constructs (while still requiring an space delimiter).A.1 List terminators
;
: Will run one command after another has finished, irrespective of the outcome of the first.First
command1
is run, in the foreground, and once it has finished,command2
will be run.A newline that isn't in a string literal or after certain keywords is not equivalent to the semicolon operator. A list of
;
delimited simple commands is still a list - as in the shell's parser must still continue to read in the simple commands that follow a;
delimited simple command before executing, whereas a newline can delimit an entire command list - or list of lists. The difference is subtle, but complicated: given the shell has no previous imperative for reading in data following a newline, the newline marks a point where the shell can begin to evaluate the simple commands it has already read in, whereas a;
semi-colon does not.&
: This will run a command in the background, allowing you to continue working in the same shell.Here,
command1
is launched in the background andcommand2
starts running in the foreground immediately, without waiting forcommand1
to exit.A newline after
command1
is optional.A.2 Logical operators
&&
: Used to build AND lists, it allows you to run one command only if another exited successfully.Here,
command2
will run aftercommand1
has finished and only ifcommand1
was successful (if its exit code was 0). Both commands are run in the foreground.This command can also be written
or simply
if command1; then command2; fi
if the return status is ignored.||
: Used to build OR lists, it allows you to run one command only if another exited unsuccessfully.Here,
command2
will only run ifcommand1
failed (if it returned an exit status other than 0). Both commands are run in the foreground.This command can also be written
or in a shorter way
if ! command1; then command2; fi
.Note that
&&
and||
are left-associative; see Precedence of the shell logical operators &&, || for more information.!
: This is a reserved word which acts as the “not” operator (but must have a delimiter), used to negate the return status of a command — return 0 if the command returns a nonzero status, return 1 if it returns the status 0. Also a logical NOT for thetest
utility.And a true NOT operator inside Arithmetic Expressions:
A.3 Pipe operator
|
: The pipe operator, it passes the output of one command as input to another. A command built from the pipe operator is called a pipeline.Any output printed by
command1
is passed as input tocommand2
.|&
: This is a shorthand for2>&1 |
in bash and zsh. It passes both standard output and standard error of one command as input to another.A.4 Other list punctuation
;;
is used solely to mark the end of a case statement. Ksh, bash and zsh also support;&
to fall through to the next case and;;&
(not in ATT ksh) to go on and test subsequent cases.(
and)
are used to group commands and launch them in a subshell.{
and}
also group commands, but do not launch them in a subshell. See this answer for a discussion of the various types of parentheses, brackets and braces in shell syntax.B. Redirection Operators
POSIX definition of Redirection Operator
These allow you to control the input and output of your commands. They can appear anywhere within a simple command or may follow a command. Redirections are processed in the order they appear, from left to right.
<
: Gives input to a command.The above will execute
command
on the contents offile.txt
.<>
: same as above, but the file is open in read+write mode instead of read-only:If the file doesn't exist, it will be created.
That operator is rarely used because commands generally only read from their stdin, though it can come handy in a number of specific situations.
>
: Directs the output of a command into a file.The above will save the output of
command
asout.txt
. If the file exists, its contents will be overwritten and if it does not exist it will be created.This operator is also often used to choose whether something should be printed to standard error or standard output:
In the example above,
>
will redirect standard output and2>
redirects standard error. Output can also be redirected using1>
but, since this is the default, the1
is usually omitted and it's written simply as>
.So, to run
command
onfile.txt
and save its output inout.txt
and any error messages inerror.txt
you would run:>|
: Does the same as>
, but will overwrite the target, even if the shell has been configured to refuse overwriting (withset -C
orset -o noclobber
).If
out.txt
exists, the output ofcommand
will replace its content. If it does not exist it will be created.>>
: Does the same as>
, except that if the target file exists, the new data are appended.If
out.txt
exists, the output ofcommand
will be appended to it, after whatever is already in it. If it does not exist it will be created.>&
: (per POSIX spec) when surrounded by digits (1>&2
) or-
on the right side (1>&-
) either redirects only one file descriptor or closes it (>&-
).A
>&
followed by a file descriptor number is a portable way to redirect a file descriptor, and>&-
is a portable way to close a file descriptor.If the right side of this redirection is a file please read the next entry.
>&
,&>
,>>&
and&>>
: (read above also) Redirect both standard error and standard output, replacing or appending, respectively.Both standard error and standard output of
command
will be saved inout.txt
, overwriting its contents or creating it if it doesn't exist.As above, except that if
out.txt
exists, the output and error ofcommand
will be appended to it.The
&>
variant originates inbash
, while the>&
variant comes from csh (decades earlier). They both conflict with other POSIX shell operators and should not be used in portablesh
scripts.<<
: A here document. It is often used to print multi-line strings.Here,
command
will take everything until it finds the next occurrence ofWORD
,Text
in the example above, as input . WhileWORD
is oftenEoF
or variations thereof, it can be any alphanumeric (and not only) string you like. WhenWORD
is quoted, the text in the here document is treated literally and no expansions are performed (on variables for example). If it is unquoted, variables will be expanded. For more details, see the bash manual.If you want to pipe the output of
command << WORD ... WORD
directly into another command or commands, you have to put the pipe on the same line as<< WORD
, you can't put it after the terminating WORD or on the line following. For example:<<<
: Here strings, similar to here documents, but intended for a single line. These exist only in the Unix port or rc (where it originated), zsh, some implementations of ksh, yash and bash.Whatever is given as
WORD
is expanded and its value is passed as input tocommand
. This is often used to pass the content of variables as input to a command. For example:A few other operators (
>&-
,x>&y
x<&y
) can be used to close or duplicate file descriptors. For details on them, please see the relevant section of your shell's manual (here for instance for bash).That only covers the most common operators of Bourne-like shells. Some shells have a few additional redirection operators of their own.
Ksh, bash and zsh also have constructs
<(…)
,>(…)
and=(…)
(that latter one inzsh
only). These are not redirections, but process substitution.