How do I check if nothing was typed in the previous Zsh prompt - linux

In the zsh prompt, I have a custom script to display the amount of time the last command ran for.
I've got this working, but how do I check if the user entered nothing and just pressed "Enter" or "Ctrl + C", in the previous prompt?
In this case, I do not want to show the amount of time the user spent idle, as this is not useful information for me.
$history[$((HISTCMD-1))] doesn't appear to work in this case. If the user typed nothing and just hit enter, it will just show the last command that was run, and not be an empty string.

If you define a function with the name zshaddhistory, it will be invoked every time enter has been pressed. $1 is the command being entered, and this can also be an empty command. You could set a global variable to indicate that an empty line has been entered, and use this variable later on.
Be careful with the return value from this function: If it returns 0, the command is saved into the history; otherwise it is not. You find more on this in the zshparam man-page, where _HISTORY_IGNORE_ is described, because the main use of this function is to control whether or not a command should be added into the history. Hence, an alternative would be to add empty lines to the history too and use your original approach with $history to access it. This has the disadvantage that seeing empty history entries is not really user-friendly.

Related

VIM return to scrolling after "Type number and <Enter> (empty cancels)"

Some times you have a long list of options and you have to scroll to the end or type q to get prompted with: "Type number and Enter (empty cancels)"
Is it possible to return back to scrolling? Some times the list is long and I want to see my options again before making a decision. Especially since you exit to the "Type number..." without warning. Or the only option after reaching this point is to exit and enter again?
Is it possible to return back to scrolling? Some times the list is long and I want to see my options again before making a decision. Especially since you exit to the "Type number..." without warning.
Unfortunately, you cannot get back to scrolling. The pager is very minimal and doesn't have this option, in fact it supports little to no customization. See :h pager or :h more-prompt to familiarize with the navigation commands inside a pager, which to some extent will help mitigate this.
Or the only option after reaching this point is to exit and enter again?
Yes, but there are faster ways that doesn't require you to enter the entire command again. Meet #: and g<
#:
typing #: in normal mode re-executes the last command you ran. This means, you can get back to the top of the pager in 3 keystrokes (<cr>#:) after you reached the end of the pager and in the colon prompt.
g<
Less useful in your specific use-case, but this is useful when you hit enter accidentally. Typing g< right after you hit enter in the colon prompt, will take you back to the end of the pager, with option to enter a number again.

Linux command line issue with prompt

I started to manage a new server based on CentOS. I wanted to change the prompt, so I wrote the following command :
PS1="\e[0;36m[`pwd`]\$\e[m "
It worked perfectly. But since I got an annoying issue. When I write something quite long, or displays an older command that is quite long using up arrow, or paste it, and then click on "home" to get to the top of the line, the cursor stops within the command, 10 characters ahead of the prompt. For example, lets say I write this :
[/]$ git log --pretty=oneline
And then click on home button, the cursor will stop on the "p" letter, after "--". And if I try to move with left key to get on top of the line, it does that annoying bip saying "you are already there, mate"...
Now, lets say I wrote
ls
and right after
git log --pretty=oneline
If I go up two times, the prompt displays this :
[/]$ git log --ls
And if I keep going up the "git log --" never goes away. Now if I press enter, it will still launch the ls command without any problem. Seems like it's just a display issue. But still, that confuse me all the time...
Thanks ahead for your help !
Use \w to print the working directory in your shell prompt, rather than trying to embed a command.
You also need to escape the escape sequences so that bash doesn't attempt to count them as printed characters. This is done by enclosing them with \[ and \].
So you should end up with something like:
PS1="\[\e[0;36m\][\w]\$\[\e[m\]"
Your prompt is also very compact, you may want to stick some spaces in it. The key is that you have used the brackets to escape the non-printable characters.
You can find a complete list of these substitutions in the PROMPTING section of the bash man page.

Using bash history to get a previous command, copy it and then 'run' it but with the command commented

