combine history across tty - linux

At any given time I have 3 logins to the same server. Sure, I often use screen, but assume this purpose allows me to have:
1 session running something
1 session looking at output
1 session shuffling files to/from various places
If at any point I lose all three connections, I lose 2/3 of my history, as when I log back in, I get the history from a random 1 of my three connections.
Is there a way to stop this and combine all three history files into 1?
Alternatively, is there a way to declare each login as "ttyN" thus keeping each with its separate history separate, but retrievable / re-connectable?
Thanks!

Possible solution?
After you have opened your Terminal /Screen
start your shell with a History-File setting:
HISTFILE=$HOME/session1-history bash
and continue to work with this bash.
To run a different session history
HISTFILE=$HOME/session2-history bash
etc.

Just add this in your .bashrc file.
# Avoid duplicates..
export HISTCONTROL=ignoredups:erasedups
# Append history entries..
shopt -s histappend
# After each command, save and reload history
export PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND$'\n'}history -a; history -c; history -r"
Found this answer at this Unix Stackexchange post.
I tried this and it seems to work on multiple terminals simultaneously, only catch is you've to execute a command for the terminal to fetch the latest history from .bash_history.
So for example, if I open the first terminal and type echo check 1 and then open a second terminal and type echo check 2. Now if I go back to the first terminal and press the up key, I won't get echo check 2, since the last time the history was fetched from the history file was when I executed the previous command. So I can just press Enter without specifying a command and it will fetch the last history entries. Now if I press the up key, it will show up echo check 2, which is the expected behavior.

There is an open source shell history logger for bash and zsh which would solve your problem (disclaimer: I wrote and maintain it).
https://github.com/barabo/advanced-shell-history
The idea is that your commands are written into a sqlite3 database using a builtin bash hook. If you really needed to go back and figure out what commands you entered / how long they ran / what their exit codes were - you should give it a try.
It's saved me many times.

There are numerous options you can set to prevent Bash instances from overwriting each others history. See this FAQ for a full explanation:
In your .bashrc, add
HISTFILESIZE=400000000
HISTSIZE=10000
PROMPT_COMMAND="history -a"
export HISTSIZE PROMPT_COMMAND
shopt -s histappend

If I've understood your requirements right you have multiple terminal sessions open and want commands entered in one session to be available via history in other sessions.
You need to put these lines on top of your ~/.bashrc:
export HISTFILESIZE=100000
export HISTSIZE=100000
# avoid & erase dups
export HISTCONTROL=ignoredups:erasedups
shopt -s histappend
# save & reload history after each command enter in shell
export PROMPT_COMMAND="\history -a; \history -c; \history -r; $PROMPT_COMMAND"
Most important is histappend which makes sure that new history lines are appended to the history file each time.
PS: You need to press enter to see most recently added history in each session.
For more robust solution see: http://ptspts.blogspot.in/2011/03/how-to-automatically-synchronize-shell.html

Related

Check Previous Output in Google Cloud Console, is it possible?

I'm trying to view all commands I have entered and their outputs. I understand that I can use 'history' to view all of my recent commands. However, is there a way I can check for previous outputs of those commands? I have tried looking it up and there does not seem to be a way.
It only stores history of the commands you ran (which you can retrieve by typing history). Unless you already have set the scroll-back to a very high number, there is no way to see the outputs that are older than the set value of scroll-back. Also setting this value to a very high number will make your scrolling sluggish since the lines are stored in the memory. Cloud Shell uses Tmux, which has a scroll back buffer of 2000 lines by default.
To store your future commands and their outputs, there are few options:
Using screen
Start a screen session by entering screen. Once you are inside ‘screen’, press Ctrl-a, then :, then enter log. All the I/O will be captured in screenlog files in the directory where you started the screen command.
Using script
You can start by typing script. A script session will start that will capture all the I/O to a file named typescript. You can exit the script session by Ctrl-d and view the logs in the typescript file.
Using tee
tee is a handy tool. You can do something like this:
$ tmux | tee log.txt
This will open a new bash shell inside the one you are already running. When you exit out of this, you can see the outputs in the file called log.txt
Other ways
As Dustin Kirkland suggested in this post, you can also use byobu. Although, I have never used terminal screencasting tools such as Shelr also sounds like an option.
Other ways
Cloud Shell commands to run and a link that explains more. This will exit the terminal and thus delete all the scroll back buffer, resetting it to empty, but in the future it will save 5000 lines rather than 2000.
$ tmux show-options -g | grep history
history-limit 2000
$ echo "set -g history-limit 5000" >> ~/.tmux.conf
$ exit
$ tmux show-options -g | grep history
history-limit 5000
If you type…
$ man tmux
… you can find the documentation for this setting (search by typing '/history-limit' + (enter)).
‘history-limit lines’ set the maximum number of lines held in window history. This setting applies only to new windows - existing window histories are not resized and retain the limit at the point they were created.

