Key binding to go up by one directory (in Bash) - linux

In Zsh, I have a key binding to go up by one directory (very useful):
# C-M-u: up-directory
up-directory() {
builtin cd .. && zle reset-prompt
}
zle -N up-directory
bindkey '\e\C-u' up-directory
It's very nice. So nice that I would like to get it as well in my Bash config.
How can we do that?

In case someone still need this:
This binds cd .. to F2. Keeps the currently typed command and throws an updated prompt.
bind '"\eOQ": "\C-a\C-kcd ..\C-m\C-y"'
How it works:
\eOQ is F2 on my terminal
C-a beginning-of-line
C-k kill-line
cd ..
C-m accept-line
C-y yank <- this restores the killed line
However the cd .. command appears in the screen. If it bothers you, use this instead:
bind -x '"\201":cd ..'
bind '"\eOQ": "\C-a\C-k\201\C-m\C-y"'
\201 is non existent key, an empty slot we can use.
Note1: It doesn't save the cursor position, after F2 the cursor lands on the end of the line, which is a little limitation.
Note2: The original was: bind '"\eOQ": " \C-a\C-k\201\C-m\C-y\C-b\C-d"' which adds an extra space, then removes it. I don't know why, maybe it was some workaround in the old ages when i wrote this, but it looks it does not needed now.

