Bash Ctrl + R use current text - bash

I use the reverse-i-search in Bash a lot. But I always type in the start of the command I'm about to use before I realise that I need to search for it.
Is there a way to make Ctrl + R use the input text, so that I don't have to type it again?
By input text, I mean text that I have typed in to the terminal before pressing Enter.
An example:
cd ~/some/folder/
cd some/subfolder/
I am about to go to a subfolder with cd. Before pressing Enter, I will type "cd some/subfolder". That is the input text.

You can use the CTRL-aryr command.
It looks worse than it is: for a half typed complex command it's quite helpful.
That's a compound command:
CTRL-a: home
-r: open reverse incremental history search and copy the text after the cursor position
-y: paste the text
-r: look for matching commands

It appears you can not do this. That is CTRL-R reads directly from the keyboard (at least on Linux) so you can't even paste into the buffer.
The best solution I can recommend is that you consider using Emacs Shell mode which will replace Bash's input with Emacs buffers.

Related

How do I edit current shell command without executing it?

There seems to be quite a lot of information on how to edit and execute a command using your editor using "edit-and-execute-command (C-x C-e)", but what I would like to achieve is take the current shell command, apply certain filtering (using a script) and then return it to prompt for further approval/manual changes before execution. Is this possible with bash?
Latest update based on my experience
The part 0"+y$dd in the following mapping is really something that you should carefully think about and tailor it to your taste/workflow/experience.
For instance, very frequently I've found myself ending up with multiple lines in the buffer, where I only want to execute the one the cursor is on; in this case I can use 0"+y$dd:%d<CR> instead of 0"+y$dd.
And this is just one of the possible scenarios.
Final answer for those who like vim
Set vim as your EDITOR/VISUAL, so that when editing a command line, you will use vim to edit it.
Put au BufEnter /tmp/bash-fc.* nn <Leader>d 0"+y$dd:wq<CR> in your ~/.vimrc file to map Leaderd (which you will rarely use when editing a command) to the action "delete the current line into the + register without the trailing EOL".
you can use either the + or the * register in the mapping above; the ways to paste into the terminal will likely differ; you need the +clipboard option for these registers to be available.
When finished editing a command in the vim editor, hit EscapeLeaderd.
Paste the clipboard into the terminal (this is terminal-dependent).
Original answer
I often need to do the same, and I do it as follows. (I normally use the set -o vi in bash, so points 1 and 2 in the following are different if you use set -o emacs, the default; based on your question it looks like points 1 and 2 are unified in Ctrl+x followed by Ctrl+e, which is harder to type, imho.)
hit Escape to be in normal mode,
hit v to enter the editor to edit the command,
edit the command as I like,
(This is where you ask the question.)
hit Escape0"+y$dd:wq,
Note: 0"+y$, not simply "+yy, as the latter would copy the newline too, and this would result in executing the command upon pasting it in the command line,
paste the clipboard on the command line
how to do this depends on the terminal you are using, I guess; I hit Ctrl+Alt+v in URxvt.
proceed to approval/manual edit.
Clearly this is just a workaround, consisting in copying the edited command into the clipboard before deleting the whole command, so that nothing gets executed upon exiting the editor; however it's the best I can get for myself.
Update
As my EDITOR (and VISUAL) is equal to vim, when I edit the command, I edit it in vim.
In this respect, I have noticed that the buffer is named /tmp/bash-fc.random, where random is a 6-characters alphanumeric random string.
This gives space to a lot of possiblities, if you use vim as your editor, as you can define some mapping in your .vimrc to execute the whole sequence Escape0"+y$dd:wq. For instance, one command that you'd rarely use when editing a command line is Leaderd; therefore you can put the following mapping in your .vimrc file
au BufEnter /tmp/bash-fc.* nn <Leader>d 0"+y$dd:wq<CR>
so that step 4 in the above recipe becomes
hit EscapeLeaderd
It's not possible to do that in Bash/readline but it's possible in zsh
using edit-command-line command:
darkstar% autoload edit-command-line; zle -N edit-command-line
darkstar% bindkey "^X^E" edit-command-line
Now press Control-x Control-e to open your editor, edit line, leave the editor - you will see the updated command line but it will not be executed automatically.
Now that I think about it, maybe a variation of what #kenorb suggested in a comment is the best workaround (as it seems no solution exists), if we want to stick to bash.
What you can do is prepend a # (the comment character in bash) to the command, rather than echo. Then when you exit the editor, the command will be ineffective, and you will only have to press arrow up (or k, if you use set -o vi), remove the # and confirming.
Note that this strategy adds just a few keystrokes, so it can be fairly efficient, depending on your typing level.
These pieces might get you closer:
a) replace the the normal binding for newline newline (ctrl-M)
bind -x '"\C-M":date"
b) grab the current line from the history using !#
replace date with whatever script you want.
c) edit manually
d) if necessary, somehow tie in !!:p which prints the new command to the command line but does not execute it, thus letting you manually edit it.
e) using ctrl-J submit edited command rather than a newline
or they might not ....
There is an option in bash to modify command from history without executing it. I'm not sure it it's possible to use script for this, doesn't seem to be likely. Although, you can make modifications using history modifiers.
Enable option histverify to prevent execution of modified command
Use chain of modifiers to change last command
Use "!!" to put your result to command line for final edit
Here is how it looks:
$ shopt -s histverify
$ ls *.sh
script1.sh script2.sh script3.sh script-return.sh
$ !!:s/*/script1/:p
ls script1.sh
$ !!:s/1/2/:p
ls script2.sh
$ !!
$ ls script2.sh
script2.sh
I'd like to point you to the Composure framework for Bash (I'm not affiliated with it): https://github.com/erichs/composure
It provides draft and revise functions that sound like they could help with what you're trying to do. Here's a (long) quote from the project's readme file:
Composure helps by letting you quickly draft simple shell functions,
breaking down your long pipe filters and complex commands into
readable and reusable chunks.
Draft first, ask questions later
Once you've crafted your gem of a command, don't throw it away! Use
draft () and give it a good name. This stores your last command as a
function you can reuse later. Think of it like a rough draft.
$ cat servers.txt
bashful: up
doc: down
up-arrow
$ cat servers.txt | grep down
doc: down
$ draft finddown
$ finddown | mail -s "down server(s)" admin#here.com
Revise, revise, revise!
Now that you've got a minimal shell function, you may want to make it
better through refactoring and revision. Use the revise () command
to revise your shell function in your favorite editor.
generalize functions with input parameters
add or remove functionality
add supporting metadata for documentation
$ revise finddown
finddown ()
{
about finds servers marked 'down' in text file
group admin
cat $1 | grep down
}
$ finddown servers.txt
doc: down
It does not seem possible with a keyboard shortcut, at least:
$ bind -P | grep -e command -e edit
complete-command can be found on "\e!".
edit-and-execute-command can be found on "\C-x\C-e".
emacs-editing-mode is not bound to any keys
possible-command-completions can be found on "\C-x!".
vi-editing-mode is not bound to any keys
This can be done in native bash using readline specifically READLINE_LINE and READLINE_POINT variables. I use this functionality all the time though not through vim, you would need to get the value of $selected from your vim command and if not empty it takes your original line + your input and replaces your original line with the combination without executing. output as a variable
_main() {
selected="$(__coms_select__ "$#")"
origonal_text=$READLINE_LINE READLINE_LINE="${READLINE_LINE:0:$READLINE_POINT}$selected${READLINE_LINE:$READLINE_POINT}"
READLINE_POINT=$(( READLINE_POINT + ${#selected} ))
}
bind -m emacs-standard -x '"\C-e": _main '
bind -m vi-command -x '"\C-e": _main '
bind -m vi-insert -x '"\C-e": _main '
Edit
Just remembered these two utilities that will let you do this as well.
Vipe allows you to run your editor in the middle of a unix pipeline and edit the data that is being piped between programs.
vp, up, vipe, Neomux (upgrade of nvim terminal) you can do some pretty neat throwing buffers between the terminal and split window.
and Athame (full vim on the command line)
https://github.com/ardagnir/athame
careful with that one though plugins work on the cli and it can get funky if you got tons of plugins

Shortcut to jump to the previous white space in a bash command line

To jump to the previous word in a command line, I use Alt + b.
However, the names of my files are pretty long and look like this:
2018_09_03_abcdef_ghijkl_mnopqr_stuvwx_yz.txt
When I want to change the name of these files, I use the command mv and a shortcut** that permits me to paste the first word/argument (the current name of the file). This gives me the following command:
$ mv 2018_09_03_abcdef_ghijkl_mnopqr_stuvwx_yz.txt 2018_09_03_abcdef_ghijkl_mnopqr_stuvwx_yz.txt
Then I either want to change the date of the day and/or the first letters of the file name to get for example the final command line:
$ mv 2018_09_03_abcdef_ghijkl_mnopqr_stuvwx_yz.txt 2018_09_04_ABcd1234_ghijkl_mnopqr_stuvwx_yz.txt
To make the change at the beginning of the file name, I have to type the shortcut Alt + b several times since this shortcut considers every letters separated by an underscore as a word.
I would like to be able to jump directly to the beginning of the name (not the beginning of the line) to modify it. A shortcut targeting the white space would be ideal.
I have not been able to find such a shortcut that would skip the underscores and go directly to the previous white space.
Did anyone already create a shortcut in bash that allows you to do this?
Would the only possible way to accomplish this be to switch from emacs mode to vi mode (set -o vi) and use the vi shortcut:
F + space
?
Thank you very much in advance!
** The shortcut I found to paste the previous word in the current command line and paste it in the same current command line uses Alt + j and has to be added in the '~/.inputrc' file (followed by bind -f ~/.inputrc):
"\ej":"!#:$\e^"
There are several shortcuts to navigate bash command line. Here's another list.
Assuming cursor is at the end of the line, it could jump to 03 (day part) by typing Meta+7 Meta+b (2 keystrokes without releasing Meta key).
Now, for the file name changes, brace expansion could be used to get a command like this that provides the second argument from expansion.
mv 2018_09_{03_abc,04_ABC}def_ghijkl_mnopqr_stuvwx_yz.txt

Jump to string in current line while using Bash in VI mode

While navigating in BASH using VI mode, I can jump back to a specific character (e.g. '-') of the current command line via the following command:
F-
How can I jump back to a specific string (e.g. '--path') in the current command line of BASH? I know navigating in VI but I did not understand how to perform regex search in current command line of BASH.
According to here, what you want doesn't seem possible. The ?word and /word bindings search in command history rather than in the current command line.
But in vi mode, you can press ESC v to open the current command line in an editor. Then you can edit & save the command and it will be executed (source).
Of course, as pointed out in nur-sh's answer, you can simply keep pressing B to get to the word.
You could use the find command which searches backwards from where you are
?word
or you could keep pressing B to get to the word
(this command goes back one Word at a time).

copy and paste in vi

I don't have a huge amount of experience using VI. I am running it on Mac OSX.
I've copied and pasted text before in the editor using (when I say gui in the following I mean the Mac OSX gui)
Cursor to highlight and copy i using command C or the gui or the yy command in VI.
Entering insert mode where I want to paste the text and then pasting using command V or the gui
My problem is that a very long line that is split over multiple lines in the terminal becomes multiple lines as shown on the terminal when copied and pasted by any of the methods.
How do I get it to copy and paste excatly as is?
Move the cursor to the line from where you want to copy and paste contents at another place.
Hold the key v in press mode and press upper or lower arrow key according to requirements or up to lines that will be copied. you can press key V to select whole lines.
Press d to cut or y to copy.
Move the cursor to the place where you want to paste.
Press p to paste contents after the cursor or P to paste before the cursor.
You have
:set paste
Put Vim in Paste mode. This is useful if you want to cut or copy
some text from one window and paste it in Vim. This will avoid
unexpected effects.
Setting this option is useful when using Vim in a terminal, where Vim
cannot distinguish between typed text and pasted text.
Assuming your vi is actually vim, before pasting, do:
:set paste
That disables word wrapping and auto-indent and all similar things that modify typed text. After pasting, turn it off again with
:set nopaste
The reason is that while gvim can tell pasting from typing (so you don't need this when using gvim), the terminal version can't, because it's the terminal doing copy and paste and vim simply sees the text as typed. And therefore applies the transformation like it does for any other text.
Someone showed me a neat trick. In the vi editor, set to insert mode ("i"). Then middle-button click at the location where you would like to insert.
Well, if it is actually one long line the easiest way to do this is with a 'Y' in command mode. Just move to the line and do Y and then move to where you want to put the line and do a p (for paste).
After trying everything suggested here and in other answers including trying to format the file afterwards with vi(m) commands and seds, I got smart and just heredoc'd what I wanted to paste with redirection to the file. i.e.
cat << EOF > yourfile.txt
paste what you are trying to paste
another line of pasted text
yet another
foo > bar?
foo = bar??
end of the file(yay)
EOF
everything between the first line and the last line will be pasted to your file without those pesky newlines interpreted as many spaces/tabs. Just beware that what you are pasting might have its own heredoc(like mine did coincidently LOL). In that case would need to manually paste those lines in your editor which shouldn't be an issue.

In bash, how does one clear the current input?

Suppose in bash you start writing a command like:
$ rm -rf /foo/bar/really/long/path/here
and then realize you don't want to execute this after all. Is there a way to clear the input with one or two keystrokes?
What I have been doing lately is prepending echo and enclosing the input in quotes (Ctrl+A, echo ", Ctrl+E, ") then hitting enter. Is there a faster way?
Press Ctrl-U to delete everything before the cursor. The deleted command will be stored into a buffer. Press Ctrl-Y to paste the deleted command.
(Optional: Press End or Ctrl-E to jump to the end of the input first.)
Alternatively, press Ctrl-C to abort what you're typing.
Try Ctrl+U. That clears the input line.
Found a short reference at http://www.ice2o.com/bash_quick_ref.html while searching.
ctrl + e (if not at the end of the line) plus ctrl + u will do it.
Ctrl-U, Ctrl-K does the trick as well.
Ctrl-U deletes everything from the beginning of the line up to the cursor, Ctrl-K deletes everything from the cursor to the end of the line. (It is sometimes useful to use only one of them.)
There are two options to do this
ctrl+c - this clears the whole line, no matter where the cursor is.
ctrl+u - this clear the line from the position of the cursor until the beginning.
A nice shortcut is pressing Esc#. It will prepend a # character (thus making the line a comment) and then press enter. If you then decide that you still the need the command, you still have it in your history :)
Pressing Esc plus Backspace in bash will clear everything up to the cursor's position.
(In Cygwin, this will clear the input up to the next word. Words are separated by spaces, underscores, ...)
This is an expansion of knittl's answer that stores the line in the console history by prefixing with a hash. Overcoming drawbacks of the clipboard, such as accidental overwriting or being unable to view the cut line for reference.
Comment Line & Return New Prompt
Use either key shortcut:
Esc,#
Alt+#
A hash character # will be prepended to the line, thus turning the whole line into a comment. It will also return a new prompt, as if enter was pressed by the user. e.g.
$ #rm -rf /foo/bar/really/long/path/here
$
Retrieve Commented Line
To recover the old line from console history use one of the following shortcuts:
Up
Ctrl+p
Repeat key shortcut until the desired line appears.
Quick Hash Prefix Removal
To remove the line's hash # prefix there are a few different options available:
Remove first character and immediately execute command:
Esc,1,Esc,#
Alt+-, Alt+#
Move cursor to start and remove first character, without executing the command:
Home, Delete
Ctrl+a, Ctrl+d
Consider that using Ctrl-U (or Ctrl-E and then Ctrl-U) will store what you clear in a buffer so that you can then paste it later using Ctrl-Y.
If you are using Bash in vi mode (set it with set -o vi), then press Esc to switch to the normal mode of vi, and type dd to delete the current line!
To delete the current line, try:
Ctrl-X, Ctrl-U
As an alternative you may use:
Esc-D
which requires in ~/.inputrc:
"\ed": kill-whole-line
see: http://codesnippets.joyent.com/posts/show/1690

Resources