Weird output when redirecting bash prompt to a file
Solution 1:
bash 2>file
explained
Interactive Bash does use stderr to print the prompt (and the command line while it's being edited).
bash 2>file
does not save any of these to the file because it's not interactive. Non-interactive bash
does not print any prompt, does not evaluate PROMPT_COMMAND
; there is no command line editing provided by readline
and if the input is from the terminal then only basic editing provided by the terminal driver is available.
While Bash Reference Manual claims "an interactive shell is one started without non-option arguments […] and whose input and output are both connected to terminals […]", my tests indicate it's not about stdin and stdout (as I would interpret "input and output") but about stdin and stderr. I mean these start non-interactive shells:
<script bash
bash 2>file
and these start interactive shells:
bash >file
bash
With bash 2> file
you started a non-interactive shell. bash
did not print any prompt, that's why the file turned out to be empty.
If you did bash -i 2>file
then you would enforce an interactive shell, you would send prompt(s) to the file. Try it, but expect no echo of what you type, because this goes to the file.
Similarly if you redirected stderr of already running interactive bash (exec 2>file
) then you would also capture its prompt(s).
bash >file
explained (in your case)
As stated above, bash >file
starts an interactive shell. One sees prompts in the terminal, one can edit commands. Stderr is redirected as one expects.
In your case file
contained extra strings because your PROMPT_COMMAND
is what it is.
PROMPT_COMMAND
If this variable is set, and is an array, the value of each set element is interpreted as a command to execute before printing the primary prompt ($PS1
). If this is set but not an array variable, its value is used as a command to execute instead.
(source)
This is your PROMPT_COMMAND
:
printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/\~}"
When executed, it prints to stdout. If stdout is redirected to a file, it will print to the file.
When sent to a sufficiently advanced terminal (terminal emulator), a byte sequence resulted from \033]0;…\007
changes icon name and window title. In other words normally your terminal intercepts this output and uses it to configure itself rather then printing anything.
Probably you had deliberately set this PROMPT_COMMAND
and forgot (or wasn't aware in the first place) it prints to stdout.
When stdout is redirected to a regular file then there will be no terminal to intercept the sequence and what printf
from PROMPT_COMMAND
prints will get to the file. You observed exactly this. It seems bat
tried to do something with the ESC characters (from \033
), ]0;
and the first letter of kapil
, that's why you saw apil
; BEL characters resulting from \007
were shown as ^G
. It doesn't matter. The point is what you didn't expect came from printf
in your PROMPT_COMMAND
.
If I were you and I wanted to keep this PROMPT_COMMAND
, I would set it to printf … >&2
. Normally stdout and stderr of an interactive shell go to the same terminal, so it doesn't matter which one printf
(executed from PROMPT_COMMAND
) prints to. But when you redirect stdout as you did, you don't want "garbage" from PROMPT_COMMAND
.
Solution 2:
Redirecting will result in a file that is difficult to read (or use) because of the binary characters in the prompt.
For getting a readable complete copy of your session, you could use script(1) to log everything sent to the terminal:
$ script
Script started, file is typescript
$ # do your work
...
$ # then exit with ^D
$ exit
Script done, file is typescript