You can do that.
It's not as elegant or straight forward as with zsh but it's doable in bash using bind.
You can not only bind built in Readline functions (listed with bind -l) but other macros and shell functions too.
bind -m emacs -x '"\C-i":"cd .."' will bind a shell command (cd ..) to the keys (Ctrl+i) in emacs mode (the default mode). (Ctrl+i is unbound by default, u isn't)
Note that your prompt will probably not reflect the change.
If you leave out -x the string will instead be typed out for you so "cd ..\n" achieves the same result.
Edit: bind is how you bind keys and macros can accomplish what you want even though no built in thing exists.
If you end your PS1 prompt with \033[K (erase to eol) and can use bind -m emacs '"\C-i":" cd ..&&echo -e \"\\033[2A\"\n"' to do what you want.This will first print cd .. then control chars to move the cursor up and run it (with \n).
The end of your PS1 prevents it from showing. This is a hack but it shows that it's doable.

Related

How to reset terminal without losing the current command?

I'm trying to create a keyboard shortcut to reset the current terminal.
I'm using a .inputrc entry like this:
"\C-K": 'echo -en "\\033c"\n'
It works, however, I can't do that while typing a command.
For instance, if I'm typing a command like this (with the cursor at the end):
$ foobar
and press CTRL+K, it will become
$ foobarecho -en "\033c""
and, of course, it is not going to work.
It is possible to do it?
I suppose it is possible, since that's what CTRL+L does.
The only problem with CTRL+L is that it won't clear the entire terminal, including history, just what's on screen.
I am not sure how to do that in inputrc, but you can do it with the bind command and its -x option.
bind -x '"\C-K": "echo -en \\033c"'
You can put the above line in your .bashrc and it will offer the same behaviour as the one you describe with CTRL+L.
do not understand "reset the current terminal". Clean your terminal? If so you can use command clear

Home/End keys do not work in tmux

I'm currently using tmux with xterm-256color $TERM variable. When in bash under tmux, pressing home/end would insert tilde characters (~). Outside of tmux the home/end keys work fine.
Using cat and tput, I could see that there was a mismatch between the generated and expected sequences:
$ cat -v # pressing home, then end
^[[1~^[[4~
$ tput khome | cat -v; echo
^[OH
$ tput kend | cat -v; echo
^[OF
To fix this, I decided to add the following to my .bashrc:
if [[ -n "$TMUX" ]]; then
bind '"\e[1~":"\eOH"'
bind '"\e[4~":"\eOF"'
fi
That fixed the problem for bash, however in other readline programs, such as within a REPL such as ipython, it still inserts a tilde for home/end.
Why exactly is this a problem in the first place? Why is the generated sequence different when I'm inside tmux vs outside it? How can fix this so that it's not an issue in any programs?
It appears the main problem is with using xterm-256color for $TERM. I switched $TERM to screen-256color and the problem went away.
Add the following to your .tmux.conf:
bind-key -n Home send Escape "OH"
bind-key -n End send Escape "OF"
And you're done!
Explanation
After attempting each one of these, and several others I saw while perusing other answers and documentation, this finally worked for me in every scenario I threw at it. I can't promise the same for you, because everyone's scenarios are different, but this is what I ended up with.
This was discovered after introducing the same trial/error and logic from a somewhat relevant article that is no longer available. The key is where the translation is occurring; in my case, this happens within my .tmux.conf, rather than .bashrc or .zshrc (mainly because my home/end worked fine outside of tmux)
Debugging
You can debug this issue by using cat -v.
Run cat -v, then press the Home and End keys. Exit using Ctrl+C.
$ cat -v
Here's what my output looked like within tmux using zsh, zsh, and bash:
tmux
➜ ~ cat -v
^[[1~^[[4~^C
zsh
➜ ~ cat -v
^[[H^[[F
bash
bash-3.2$ cat -v
^[[H^[[F
Solutioning
Compare the above examples to what we're expecting to see, by pairing tput with cat -v:
$ tput khome | cat -v; echo
^[OH
$ tput kend | cat -v; echo
^[OF
Conclusion
Because this problem exists solely within tmux, and not within the shells themselves, I opted to make the bind changes within the tmux configuration instead. By using bind-key paired with send, we can use the Escape keyword paired with the sequence we want to achieve our translation. Thus:
bind-key -n NAME_OF_KEY send Escape SEQUENCE_GOES_HERE
This debugging and solutioning process can be applied to any other key translation issues. But, don't go too crazy. Some keys are mapped to certain escape sequences for a reason. Notice how bash and zsh received the ^[[H sequence for Home instead of ^[OH; it's probably not recommended we override this in our .zshrc unless we're having major issues with this in zsh.
In tmux 2.0, you can just add these 2 lines in your .tmux.conf:
bind -n End send-key C-e
bind -n Home send-key C-a
If you want to stay with xterm-256color in tmux for some reason - use arch solution with inputrc. I tested it in tmux with rxvt, ruby irb, python, lua and home/end keys are ok. Probably every readline app will be ok.
From the arch wiki:
First things first:
do not set $TERM manually - let the terminal do it.
Many command line applications use the Readline library to read input. So properly configuring Readline can fix Home and End in many cases.
the default /etc/inputrc file does not include a mapping for home/end keys.
To check what the emitted escape sequence for these keys is:
1. Ctrl + V
2. Home
3. Spacebar
4. Ctrl + V
5. End
this will probably print: $ ^[[1~ ^[[4~. So you have to add a mapping for these sequences to your inputrc (/etc/inputrc to be globally, or only for your user ~/.inputrc):
"\e[1~": beginning-of-line
"\e[4~": end-of-line
In my case it was a problem with zsh in tmux (bash in tmux was ok).
None of the other anwsers here worked for me.
But adding this to .zshrc fixed it:
bindkey "\E[1~" beginning-of-line
bindkey "\E[4~" end-of-line
Besides that I also have:
bindkey "\E[H" beginning-of-line
bindkey "\E[F" end-of-line
bindkey "\E[3~" delete-char
From tmux FAQ:
PLEASE NOTE: most display problems are due to incorrect TERM! Before
reporting problems make SURE that TERM settings are correct inside and
outside tmux.
Inside tmux TERM must be "screen", "tmux" or similar (such as
"tmux-256color"). Don't bother reporting problems where it isn't!
Outside, it should match your terminal: particularly, use "rxvt" for
rxvt and derivatives.
Add the following command to your ~/.tmux.conf:
set -g default-terminal tmux-256color
PS: any solution involving binding the keys explicitly is a hack, and as such bound to fail.
set-window-option -g xterm-keys on
This should work in tmux 2.6 and later. (tmux -V to check) If you are using an older version than that, you're probably running an older Ubuntu and you should definitely consider pointing at a ppa with backports.
This definitely doesn't work with kitty, and is effectively "hardcoding" the terminal in terms of input, but it is better than hardcoding specific keys.
So I don't have enough points to comment,so I'll say it here instead. I believe the preferred solution is using
set -g default-terminal "screen-256color" in your ~/.tmux.conf. I actually had this problem a while ago and decided to go with sumanta's solution :
bind -n End send-key C-e
bind -n Home send-key C-a
However I had forgotten I left this in here and ended up having a similar issue with vim (home and end were copy pasting from registers) instead of zsh. In short bind DOES affect vim.
I wasted a lot of time trying all off the above.
In the end I reverted to barbarism:
sudo apt purge tmux
sudo apt install tmux
fixed it for me.

Matlab-like command history completion in csh

My question is how to get command line completion of commands stored in the history in csh, writing the start of the command and using up/down arrows to move through the list, in a similar way as done in Matlab. Example:
$ vim fi
[press up]
$ vim file.py
I have seen the topic answered for bash shell in the below posts but not for csh. I think that it not may be possible for csh ...
here and
here
Thanks
PS: my first incursion into stackoverflow, this site is awesome !!
I think what you are looking for is one of the following commands:
bindkey -k up history-search-backward
bindkey -k down history-search-forward
Alt+P will perform the functionality in csh. There might be a way to map up arrow key to ALt+P just for csh (not sure if that would mess something else up).
Aside:
Alt+/ is a nice shortcut to complete long paths/commands stored in history.
For example if you typed this first:
ls a/very/very/long/path
Then, if you wanted to change your directory into it that just typing the first letter (a) and then Alt+/ would complete the path.
cd a[Alt+/]

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.

Alternative to Up Arrow + Enter to run previous command?

Sometimes I have to run a command many times in succession, for example to see if a service has started, and it becomes tedious to move my hands away from my normal typing position to press the Up Arrow and Enter keys repeatedly. Is there a way to run the previous command without the Up Arrow and Enter keys, perhaps with an elaborate shell script?
I've tried the following, but it is unsatisfactory because it cannot execute aliases, and it is a little slow.
history | tail -2 | head -1 | cut -d' ' -f4- | cat > prev_command.txt
sleep .01
chmod 777 prev_command.txt
eval prev_command.txt
rm prev_command.txt
Ideally I'd have an alias to this script so I can type in something like "prev" in the command line and hit Enter to run the previous command again.
In bash, you can press ctrlp to go to the previous command -- that's a lot better than having to move to the arrow keys.
See also: https://github.com/fliptheweb/bash-shortcuts-cheat-sheet/
Use
!!
to run your previous command.
sudo !!
also works , for the record.
Instead of running the same command many times in succession, why not watch it instead? watch will run a specified command repeatedly and display the output in stdout so you can see it change over time.
watchcommand
I often use the "history expansion" feature in bash (usually activated with cntlR) -- it interactively searches through your history for the previous closest match.
See the bash manual section Searching for Commands in the History, and also Using History Interactively.
Are you an emacs or vi user? You can use
set -o vi
set -o emacs
to set emacs or vi keybindings. You can then use the emacs or vi key bindings in bash. I don't know if this should work for other shells. I believe the vi mode starts in insert mode, so you need to hit esc to enter command mode. In emacs mode (the default), you can use ctrl+p and then ctrl+j to move to the previous line and do a carriage return.
Otherwise, you can use !! as someone else suggested.
In bash:
$ help fc
fc: fc [-e ename] [-lnr] [first] [last] or fc -s [pat=rep] [command]
Display or execute commands from the history list.
fc is used to list or edit and re-execute commands from the history list.
FIRST and LAST can be numbers specifying the range, or FIRST can be a
string, which means the most recent command beginning with that
string.
Options:
-e ENAME select which editor to use. Default is FCEDIT, then EDITOR,
then vi
-l list lines instead of editing
-n omit line numbers when listing
-r reverse the order of the lines (newest listed first)
With the `fc -s [pat=rep ...] [command]' format, COMMAND is
re-executed after the substitution OLD=NEW is performed.
A useful alias to use with this is r='fc -s', so that typing `r cc'
runs the last command beginning with `cc' and typing `r' re-executes
the last command.
Exit Status:
Returns success or status of executed command; non-zero if an error occurs.
Note the suggestion for alias r; I use this frequently.
Depending on what terminal you're using, I know a lot used to have F3 as an option for repeating, but that's still outside the normal range for typing as well unless you have a special keyboard with more accessible function keys.
My keyboard makes the function keys easily accessible, but I don't do much command line work in unix any more, so I wouldn't be able to tell you for sure whether or not this is still possible.

Resources