Why is my shell history not written sometimes, and how can I fix this? - bash

Sometimes my shell history isn't written. I think it might be when a shell exits because of certain signals, like when the OS is shutting down. So:
is it true that bash and/or zsh don't write out their history when terminating as a response to certain signals?
is there a way to hook into these signals and tell the shell to still write history?
I would rather not have it write history after each command.

Your history command store in RAM until you regularly terminate your terminal. Then list of your command write into the .bash_history.
If you wanted to write your commands history at anytime you need, use below command:
#history -a
Based on history command manual -a append history lines from this session to the history file

Related

Redirect entire bash session to log file

I am familiar with the ability to pipe and redirect the IO of individual processes when running them in bash. However, is there a way to redirect stdio for an entire bash session?
Ideally, I would like to transparently pipe all stdout and stderr of all processes spawned by bash into tee to duplicate into a file the printed output displayed to the user. No matter what processes are run within that bash session, I could then go back later and look over the output.
Even more ideally, this should be the case for simple interactive programs that take options from stdin, but not for heavily interactive programs like vim.
The best I've found so far is: whenever the user opens a new terminal, run the command:
bash --login -i > >(tee ~/bash_$$.log) 2>&1
This will immediately start an interactive child shell in that new shell, and tee all stdin and stderr to a logfile named with the new parent shell's PID (to avoid overwriting).
This works, but vim fails to start with Vim: Warning: Output is not to a terminal. Are there any known solutions, up to and including patching the shell, to do this?
Background: vim is failing because isatty() is returning false when given the file descriptor for stdout; this is a safeguard to prevent uses such as vim >file that generally don't make sense. (Also, there are operating system calls available for interacting with PTYs that are useful to graphical, cursor-oriented programs that aren't available with a simple FIFO; this is why tools like ssh go to the trouble to provide a pseudoterminal during interactive use).
What's important for your purposes is that vim is directly inspecting the file descriptor it's passed as stdout. The shell is not a party to this -- it's literally vim running a standard-C-library call that gets some details about an open file descriptor -- so it's nothing that patching or reconfiguring the shell can fix.
To avoid this, then, you need to find a different way to redirect your output for logging such that stdout and stderr are still pointed at PTYs.
That said, for your real goal (logging all activity, vs redirecting stdout in-place), what you want is probably script:
if [ -z "$redirection_done" ]; then
redirection_done=1 exec script shell.log bash --login -i
fi
Using logging support from another tool which simulates a TTY, such as screen or tmux, will likewise suffice. (unbuffer, from the expect toolkit, can be used with similar effect).
Back to your literal question... (since while it may not be what you want to know, it is what you asked):
In all POSIX shells, including bash,
exec >wherever
...will immediately redirect stdout for the current shell to wherever. This can be a process substitution in bash, as anywhere else; thus, in an already-running shell, you can execute
exec > >(tee shell.log) 2>&1

Bash script calls vi for manual editing, then script resumes?