Bash History Scope

Defined in .bashrc
function gitpullsite(){
echo "Enter GIT username";
read gituname;
echo "Enter GIT password";
read -s gitpword;
giturl=https://$gituname:$gitpword#github.com/whateveruser/whateverrepo.git
repopath=/home/whateveruser/html/whateverrepo/;
sudo rm -r $repopath;
sudo git clone $giturl $repopath;
}
and then run in terminal gitpullsite I want to remove gituname and gitpword from history but I cannot find such entries in history anywhere. I have no process in place to remove such entries so where is this history stored ? Is it ignored as within the scope of the function and not the shell ?
What I don't want is an unknown trail of usernames and passwords stored somewhere I don't know about - an obvious security issue should a system be compromised.
The aim is to add further different 'nested' repos but the user only needs to enter credentials once.
You may want to try https://github.com/dvorka/hstr which, in addition to history management i.e. deleting particular command(s) from history, allows for "suggest box style" filtering.
It can be easily bound to Ctrl-r and/or Ctrl-s
One possible solution would be to not log part of your commands at all.
Multiple ways to do so :
1
To stop logging bash history is:
set +o history
and to reset, that is to start logging again:
set -o history
2
You can also use add ignorespace to your HISTCONTROL environment variable. Then any command line that begins with a space won't be entered into your history.
methods for avoiding bash history logging
Can you prevent a command from going into the bash shell command history?

linux history command

If I type history command, I can view only 1000 lines. I want to view more than that. It will be nice If I can view all commands typed between certain dates or months.
Is this possible? If so, how to do this?
You want to set the bash variables HISTSIZE and HISTFILESIZE in your .bashrc.
HISTSIZE determines how many comands are retained in history. HISTFILESIZE determines how many lines of history are stored in the file in which they are persisted, usually .bash_history.
add some commands to .bash_logout to organize your history files
e.g.
today=`date +%y%m%d`
cp .bash_history >.bash_history_$today
echo >.bash_history # to clear out file
However, one problem: .bash_logout doesn't seem to run in a lot of X-based environments. On a ssh account, such as with a virtual server, it works fine.

Bash Command Logger

I was wondering, out of curiosity, if it is possible to code a bash script logs all the command run in a Bash/SSH session. I know history is suppose to log all the commands run but it seems to be very unreliable!
I have been messing about this morning and came up with the following bash script which does log what the user runs in the terminal but does not run all the commands correctly.
prompt_read() {
echo -n “$(whoami)#$(hostname):$(pwd)~$ “
read userinput
}
prompt_read
while :; do
if [[ $userinput != exit ]]; then
logger "logit $userinput"
bash -c "$userinput"
prompt_read
else
kill -1 $PPID
fi
done
Is anyone aware of anything that logs commands better and more reliably than history
Cheers
The reason why history seems unreliable to you is because it only writes to history at the end of a BASH session, so you could lose commands.
I have a few things in my bash profile:
HISTFILESIZE=10000 # how many lines of history to store in the history file
HISTSIZE=10000 # how many lines of history to store in a session ( I think )
HISTCONTROL=ignoredups # ignore duplicate commands
shopt -s histappend # append history, rather than having sessions obliterate existing history
PROMPT_COMMAND="history -a;$PROMPT_COMMAND"
The last few are the important ones, setting your PROMPT_COMMAND with history -a will make history append immediately, rather than post-session. And setting shopt -s histappend will make bash sessions append to the history file, rather than overwrite existing histories.
Some more info: http://linuxcommando.blogspot.com/2007/11/keeping-command-history-across-multiple.html
Additionally, if this is useful to you, you can change the name of the history file you use for a particular bash session with the HISTFILE environment variable.
Check out script: http://bashshell.net/commands/using-the-script-command/
It records everything that appear on the terminal to a file, and iirc it can play back the recorded session.
You can set this as the user's shell to make it record everything upon login.
You can find a script here to log all 'bash' commands/builtins into a text-file or a 'syslog' server without using a patch or a special executable tool.
You can also write directly without syslog to a logfile.
It is very easy to deploy, as it is a simple shell script that need to be called once at the initialization of the 'bash'.

