I am trying to redirect all output from bash (prompt, user input, results) to a file
Example:
/bin/bash > file.txt 2>&1
I thought that would work, but I'm not getting the prompt. Can anyone tell me what I'm doing wrong?
I am trying to redirect all output from bash (prompt, user input, results) to a file
Example:
/bin/bash > file.txt 2>&1
I thought that would work, but I'm not getting the prompt. Can anyone tell me what I'm doing wrong?
Bash outputs the prompt only in interactive mode. I.e. it is normally output to the terminal (/dev/tty on linux). That is neither /dev/stdout or /dev/stdin :)
Now, I'm not sure but I can imagine that bash will allow limited interactive mode when there isn't a fully functional tty. In that case I'd expect the prompt to be written to stdout. I haven't tested that.
Nice Proof Of Concept:
(for a in some set of words; do echo $a > /dev/tty; done) 2>&1 > /dev/null
will just output 1..10 as if there wasn't redirection. Like the prompt, output is directly sent to the terminal (which will fail if there isn't one)
HINT: if you wanted everything to be collected look at
set -o xtrace
(a.k.a. set -x
, bash -x
etc) for general logging of statementsThe simplest way to do it would be
bash -i >/tmp/logfile 2>&1
Bash will write everything to /tmp/logfile
and keep executing commands as you type them, but nothing will be displayed in the terminal. You can make it exit just as you exit your terminal session - by pressing Ctrl+D or typing exit
.
Notice that if you run the same thing without stderr
redirection, you will only have the greeting message logged to the file, all the rest will work in your current terminal. So the answer to your question about stream to which bash outputs its prompt (and all the following commands) seems to be: stderr.
Oh yes, and the -i
parameter simply forces bash to run in interactive mode. Don't listen to those people - you don't need any magic tricks to do that ;)
<sub>
to format. I just learned something new today. :D
– Krista K
Jan 13 '14 at 20:43
The prompt is written to stderr as truss (on Solaris here) shows:
$ truss -ft write -p 10501
10501: write(2, " d", 1) = 1
10501: write(2, " a", 1) = 1
10501: write(2, " t", 1) = 1
10501: write(2, " e", 1) = 1
10501: write(2, "\n", 1) = 1
10521: write(1, " S a t u r d a y , S e".., 46) = 46
10501: Received signal #18, SIGCLD [caught]
10501: siginfo: SIGCLD CLD_EXITED pid=10521 status=0x0000
10501: write(2, " $ ", 2) = 2
To trick bash
into thinking it's in interactive mode (although stdout
is not being sent to a terminal) you may use the already mentioned script
command.
(
exec 1> >(tee bashlog.txt) 2>&1
script -q /dev/null /bin/bash -l
)
# alternative without script command
(
# bash: no job control in this shell
exec 1> >(tee bashlog.txt) 2>&1
/bin/bash -il
)
In interactive mode, bash outputs its prompt into stderr. The following strace command shows it (--norc
option to make sure that ~/.bashrc which sets another prompt is not interpreted to overwrite my custom prompt):
$ export PS1="MYPROMPT> "
MYPROMPT> strace -f -- bash --norc
execve("/usr/bin/bash", ["bash"], 0x7ffcd83805b0 /* 59 vars */) = 0
[...]
openat(AT_FDCWD, "/dev/tty", O_RDWR|O_NONBLOCK) = 3
[...]
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(2, TCGETS, {B38400 opost isig icanon echo ...}) = 0
[...]
write(2, "MYPROMPT> ", 10MYPROMPT> ) = 10 <----- prompt written into stderr (fd = 2)
pselect6(1, [0], NULL, NULL, NULL, {[], 8}
The same command with the redirection of stdout and stderr into a file shows that the prompt is not printed but the shell accepts the commands:
MYPROMPT> strace -f -- bash --norc > /tmp/shout 2> /tmp/sherr
date
Look at the files from another terminal. The content of shout is:
$ cat /tmp/shout
dim. 04 sept. 2022 16:19:24 CEST <------- Result of the date command entered above
The content of sherr is:
$ cat /tmp/sherr
execve("/usr/bin/bash", ["bash", "--norc"], 0x7ffe73ea5da8 /* 59 vars */) = 0
[...]
openat(AT_FDCWD, "/dev/tty", O_RDWR|O_NONBLOCK) = 3
[...]
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(2, TCGETS, 0x7ffc43496b30) = -1 ENOTTY (Inappropriate ioctl for device)
[...]
read(0, <-------- No prompt is displayed
But if you do the same adding -i
option to the bash command line:
MYPROMPT> strace -f -- bash --norc -i > /tmp/shout 2> /tmp/sherr
The prompt is displayed on stderr:
$ cat /tmp/sherr
execve("/usr/bin/bash", ["bash", "--norc", "-i"], 0x7ffd8c1fa520 /* 59 vars */) = 0
[...]
ioctl(2, TCGETS, 0x7ffe1a8465f0) = -1 ENOTTY (Inappropriate ioctl for device)
[...]
ioctl(0, TIOCGWINSZ, {ws_row=37, ws_col=155, ws_xpixel=0, ws_ypixel=0}) = 0
[...]
write(2, "MYPROMPT> ", 10MYPROMPT> ) = 10 <------ Prompt displayed on stderr (fd = 2)
pselect6(1, [0], NULL, NULL, NULL, {[], 8}
seq
is a highly nonstandard external command, and shouldn't be used in this manner. If you're usingbash
, do something likefor x in {1..10}
, orfor ((x=1; x<=10; x++))
instead. – Chris Down Sep 15 '11 at 23:25bash -i > stdout.txt 2> stderr.txt
and you'll see that the prompt is written to stderr.txt, not your terminal. – Dominick Pastore Jan 28 '21 at 18:11