I wrote a script that creates a backup of a text file, and a second script that verifies some syntax in text file using SED.
In the middle, there is a manual process: Users edit the original file adding some strings. This process must remain manual.
I would like to merge my two scripts so the backup is created, vi is open for the user, when the user is done editing the file, the script resumes doing the syntax verification.
I am learning by doing, but really do not know how to code the "open vi, wait for the user to do his editing, take control over and resume with verification" part.
I read there is a function called system (in Perl) that could be used, but my code is in BASH.
Any suggestions on how to get this done in BASH? Thanks!
In bash, each statement is essentially like an implicit call to system (unless it's a builtin shell command) since shell scripts are designed to make it easy to run other programs.
backup some_file.txt
vi some_file.txt # The script blocks until the user exits vi
verify_syntax some_file.txt
The only difference between using vi and a command like ls is that ls will do its thing and exit without user intervention, while vi (or any interactive command) will run until the user explicitly exits.

BASH: Is there a way to automatically save recent lines to my bash history during a period of inactivity?

The .bash_history file is a life-save for many of us. Unfortunately, BASH only seems to save the commands of a session when that session is closed (via exit).
It's a tragedy, then, when all your commands from an important session are vaporized when a session is closed unexpectedly -- before it gets to archive all the commands with fancy syntax that took hours to get right....
This happens to me when I forget to close a SSH connection when leaving work, and it gets disconnected due to inactivity (Write failed: broken pipe), or when I restart my computer without closing my terminals manually, and so on.
I would love to have my BASH commands archived after some interval -- say every 10 minutes -- so that if I do close a session, my commands will still be there. This seems like something a lot of people might find useful.
Does anyone have an idea of how to do this?
Ideally....
The functionality would require no extra effort on the user's part once set up -- something he/she could add to ~/.bashrc
The user could change the backup interval
It would avoid using temporary files, aliasing bash, or other "hacks"
StackOverflow-ers -- consider yourself challenged!
You can use history command with -a option:
history
-a Append the ``new'' history lines (history lines entered since the
beginning of the current bash session) to the history file.
You can write each and every command to history file at once with a little help of PROMPT_COMMAND function:
PROMPT_COMMAND
If set, the value is executed as a command prior to issuing each primary prompt.
So just put this into .bashrc
PROMPT_COMMAND="history -a"
According to this bash will (usually) receive a SIGHUP on disconnect.
Using trap we can write the history (lame as #*?! that bash doesn't do it by default though..):
function rescue_history {
history -a
}
trap rescue_history SIGHUP
Put the above in your .bashrc

Are shell scripts read in their entirety when invoked?

I ask because I recently made a change to a KornShell (ksh) script that was executing. A short while after I saved my changes, the executing process failed. Judging from the error message, it looked as though the running process had seen some -- but not all -- of my changes. This strongly suggests that when a shell script is invoked, the entire script is not read into memory.
If this conclusion is correct, it suggests that one should avoid making changes to scripts that are running.
$ uname -a
SunOS blahblah 5.9 Generic_122300-61 sun4u sparc SUNW,Sun-Fire-15000
No. Shell scripts are read either line-by-line, or command-by-command followed by ;s, with the exception of blocks such as if ... fi blocks which are interpreted as a chunk:
A shell script is a text file containing shell commands. When such a
file is used as the first non-option argument when invoking Bash, and
neither the -c nor -s option is supplied (see Invoking Bash), Bash
reads and executes commands from the file, then exits. This mode of
operation creates a non-interactive shell.
You can demonstrate that the shell waits for the fi of an if block to execute commands by typing them manually on the command line.
http://www.gnu.org/software/bash/manual/bashref.html#Executing-Commands
http://www.gnu.org/software/bash/manual/bashref.html#Shell-Scripts
It's funny that most OS'es I know, do NOT read the entire content of any script in memory, and run it from disk. Doing otherwise would allow making changes to the script, while running. I don't understand why that is done, given the fact :
scripts are usually very small (and don't take many memory anyway)
at some point, and shown in this thread, people would start making changes to a script that is already running anyway
But, acknowledging this, here's something to think about: If you decided that a script is not running OK (because you are writing/changing/debugging), do you care on the rest of the running of that script ? you can go ahead making the changes, save them, and ignore all output and actions, done by the current run.
But .. Sometimes, and that depends on the script in question, a subsequent run of the same script (modified or not), can become a problem since the current/previous run is doing an abnormal run. It would typically skip some stuff, or sudenly jump to parts in the script, it shouldn't. And THAT may be a problem. It may leave "things" in a bad state; particularly if file manipulation/creation is involved.
So, as a general rule : even if the OS supports the feature or not, it's best to let the current run finish, and THEN save the updated script. You can change it already, but don't save it.
It's not like in the old days of DOS, where you actually have only one screen in front of you (one DOS screen), so you can't say you need to wait on run completion, before you can open a file again.
No they are not and there are many good reasons for that.
One of the things you should keep in mind is that a shell is not an interpreter even if there are some similarities. Shells are designed to work with a stream of commands. Either from the TTY ,a PIPE, FIFO or even a socket.
The shell reads from its resource line by line until a EOF is returned by the kernel.
The most shells have no extra support for interpreting files. they work with a file as they would work with a terminal.
In fact this is considered to be a nice feature because you can do interesting stuff like this How do Linux binary installers (.bin, .sh) work?
You can use a binary file and prepend shell scripts. You can't do this with an interpreter. because it parses the whole file or at least it would try it and fail. A shell would just interpret it line by line and doesnt care about the garbage at the end of the file. You just have to make sure the execution of the script gets terminated before it reaches the binary part.

How do I tell Zsh to write the current shell's history to my history file?

I work in a place that has gazillions of tools which require tons of options, so I rely on my shell's history significantly. I even back it up every now and then just to make sure I don't lose useful, lengthy commands.
I just typed one of these commands and I want to make sure it's flushed to the history file, but I have a long-running job in the background and I can't type exec zsh. Is there something else I can do in this situation?
(Sure, I could copy and paste it into a file, but it would be more logical for there to exist a flush-history command.)
To write the shell history to the history file, do
fc -W
fc has some useful flags, see them all in man zshbuiltins.
You can also fully automate reading and writing the history file after each command (thus sharing your history file automatically with each running zsh) by saying setopt -o sharehistory. Read more history-related options in man zshoptions.
I also just found:
setopt INC_APPEND_HISTORY
From man zshoptions:
INC_APPEND_HISTORY
This options works like APPEND_HISTORY except that new history
lines are added to the $HISTFILE incrementally (as soon as they
are entered), rather than waiting until the shell exits. The
file will still be periodically re-written to trim it when the
number of lines grows 20% beyond the value specified by $SAVE-
HIST (see also the HIST_SAVE_BY_COPY option).
And use
fc -R
to read in the history (after writing it) in an existing zsh shell.
appen below line to ~/.zshrc, it will save 1000 entry we increase by changing value of HISTSIZE and SAVEHIST
HISTSIZE=1000
if (( ! EUID )); then
HISTFILE=~/.zsh_history_root
else
HISTFILE=~/.zsh_history
fi
SAVEHIST=1000

Resources