Bash: call script with customized keyboard shortcuts?

Lets say I have a script, "myscript.sh", with contents being simply echo $PWD. I'd like to bind somehow this script to a key combo in bash (gnome-terminal) - so that when I press this key combination, the output of "myscript.sh" is inserted ("pasted") at the cursor position in the terminal.
Apparently, bash history and line manipulation is handled by readline - and the references I got for bash keyboard shortcuts, do reference readline:
bash keyboard shortcuts
Bash Reference Manual: Bindable Readline Commands
I've also seen in Bash Reference Manual: Readline Init File Syntax that the key bindings for bash can be listed by using bind -p (see help bind [not 'man bind'] for more). So maybe this question would better be titled as "_binding macros to custom keyboard shortcuts in readline" :) But in any case, is what I want possible to do?
I guess an alternative would be to have the script be something like "pwd | xsel -b", and then I call it on terminal - and I can paste afterwards; but I'd still like a single keyboard shortcut instead, say like Ctrl-Alt-H (which seems to be not used for anything), which will immediately insert/paste script output when pressed.
Thanks in advance,
Cheers!
EDIT: Just to clarify - here is my use case where I'd like this facility. I'm usually cd'd in a project folder, usually named something like myproject-folder-0012a, which is under revision control by svn. And there is a bunch of these folders. So quite often, I do commits where the first word of the message is the directory name, as in:
svn ci -m "myproject-folder-0012a: here a commit message"
But that is what I don't like - first I type 11 characters, which go rather fast:
svn ci -m "
And then, I cannot use autocompletion to get the name (i'm inside the folder) - which means I either have to fully type it (no way :)), or I copy paste it from the prompt (which requires selection - press mouse, drag, release mouse; then Ctrl+Shift+C, and then Ctrl+Shift+V, plus any left/right keys if I miss allignment - plus deletions and such if I make the copy wrong).
Meaning - so much work, just to get the bloody folder name for a bloody commit message :( I'd MUCH rather press something like (say) Ctrl-Alt-H, and have the folder name automatically inserted at cursor position, and be done with it :)
My suggestion for xsel is only because I could put it into a "global" script - say symlink it as /usr/bin/myscript (and obviously, the contents of the script are echo $(basename $PWD) rather than just pwd for my needs), and then I could do:
$ myscript # this puts directory name in clipboard
$ svn ci -m "[CTRL+SHIFT+V TO PASTE HERE]myproject-folder-0012a[NOW TYPE]: here a commit message"
... which sort of makes the workload less, but still - then I have to remember what the script name is, and call it, before I type the svn command (and I don't always remember that)... And still - I have to call a command, and then press a key combo; why shouldn't I just press a key combo once, and be done with it ??! :)
Well, hope this clarifies my problem a bit better ....
EDIT2: However, another reason why a bash keyboard shortcut would be useful, is that then I could also "paste/insert current directory name" not only in shell commands - but also in terminal programs, say like nano (where it would, arguably, be more difficult to use bash script or function expansion directly).
Simple version:
This command at a shell prompt:
bind '"\ee": "${PWD##*/}\e\C-e"'
or this line added to your ~/.inputrc:
"\ee": "${PWD##*/}\e\C-e"
will cause Alt-e to insert the basename of the current directory on the command line. It requires that the default binding of the readline function shell-expand-line which is \e\C-e be present (this could be adapted if it's different). I'm also making the assumption that you're using Bash's emacs mode.
Unfortunately, it causes things that have already been typed to be expanded as well. One of the affects of this is that after having typed:
svn ci -m "
and pressing Alt-e, the quotation mark will have disappeared. There are a couple of ways to deal with this.
One, assume that all you'll lose is the quote and either manually add it back or have the readline macro add it for you:
bind '"\ee": "${PWD##*/}\e\C-e\eb\"\C-e"'
which just isn't very satisfactory.
Advanced version:
Or, two, kill the line, do the insertion, then yank the line back:
bind '"\ee": " \C-u \C-a\C-k${PWD##*/}\e\C-e\C-y\C-a\C-y\ey\b"'
or
bind '"\ee": " \C-u \C-a\C-k${PWD##*/}\e\C-e\C-y\C-a\C-y\ey\b\ef\C-f"'
This leaves the rest of the line intact (nothing else is expanded or deleted), but it uses the kill ring, so it may leave it in a state that's different than you expect (if you're using it). It also inserts a space after the inserted directory name (the spaces in the macro are used to ensure that older kill-ring contents are not regurgitated if the macro is executed at the beginning or end of the line). The macro should work regardless of the position of the cursor in the line. The insertion will be made at the cursor's position, leaving the cursor in the same position [in the first version].
Edit: The second version leaves the cursor after the dirname and space that are inserted.
Edit 2:
The readline function shell-forward-word (unbound) does a better job than forward-word (\ef) for this. You can make use of that like this:
bind '"\ew":shell-forward-word'
bind '"\ee": " \C-u \C-a\C-k${PWD##*/}\e\C-e\C-y\C-a\C-y\ey\b\ew\C-f"'
By the way, you should know that Bash keyboard shortcuts are not active in other programs such as nano.
Ok, not really an answer, but I'd just like to summarize the comments I got so far, which are useful for my problem. However, the question as it stands - in respect to bash keyboard shortcuts running arbitrary scripts - is still not answered (I'd still prefer doing all this with a single key combo :))
First, I can use a 'global' script like:
$ sudo bash -c 'cat > /usr/bin/bpwd <<EOF
#!/bin/bash
basepwd=\$(basename \$(pwd))
echo -n \$basepwd # suppress line ending
# exec 1>/dev/null # debug: redir stdout to null
echo -n \$basepwd | xsel -i -b # suppress LF, and make xsel read from stdin
# exec 1>/dev/tty # debug: restore stdout
EOF
chmod +x /usr/bin/bpwd'
Or, I can add bash functions to my .bashrc (note: make sure you reload bash after you add these lines to .bashrc - for example, simply by typing bash in your current terminal):
$ echo '
bpwd2() { basepwd=${PWD##*/} ; echo -n $basepwd | xsel -i -b ; echo -n $basepwd ; }
svnci-test() { echo -n "$(bpwd2): $*" ; }
svnci-m() { svn ci -m "$(bpwd2): $*" ; }' >> ~/.bashrc
Basically, I misunderstood Reese Moore's suggestion originally - you can indeed use backticks - consider this command session (after the above commands have been ran):
$ bpwd
Desktop\
$ bpwd2
Desktop\
$ echo `bpwd`
Desktop
$ echo "`bpwd2` 2"
Desktop 2
This is what I needed to understand Moore's "the output from the backticked commands will be used as input on the executed command" (however, one also needs to take care to clean the line endings from the output); or, in my case, I can call
svn ci -m "`bpwd`: my message here"
# svn ci -m "${PWD##*/}: my message here" # alternatively
... or, I could follow camh's suggestion, and use svnci-m as a function (in my case, I almost never use additional arguments to svn ci, and so my version is slightly different). And to test whether arguments are passed correctly, I can use the svnci-test function:
$ svnci-test "my message"
Desktop: my message\
Thanks for the comments so far,
Cheers!
One way to do what you want with a single key press is to take advantage of programmable completion in bash. You possibly have some programmable completion set up with the bash_completion tool/package. If not, look into that to see the specifics of how it is done.
The idea is to have the programmable completion recognise when you have hit at the start of a svn commit message and then have it return a single completion which is the text you want to insert (the basename of the current directory).
I've only dabbled with programmable completion so I can't give you the details, but the above-mentioned bash_completion package or the subversion completion script may be a good start.

Resources