How closing STDOUT effects printf - linux

If someone closes a proccess' STDOUT in it's file descriptor table like so:
close(STDOUT);
and then opens a file to read/write :
​
int ​fd = open(​"myFile"​, O_RDWR);
and after that uses printf:
printf("hello");
I know it won't go to the screen, but will it be printed in the file? Or does he have to use fprintf or the write system call?

From the man page of open:
The file descriptor returned by a successful call will be the lowest-numbered file descriptor not currently open for the process.
When you close the file descriptor for STDOUT, the subsequent open system call will assign the fd of stdout to the new file. Printf just sends to the fd it doesn't matter whether it is stdout or not. So a printf in this scenario will dump the output to "myfile"

Related

Understanding file descriptor duplication in bash

I'm having a hard time understanding something about redirections in bash.
I'll start with what I know:
Each process has file descriptors opened which it can write to/read from. These file descriptors may represent files on disk, terminals, devices, etc.
When we start teminal with bash, we have file stdin (0) stdout (1) and stderr (2) opened, pointing to the terminal. Whenever we run a command (a new process), that process inherits the file descriptors of its parent (bash), so by default, it will print stdout and stderr messages to the terminal, and read from terminal also.
When we redirect, for example:
$ ls 1>filelist
We're actually changing file descriptor 1 of the ls process, to point to the filelist file, instead of the terminal. So when ls will write(1, ...) it will go to the file.
So to sum it up, a redirection is basically changing the file to which the file descriptor to which the program writes/reads to/from refers to.
Now, let's say I have the following C program:
#include <stdio.h>
#include <fcntl.h>
int main()
{
int fd = 0;
fd = open("info.log", O_CREAT | O_RDWR);
printf("%d", fd);
write(fd, "INFO::", 6);
return 0;
}
This program opens a file info.log, which is referred to by a file descriptor (usually 3).
Indeed, if I now compile this program and run it:
$ ./app
3
It creates the file info.log which contains the "INFO::" text in it.
But here's what I don't get: according to the logic described above, if I now redirect FD 3 to another file:
$ ./app 3> another_file
The text should be written to this other file, but for some reason, it doesn't.
Can someone explain?
Hint: when you run ./app 3> another_file, it'll print "4" instead of "3".
More detailed explanation: when you run ./app 3> another_file in the shell, a series of things happens:
The shell fork()s a subprocess that'll run ./app. The subprocess is basically a clone of its parent process so, it'll still be running the shell program.
In that subprocess, the shell opens "another_file" on file descriptor #3 for writing.
Then it uses one of the execl() family of calls to execute the ./app binary (with "another_file" still open on FD#3).
The program runs open("info.log", O_CREAT | O_RDWR), which creates "info.log" and opens it on the next available file descriptor. Since FD#3 is already in use, that's FD#4.
The program writes "INFO::" to FD#4, which is "info.log".
Since open() uses a new FD, it's not really affected by any active redirects. And actually, if the program did open something on FD#3, that'd replace the connection to "another_file" with whatever it had opened instead, essentially overriding the redirect.
If the program wanted to use the redirect, it'd have to write to FD#3 without first opening anything on it. This is what's normally done with FD#1 and 2 (standard output and error), and that's why redirecting those works.

by default, does stderr start as a duplicate file descriptor of stdout?

Does stderr start out as a duplicate FD of stdout?
i.e. considering dup(2), is stderr initialized kind of like so?
int stderr = dup(stdout); // stdout = 1
In the BashGuide, there's a code example
$ grep proud file 'not a file' > proud.log 2> proud.log
The author states
We've created two FDs that both point to the same file, independently of each other. The results of this are not well-defined. Depending on how the operating system handles FDs, some information written via one FD may clobber information written through the other FD.
and further says
We need to prevent having two independent FDs working on the same destination or source. We can do this by duplicating FDs
So basically, 2 independent FDs on the same file = broken
Well, I know that stdout & stderr both point to my terminal by default. Since they can both function properly (i.e. i don't see mish-mashed output+error messages), does that mean that they're not independent FDs? And thus, stderr is a duplicate FD of stdout? (or vice versa)
No, stderr is not a duplicate of stdout.
They work in parallel, independent and asynchronously.
Which means that in a race condition, you might even 'mish-mash' as you say.
One practical difference is also that stderr output will be ignored when you pipe your output to a subsequent command:
Practical example:
$ cat tst.sh
#!/bin/bash
echo "written to stdout"
echo "written to stderr" 1>&2
exit 0
~$ ./tst.sh
written to stdout
written to stderr
~$ ./tst.sh | xargs -n1 -I{} echo "this came through the pipe:{}"
written to stderr
this came through the pipe:written to stdout

How do I redirect 'stdout' to 'stderr' and 'stderr' to 'stdout'? [duplicate]

This question already has answers here:
IO Redirection - Swapping stdout and stderr
(4 answers)
Closed 7 years ago.
I want to redirect everything that is supposed to go to stdout to stderr, and everything that is going to stderr to go to stdout instead.
The following code does not work:
$ bin/stdout-test 1>&2 2>&1
I'm sure there is a practical use for this somewhere out there, but currently it's just a mental exercise to help learn and understand io redirection.
In Ubuntu, both stdout and stderr get redirected to the console anyway, so you can't really tell if you are making any progress, but the command tcpserver will redirect stdout to the connecting remote user, and stderr to the terminal that started the tcpserver session, making it a perfect place to test this type of redirection.
I created a test project just for this purpose:
GitHub: IQAndreas-testprojects/stdout-stderr-testing
Your attempt does not work because 1>&2 merges stdout into stderr, and after that there is no hope of ever separating them again. You need a third temporary file descriptor to do the swap, like when swapping the values of two variables.
Use exec to move file descriptors.
( exec 3>&1- 1>&2- 2>&3- ; bin/stdout-test )
or just
bin/stdout-test 3>&1- 1>&2- 2>&3-
Explanation:
3>&1- moves stdout (FD 1) to FD 3
1>&2- moves stderr (FD 2) to FD 1
2>&3- moves FD 3 to stderr (FD 2)
See Moving File Descriptors.

order of 2>&1 when redirecting std error to file

I'm seeing some different formats for redirecting std output to a file :
a. command 1&2>output.txt
b. command >output.txt 2>&1
c. command 2>&1>output.txt
d. command &>output.txt
Is there any difference between these?
If 2>&1 is placed at the end (b) , how does it redirect the stderr of the first command ?
Yes. The order matters. > may bring up the idea of pointing and pointers/references and so does the word "redirect", but fd redirections are more like assignments.
That is, if you do
exec 2>&1 1>output.txt
It will "assign" the current "value" (the actual file opened with that file descriptor) of file descriptor 1 to file descriptor 2 and then open output.txt and assign it to file descriptor 1.
What it won't do is point &2 (read & as "file descriptor") to &1. It won't make accessing &2 query &1.
A file descriptor is only ever associated with an actual file, never with another file descriptor. 2>&1 associates the file now opened under &1 with &2. It doesn't redirect &2 to &1 in the sense that writing to &2 would make it write to what &1 is associated with at the moment. &1 can later be reopened with a different file that what it was associated with at the time of the 2>&1 redirection, but that won't affect what &2 writes to.
Check out dup2(2) if you want to know how this functionality is exposed at the system call level.

exec n<&m versus exec n>&m -- based on Sobell's Linux book

In Mark Sobell's A Practical Guide to Linux Commands, Editors, and Shell Programming, Second Edition he writes (p. 432):
The <& token duplicates an input file
descriptor; >& duplicates an output
file descriptor.
This seems to be inconsistent with another statement on the same page:
Use the following format to open or
redirect file descriptor n as a
duplicate of file descriptor m:
exec n<&m
and with an example also on the same page:
# File descriptor 3 duplicates standard input
# File descriptor 4 duplicates standard output
exec 3<&0 4<&1
If >& duplicates an output file descriptor then should we not say
exec 4>&1
to duplicate standard output?
The example is right in practice. The book's original explanation is an accurate description of what the POSIX standard says, but the POSIX-like shells I have handy (bash and dash, the only ones I believe are commonly seen on Linux) are not that picky.
The POSIX standard says the same thing as the book about input and output descriptors, and goes on to say this: for n<&word, "if the digits in word do not represent a file descriptor already open for input, a redirection error shall result". So if you want to be careful about POSIX compatibility, you should avoid this usage.
The bash documentation also says the same thing about <& and >&, but without the promise of an error. Which is good, because it doesn't actually give an error. Instead, empirically n<&m and n>&m appear to be interchangeable. The only difference between <& and >& is that if you leave off the fd number on the left, <& defaults to 0 (stdin) and >& to 1 (stdout).
For example, let's start a shell with fd 1 pointing at a file bar, then try out exactly the exec 4<&1 example, try to write to the resulting fd 4, and see if it works:
$ sh -c 'exec 4<&1; echo foo >&4' >bar; cat bar
foo
It does, and this holds using either dash or bash (or bash --posix) for the shell.
Under the hood, this makes sense because <& and >& are almost certainly just calling dup2(), which doesn't care whether the fds are opened for reading or writing or appending or what.
[EDIT: Added reference to POSIX after discussion in comments.]
If stdout is a tty, then it can safely be cloned for reading or writing. If stdout is a file, then it may not work. I think the example should be 4>&1. I agree with Greg that you can both read and write the clone descriptor, but requesting a redirection with <& is supposed to be done with source descriptors that are readable, and expecting stdout to be readable doesn't make sense. (Although I admit I don't have a reference for this claim.)
An example may make it clearer. With this script:
#!/bin/bash
exec 3<&0
exec 4<&1
read -p "Reading from fd 3: " <&3
echo From fd 3: $REPLY >&2
REPLY=
read -p "Reading from fd 4: " <&4
echo From fd 4: $REPLY >&2
echo To fd 3 >&3
echo To fd 4 >&4
I get the following output (the stuff after the : on "Reading from" lines is typed at the terminal):
$ ./5878384b.sh
Reading from fd 3: foo
From fd 3: foo
Reading from fd 4: bar
From fd 4: bar
To fd 3
To fd 4
$ ./5878384b.sh < /dev/null
From fd 3:
Reading from fd 4: foo
From fd 4: foo
./5878384b.sh: line 12: echo: write error: Bad file descriptor
To fd 4
$ ./5878384b.sh > /dev/null
Reading from fd 3: foo
From fd 3: foo
./5878384b.sh: line 9: read: read error: 0: Bad file descriptor
From fd 4:
To fd 3
Mind the difference between file descriptors and IO streams such as stderr and stdout.
The redirecting operators are just redirecting IO streams via different file descriptors (IO stream handling mechanisms); they do not do any copying or duplicating of IO streams (that's what tee(1) is for).
See: File Descriptor 101
Another test to show that n<&m and n>&m are interchangeable would be "to use either style of 'n<&-' or 'n>&-' for closing a file descriptor, even if it doesn't match the read/write mode that the file descriptor was opened with" (http://www.gnu.org/s/hello/manual/autoconf/File-Descriptors.html).

Resources