14

I am a newbie in shell scripting and I am using Ubuntu-11.10. In the terminal after using exec 1>file command, whatever commands I give to terminal, its output doesn't get shown in terminal. I know that STDOUT is getting redirected to the file, the output of those commands gets redirected to file.

My questions are here

  1. Once I use exec 1>file, how can I get rid of this? i.e. How can I stop the redirection of STDOUT to file and restore the normal operation of STDOUT (i.e. redirection to terminal rather than file)?

    I tried using exec 1>&- but it didn’t work as this closes the STDOUT file descriptor.

  2. Please throw light on this entire operation of exec 1>file and exec 1>&-

  3. What will happen if we close the standard file descriptors 0, 1, 2 by using exec 0>&- exec 1>&- exec 2>&-?

Community
  • 1
  • 1
pawan vinayak
  • 185
  • 1
  • 2
  • 11
  • I'm a bit confused. When you say "stop the redirection of STDOUT", what's the use case? I have a feeling you want to pipe to the `tee` command to output both to STDOUT and to file, but I cannot be certain from what you've said. – JayC Aug 24 '14 at 18:40
  • @JayC let me clarify my question, in terminal i gave exec 1>file_name.txt then i gave date and ls commands each time i give a new command, no output is shown in terminal rather output gets redirected to file_name.txt Now my question is that, what i have to do here, so that the output of these commands get displayed back to terminal instead of getting it redirected to file_name.txt – pawan vinayak Aug 24 '14 at 18:56
  • 1
    Don't redirect stdout in interactive shell; if you want to log your work, use script(1) or similar. To answer the original question: press Ctrl+D – Lorinczy Zsigmond Aug 26 '15 at 07:42

4 Answers4

26

Q1

You have to prepare for the recovery before you do the initial exec:

exec 3>&1 1>file

To recover the original standard output later:

exec 1>&3 3>&-

The first exec copies the original file descriptor 1 (standard output) to file descriptor 3, then redirects standard output to the named file. The second exec copies file descriptor 3 to standard output again, and then closes file descriptor 3.

Q2

This is a bit open ended. It can be described at a C code level or at the shell command line level.

exec 1>file

simply redirects the standard output (1) of the shell to the named file. File descriptor one now references the named file; any output written to standard output will go to the file. (Note that prompts in an interactive shell are written to standard error, not standard output.)

exec 1>&-

simply closes the standard output of the shell. Now there is no open file for standard output. Programs may get upset if they are run with no standard output.

Q3

If you close all three of standard input, standard output and standard error, an interactive shell will exit as you close standard input (because it will get EOF when it reads the next command). A shell script will continue running, but programs that it runs may get upset because they're guaranteed 3 open file channels — standard input, standard output, standard error — and when your shell runs them, if there is no other I/O redirection, then they do not get the file channels they were promised and all hell may break loose (and the only way you'll know is that the exit status of the command will probably not be zero — success).

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 1
    »Programs might get upset if they are run with no standard output.« I couldn't have put it better :-)) – Alfe Aug 24 '14 at 21:33
  • @Jonathan syntax for closing file descriptors is `exec n>&-`(for output file descriptors) and `exec n<&-`(for input file descriptors).but the way in which above file descriptor 3 is closed(i.e. `>&3-`), is confusing me.Can you please suggest me a way to understand and remember this. – pawan vinayak Aug 25 '14 at 04:27
  • @pawanvinayak: typo...that's all. Thanks for pointing it out. – Jonathan Leffler Aug 25 '14 at 04:28
  • While you suggestion is technically correct and a good reference for the general case, it is somewhat too complex in the OP specific case. There is no need to save stdout when it is the controlling terminal, it is already saved in the process context. – jlliagre Aug 25 '14 at 14:08
  • I have implemented something like this "exec 3>&1 1>> >(redirectToLogger) 2>&1". Now I want to reset this redirection after my job is done. How can I do this? – Ankit Raj Jun 05 '19 at 07:06
  • `exec 1>&3 3>&-` — I think without having done any testing. – Jonathan Leffler Jun 05 '19 at 07:11
10

Q1: There is a simple way to restore stdout to the terminal after it has been redirected to a file:

exec >/dev/tty

While saving the original stdout file descriptor is commonly required for it to be restored later, in this particular case, you want stdout to be /dev/tty so there is no need to do more.

$ date
Mon Aug 25 10:06:46 CEST 2014
$ exec > /tmp/foo
$ date
$ exec > /dev/tty
$ date
Mon Aug 25 10:07:24 CEST 2014
$ ls -l /tmp/foo
-rw-r--r-- 1 jlliagre jlliagre 30 Aug 25 10:07 /tmp/foo
$ cat /tmp/foo
Mon Aug 25 10:07:05 CEST 2014

Q2: exec 1>file is a slightly more verbose way of doing exec >file which, as already stated, redirect stdout to the given file, provided you have the right to create/write it. The file is created if it doesn't exist, it is truncated if it does.

exec 1>&- is closing stdout, which is probably a bad idea in most situations.

Q3: The commands should be

exec 0<&-
exec 1>&-
exec 2>&-

Note the reversed redirection for stdin.

It might be simplified that way:

exec <&- >&- 2>&-

This command closes all three standard file descriptors. This is a very bad idea. Should you want a script to be disconnected from these channels, I would suggest this more robust approach:

exec </dev/null >/dev/null 2>&1

In that case, all output will be discarded instead of triggering an error, and all input will return just nothing instead of failing.

jlliagre
  • 29,783
  • 6
  • 61
  • 72
  • 1
    `exec 1>/dev/tty` worked for me to restore stdout to the terminal after it has been redirected to a file and as mentioned in my comment in above answer,`exec 1>/dev/pts/0` also worked.Since `exec 1>/dev/pts/0` and `exec 1>/dev/tty` both are capable of doing this task, this had now triggered a question are both these `exec 1>/dev/pts/0` and `exec 1>/dev/tty` are same? – pawan vinayak Aug 25 '14 at 09:54
  • 1
    As I wrote, `exec 1>/dev/tty` and `exec >/dev/tty` are exactly equivalent so save one keystroke and use the latter. On the other hand, while `exec >/dev/tty` is portable, `exec 1>/dev/pts/0` is not and happen to do the same in your experiment only by chance. That wouldn't work with another terminal window and might not work either with a different machine. Just use `exec >/dev/tty` and you are safe. – jlliagre Aug 25 '14 at 12:38
  • 1
    Note that if the shell output is not going to the terminal (for example, it goes to a pipe), then using `/dev/tty` is actively wrong. For interactive use, redirecting to `/dev/tty` is OK, of course, but it is better to know how to do it right regardless of how the script is being run (including 'running as a command line'). – Jonathan Leffler Aug 25 '14 at 14:21
  • @JonathanLeffler I obviously agree. My point is the OP explicitely states he is working from the terminal, not from a redirected script, the reason why I prefer to suggest a simple solution that works for him and in the majority of the cases. – jlliagre Aug 25 '14 at 14:29
2

The accepted answer is too verbose for me. So, I decided to sum up an answer to your original answer.

Using Bash version 4.3.39(2)-release

On a x86 32-Bit on Cygwin Machine

GIVEN:

  • Stdin is fd #0.
  • Stdout is fd #1.
  • Stderr is fd #2.

ANSWER (written in bash):

exec 1> ./output.test.txt
echo -e "First Line: Hello World!"
printf "%s\n" "2nd Line: Hello Earth!" "3rd Line: Hello Solar System!"

# This is uneccessary, but
# it stops or closes stdout.
# exec 1>&-

# Send stdout back to stdin
exec 1>&0

# Oops... I need to append some more data.
# So, lets append to the file.
exec 1>> ./output.test.txt
echo -e "# Appended this line and the next line is empty.\n"

# Send stdout back to stdin
exec 1>&0

# Output the contents to stdout
cat ./output.test.txt

USEFUL-KEYWORDS:

There are also here-docs, here-strings, and process-substitution for IO redirection in Bourne, Bash, tcsh, zsh for Linux, BSD, AIX, HP, Busybox, Toybox and etcetera.

1

While I completely agree with Jonathan's Q1, some systems have /dev/stdout, so you might be able to do exec 1>file; ...; exec 1>/dev/stdout

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • after using `exec 1>file` when i gave `exec 1>/dev/stdout`, the STDOUT still didn't re-appeared on terminal but using this clue and some hit-n-trial i finally succeeded in getting STDOUT to get redirected to terminal using `exec 1>/dev/pts/0` – pawan vinayak Aug 25 '14 at 04:43
  • **user@ubuntu:~$** `cd /dev` **user@ubuntu:/dev$** `ls -l std*` _lrwxrwxrwx 1 root root 15 2014-08-25 08:47_ `stderr -> /proc/self/fd/2` _lrwxrwxrwx 1 root root 15 2014-08-25 08:47_ `stdin -> /proc/self/fd/0` _lrwxrwxrwx 1 root root 15 2014-08-25 08:47_ `stdout -> /proc/self/fd/1` – pawan vinayak Aug 25 '14 at 05:12
  • **user@ubuntu:/dev$** `cd /proc/self/fd/` **user@ubuntu:/proc/self/fd$** `ls -l` _total 0_ _lr-x------ 1 user user 64 2014-08-25 09:40 0_ -> `/dev/pts/0` _l-wx------ 1 user user 64 2014-08-25 09:40 1_ -> `/dev/pts/0` _l-wx------ 1 user user 64 2014-08-25 09:40 2_ -> `/dev/pts/0` _lrwx------ 1 user user 64 2014-08-25 09:47 255_ -> `/dev/pts/0` – pawan vinayak Aug 25 '14 at 05:17
  • **user@ubuntu:/proc/self/fd$** `cd` **user@ubuntu:~$** `exec 1>file` **user@ubuntu:~$** `date` **user@ubuntu:~$** `exec 1>/dev/pts/0` **user@ubuntu:~$** `date` _Mon Aug 25 09:48:55 IST 2014`_ – pawan vinayak Aug 25 '14 at 05:19
  • although it worked any how... but still i didn't got a clear idea how `exec 1>/dev/pts/0` worked..? – pawan vinayak Aug 25 '14 at 09:21