Just a question to improve my bash skills. I always do this:
$ history | grep some_long_command
...
...
123 some_long_command1.........
124 some_long_command2.........
...
I can then run the command the command I found by doing:
!123
However, I often want to do this:
some_long_command1foobar
I.e. change the command before I run it. Can you use bash to run this command instead:
#some_long_command1
so it gets commented.
Then I don't have to use my mouse to highlight the command, edit it and then run it (I can just use the keyboard - faster).
I suppose I could write a script to do it but there might already be functionality built in somewhere....?
I'd suggest instead of using the history command, you use ctrl+r and start typing that command. When you press an arrow key as if to go to modify it, it will drop out of autocomplete recognition, and will let you edit before running.
UPDATE: also, if you want to cycle through the different commands that contain the string you just typed, keep on pressing ctrl+r
Actually, you can just append :p to the command to print it without actually running it. For example:
$ ls -la
$ !!:p
Will print out ls -la as the previous command without running it, and you can just press ↑ (up) to find it and edit it.
You can also do
!123:p
to print out the 123rd command as your previous command.
You can also try fc command to edit the command in the history.
WIKI says,
​fc​ is a standard program on Unix that lists or edits and reexecutes,
commands previously entered to an interactive shell. fc is a built-in
command in the bash shell; help fc will show usage information.
Apart from reverse-incremental search(Ctrl+R), we have some more bash shortcuts:
From man bash:
previous-history (C-p)
Fetch the previous command from the history list, moving back in the list.
next-history (C-n)
Fetch the next command from the history list, moving forward in the list.
beginning-of-history (M-&lt)
Move to the first line in the history.
end-of-history (M->)
Move to the end of the input history, i.e., the line currently being entered.
reverse-search-history (C-r)
Search backward starting at the current line and moving 'up' through the history as necessary. This is an incremental search.
forward-search-history (C-s)
Search forward starting at the current line and moving 'down' through the history as necessary. This is an incremental search.
non-incremental-reverse-search-history (M-p)
Search backward through the history starting at the current line using a non-incremental search for a string supplied by the user.
non-incremental-forward-search-history (M-n)
Search forward through the history using a non-incremental search for a string supplied by the user.
yank-nth-arg (M-C-y)
Insert the first argument to the previous command (usually the second word on the previous line) at point. With an argument n, insert the nth word from the previous command (the words in the previous command begin with word 0). A negative argument inserts the nth word from the end of the previous command. Once the argument n is computed, the argument is extracted as if the "!n" history expansion had been specified.
yank-last-arg (M-., M-_)
Insert the last argument to the previous command (the last word of the previous history entry). With an argument, behave exactly like yank-nth-arg. Successive calls to yank-last-arg move back through the history list, inserting the last argument of each line in turn. The history expansion facilities are used to extract the last argument, as if the "!$" history expansion had been specified.
shell-expand-line (M-C-e)
Expand the line as the shell does. This performs alias and history expansion as well as all of the shell word expansions. See HISTORY EXPANSION below for a description of history expansion.
history-expand-line (M-^)
Perform history expansion on the current line. See HISTORY EXPANSION below for a description of history expansion.
insert-last-argument (M-., M-_)
A synonym for yank-last-arg.
operate-and-get-next (C-o)
Accept the current line for execution and fetch the next line relative to the current line from the history for editing. Any argument is ignored.
edit-and-execute-command (C-xC-e)
Invoke an editor on the current command line, and execute the result as shell commands.
!123:gs/old/new/
Will run command 123 replacing the string 'old' with the string 'new'.
You can get to edit mode by hitting M-^ (option-shift-6 on a mac).
Type this:
!123M-^
And you'll be editing command #123. It's sort of like using ctrl-r, but starting with exclamation-point syntax.
Instead of using the history command, bind history-search-backward/history-search-forward to key shortcuts which can be remembered easily (I prefer PgUp/PgDown). To do that, put this into your .inputrc file:
"<key code>": history-search-backward
"<key code>": history-search-forward
To get <key code>, type Ctrl-V <key> in the shell, and replace the starting ^[ with \e in whatever was output.
After this is set up, you can just type some and press PgUp to get some_long_command. If you need some_long_command with_some_arg but there is a similar command some_long_command with_some_other_arg later in the history, you can cycle through until you reach it by typing some and then hitting PgUp repeatedly, or you can type some, hit PgUp, move the cursor to where the two commands start to differ, type a few characters and hit PgUp once more. This ability to quickly page through / differentiate between similar commands makes it in my opinion a much more comfortable tool than Ctrl-R.
You can also put
shopt -s histverify
in your .bash_profile, which causes any history expansion to appear on your command line without running it, allowing you to edit before doing so.
You may wan to try "suggest box"-like history https://github.com/dvorka/hstr - it reads Bash history and allows for quick navigation.
To get the last command simply type hh, navigate to the command and use right arrow to get it on command line (where you can edit it and/or add comment).
^p to get the last typed command in unix/solaris
Put
alias r='fc -s'
in your .bashrc (home dir)
then you can just type in
r <whatever>
at the command prompt and you will execute a copy of the last <whatever> command (same params) that is in your history. just hit up arrow to see what you have executed if you feel the need.

The `:bar` in vim doesn't work as expected

After reading the vim doc about *:bar* *:\bar*:
'|' can be used to separate commands, so you can give multiple commands in one line.
I tried to clear history via this command:
:set history=0 | set history=20
I expect that, these two commands should be executed one by one, and the history should be cleared.
But it didn't. The history still there, and a new history was added.
Then I tried:
:set history=0
:set history=20
It works.
Why?
When you execute a command line that is what happens:
History is initialized (truncation happens here) (init_hist() function call in getcmdline()).
String to be executed is obtained (in getcmdline()).
It is added to history (add_to_history() call in getcmdline()).
It is executed (do_one_cmd() in do_cmdline(), happens after getcmdline() call (called by getexline() passed as fgetline argument to do_cmdline())).
Setting the option itself does nothing, it only changes p_hi variable. Actual modification of history is done at step 1.
Now let’s see what happens in both cases (assuming that you typed second or third additional command):
History is first initialized when p_hi has old value, then it is changed two times and then when you do next command it is already set to 20.
History is first initialized when p_hi has old value, then it is changed once and initialized for the second time, being effectively truncated at this step and only then the value changes again. By the time you start typing third command it is already wiped out.
Thus your assumption (commands are executed one by one) is true, but you have mistaken at which point history modification happens. I would rather suggest using histdel() as #Michael advised because, at first, it clears history by itself and, at second, it does not have hardcoded 'history' value. I can’t say how you live with just 20 lines long history (I’ve set it to 65535 and am always sure I won’t lose anything small, but useful), but hardcoding this value will make changing history size to something else more painful.

How to disable editing my history in bash

In bash, when I go back in history, edit some command and run it, this edited command is appended to history and the original one is left intact. But every once in a while I somehow manage to affect the original command, i.e. my edit replaces the original command back in history. I can't put my finger on how this happens.
Can someone explain? My goal is to avoid this, so any edit to a previous command always gets appended to history and never replaces the original.
I somehow manage to affect the original command, i.e. my edit replaces the original command back in history.
Right. If you go back in your history and edit the line without pressing return to execute the command but instead moving to another history entry, you've just edited the history entry. If you then list your history, you will see a * on the line indicating that you edited it. I find this "feature" immensely frustrating. Others have provided good examples of how to reproduce this.
My goal is to avoid this, so any edit to a previous command always gets appended to history and never replaces the original.
I too wanted to disable it. I found the solution via this answer over on unix.stackexchange.
To summarize, you need to enable the revert-all-at-newline readline setting which is off by default. If the setting is on then bash will revert any changes you made to your history when you execute the next command.
To enable this setting in your shell, you should add the following to your ~/.inputrc file and then restart your shell:
$include /etc/inputrc
set revert-all-at-newline on
The first line is needed because I guess that if you supply your own .inputrc file the default /etc/inputrc file is not included which is probably not what you want.
If you go back to some previous command and edit it, but then DON'T execute it (instead using history commands to go to some other command and execute it), then the edits will remain there in your history list.
Pressing Ctrl + C, after editing, counters this behaviour. It leaves the original command in tact i.e. it cancels remembering edits to the original.
Here's my own answer, please correct or provide more details if you can.
When the "vi" option is set in bash ("set -o vi" -- "Use a vi-style command line editing interface"), there are two modes of editing a command from history.
The first mode (let's call it "basic") is when you start editing immediately using Backspace, Del and character keys.
The other mode is the "vi mode", entered when you hit Esc.
If you want to keep your history intact, DO NOT use both modes in the same edit. I don't know how bash works exactly, but you can think of it this way:
Entering the "vi mode" applies any changes done in "basic mode" to the original command, and creates a copy of the command that you can edit further using vi-style commands.
The changes get applied when you hit Enter (execute), Up, Down or j,k (move to another command in history).
The changes do not get applied if you hit Ctrl-C.
Using either basic or vi-style editing ALONE does not affect the original command in history.
What do
echo $HISTCONTROL
echo $HISTIGNORE
give you?
Edit:
I was able to reproduce behavior similar to what you've seen by following these steps:
At the shell prompt, enter:
echo abcd
echo efgh
Press up arrow twice, so "echo abcd" is shown
Press 1 to add that character at the end
Press escape to enter command mode
Press left arrow twice so the cursor is on the "c"
Press x to delete the "c"
Press enter
Now as you step back through history, you'll see a new entry at the end:
echo abd1
and the entry that previously had "echo abcd" will now look like this:
echo abcd1
This is one way, I'm sure there are others.

Resources