I've written a simple function to search the current folder when I press ctrl+f with exa and peco, after I choose a directory I want to cd into it.
(ll is modified version of ls)
pecofunc() {
ll | peco --layout=bottom-up | read foo
foo="${foo##* }"
if [ -n "$foo" ]
then
builtin cd "$foo"
fi
# refresh terminal and end execution
}
zle -N pecofunc pecofunc
bindkey '^f' pecofunc
This part works well but after executing it my directory doesn't visually change until I press enter or use other command.
Picture 1
Terminal before doing anything
Picture 2 After pressing ctrl+f
Picture 3 Choosing test2 and pressing enter
Picture 4 Pressing enter again
Pressing enter again refreshes the folder which I'm in but creates another newline which is unacceptable.
How can I avoid this issue?
This is a crosspost from superuser.com, there are already some answers on that side which did not work.
Related
This question already has answers here:
Using the "alternate screen" in a bash script
(3 answers)
Closed 9 months ago.
I try to make a simple bash script that runs multiple tools, like dehader, cppcheck and a bunch of custom tools, and for readability, it cleans the terminal between each tool, and wait for the user to press the enter key before running the next tool.
clear
rm *.o
echo "removed object files"
rm __pycache__
echo "removed python cache files"
echo "everything cleaned, press enter to continue"
read a
clear
deheader
echo "deheader done, press enter to continue"
read a
clear
cppcheck
echo "cpp check done, press enter to exit"
read a
clear
Simple reproduction of what my script does
But I don't like this solution, because I want this script to execute in another screen (I don't know how to call it exactly), just like less does. This way, I could keep my terminal just like it was before the script call. Even if they aren't close to the less behavior, any suggestions that could help me are welcome.
I searched online what I could do to reproduce this behavior, but didn't find anything. I'm starting to doubt that's even possible.
Note: I don't want the 'scrolling' less behavior. I mean, there's no problem if I have to use it, but I don't particulary want it.
I think you want to run a new Terminal window and get back the prompt in your current Terminal window. If so, start a new Terminal that runs a command with lots of output and pipe it through less:
xterm -e "ls -lR / | less" &
The & runs the command in the background and gets you your prompt back in the current Terminal window.
Thanks to the comments, I figured it out.
From what I can understand, there are two ways to do it with the "cursor addressing mode":
By using tput smcup/rmcup
By entering/exiting the "cursor addressing mode" manually, when priting the escapes sequences related
This mode in terminal-implemented and is not supported by all terminals - and not always in the same way, for example multiple terminals don't reset the cursor position after entering this mode, so don't forget to clear your terminal - but is fortunately supported by a large majority of modern terminal emulators.
smcup/rmcup
Simply use the tput smcup command, and then, restore it using tput rmcup.
Example:
# save, clear screen
tput smcup
clear
# example "application" follows...
read -n1 -p "Press any key to continue..."
# example "application" ends here
# restore
tput rmcup
source
Escape sequence
You can enter this mode using printf \\33\[\?1047h, then leave it using printf \\33\[\?1047l.
Example:
# save, clear screen
printf \\33\[\?1047h
clear
# example "application" follows...
read -n1 -p "Press any key to continue..."
# example "application" ends here
# restore
printf \\33\[\?1047l
more informations
Try to avoid using both methods in the same code, or using one method to enter this mode and another to exit it, as it could lead to unexpected behavior.
Personnaly, I prefer the tcup method as it's more readable and supported by all Unix systems.
I work in Windows 10 and usually I have up to 5 CMD windows open. I work this way because I need to run the same application with different data and keep monitoring if any exception is thrown.
I set a number as the window's title (using the title command) instead of the default text, in order to easily identify which window I'm working in and be able to identify and change between them using Alt+Tab (an example of how I work with my CMD windows)
Recently I started to use Git and I really like the Git Bash terminal for Windows. I would like to work with Git Bash terminal the same way I work with CMD windows, but I can't find any way to change the window title. I searched a bit and found these instructions and some others (that I can't paste because I'm not allowed to post more than two links yet), but it seems to work only by setting a different default title. I'd like to change the window title to any custom text I choose, at any moment.
Is this possible? Is there a command like title available for Git Bash?
A simple option is echo -ne "\e]0;YOUR TITLE HERE\a".
This thread is a few months old. But I think an alternative will be helpful
You can add following line to .bashrc file in your user profile folder
export TITLEPREFIX="Git Bash"
where you want Git bash to be your title prefix. This is user specific change. So if a machine is used by multiple users with their own logins, everyone can customize their own title.
You were on the right track with this link
If you modify the git-prompt.sh script a bit (for me, this is located in c:\Program Files (x86)\Git\etc\profile.d\git-prompt.sh), you can make the title anything you want.
Note: You will need to run VS Code, Notepad++ or similar as administrator to write back to this directory.
First, save a backup of git-prompt.sh (like git-prompt.backup.sh), then modify the start of git-prompt.sh as follows:
if test -z "$GITTITLEPREFIX" # if not empty
then
GITTITLEPREFIX="Git-Bash => " # prefix that will have current pwd appended after it
fi
if test -f ~/.config/git/git-prompt.sh
then
. ~/.config/git/git-prompt.sh
else
if test -n "$GITTITLE"
then ##### Set window title directly to GITTITLE if not empty
PS1='\[\033]0;$GITTITLE\007\]'
else ##### Set window title to GITTITLE PREFIX plus the PWD
PS1='\[\033]0;$GITTITLEPREFIX${PWD//]^[:ascii:]]/?}\007\]'
fi
fi
###### Leave the rest of the file the same
PS1="$PS1"'\n'
PS1="$PS1"'\[\033[32m\]'
###### Etc.
This will first check if GITTITLEPREFIX is empty, and if not, it will set it to "Git-Bash => " similar to in the linked article. This will have the current path appended after it, so if you want "1 : $PWD", then set GITTITLEPREFIX to "1 : " like this:
GITTITLEPREFIX="1 : "
Otherwise, you can set GITTITLE to any non-empty value, and then the entire title will be set exactly to the contents of GITTITLE (no PWD appended), so if you just want "1", use this:
GITTITLE="1"
Then run the script. With my path, I did it like this:
. "/c/Program Files (x86)/Git/etc/profile.d/git-prompt.sh"
and the title should change. Of course, you can alias this or make a separate script from it in a location that is in the path so running it is much simpler, and the title could just be an argument. I'll leave that as an exercise for the reader...
In JSON setting write for Git Console:
"name": "Git Bash",
"tabTitle": "Git Bash",
"suppressApplicationTitle": true
You could use
export MYTITLE=abcd; export PS1=$(echo $PS1 | sed -r "s/(0;).*?(\\\\007)/\1$MYTITLE\2/")
It find regular expression for title and replace it with $MYTITLE.
I could be wrong but I assume title is something between 0; and \007 in $PS1. It work for me.
Or you could add next function to your .bashrc (or .bash_profile)
ttt() {
# change title in gitbash
export PS1=$(echo $PS1 | sed -r "s/(0;).*?(\\\\007)/\1$1\2/")
}
and then anytime use ttt "new title"
I solved my question making very little modifications to the script.
The first one, to pass the name I want for the window, I added the variable name=$1 and set it in the title variable:
name=$1
PS1='\[\033]0;$name\007\]' # set window title
The second one, as recommended here, I commented the next lines:
#PS1="$PS1"'\[\033[35m\]' # change to purple
#PS1="$PS1"'$MSYSTEM ' # show MSYSTEM
The final code is below:
if test -f /etc/profile.d/git-sdk.sh
then
TITLEPREFIX=SDK-${MSYSTEM#MINGW}
else
TITLEPREFIX=$MSYSTEM
fi
name=$1
PS1='\[\033]0;$name\007\]' # set window title
PS1="$PS1"'\n' # new line
PS1="$PS1"'\[\033[32m\]' # change to green
PS1="$PS1"'\u#\h ' # user#host<space>
#PS1="$PS1"'\[\033[35m\]' # change to purple
#PS1="$PS1"'$MSYSTEM ' # show MSYSTEM
PS1="$PS1"'\[\033[33m\]' # change to brownish yellow
PS1="$PS1"'\w' # current working directory
if test -z "$WINELOADERNOEXEC"
then
GIT_EXEC_PATH="$(git --exec-path 2>/dev/null)"
COMPLETION_PATH="${GIT_EXEC_PATH%/libexec/git-core}"
COMPLETION_PATH="${COMPLETION_PATH%/lib/git-core}"
COMPLETION_PATH="$COMPLETION_PATH/share/git/completion"
if test -f "$COMPLETION_PATH/git-prompt.sh"
then
. "$COMPLETION_PATH/git-completion.bash"
. "$COMPLETION_PATH/git-prompt.sh"
PS1="$PS1"'\[\033[36m\]' # change color to cyan
PS1="$PS1"'`__git_ps1`' # bash function
fi
fi
PS1="$PS1"'\[\033[0m\]' # change color
PS1="$PS1"'\n' # new line
PS1="$PS1"'$ ' # prompt: always $
MSYS2_PS1="$PS1" # for detection by MSYS2 SDK's bash.basrc
Temporally I made a copy of the script and pasted it on C:, to execute it easily every time I need to change the title, according to my path, as as follows:
$ . /c/changename.sh
I'm still learning about scripting so I could be able to set an alias. As #LightCC said, "I'll leave that as an exercise for the reader..."
I am using the following code to take input from user interactively,
thus allowing the user to make changes to the input without deleting the entire line.
#! /bin/bash
while :
do
echo -n "prompt# "
read -e input
done
Problem:
It also allows me to delete the "prompt#", which I don't want to happen.
I don't want the "prompt# " to be deleted by the user in any case.
I've tried using various options in read command but can't figure it out.
How can I achieve this?
Edit:
The sequence of events that led to the problem:
1. I ran the above script and entered "hello".
2. Then I moved my cursor to 'e' in "hello" and "pressed and held" backspace for some time which resulted in the deletion of the "prompt# "
3. If you don't type any characters it works fine and the prompt is not deleted but if you enter even a white space or any character and then press the backspace then it messes the prompt.
Take a look at the snapshot below.
Snapshot:
From 4ae1e1's comment:
You should use read's builtin prompt support, or you're on your own.
read -p 'prompt# ' -e input
I am using fish shell. When I type Ctrl-D, it sends a EOF to my terminal and then terminal closes.
I want to make it such that ctrl-D does not close my iterm2.
I saw that people have set up IGNOREEOF in bash shell like this: https://unix.stackexchange.com/questions/27588/how-can-i-keep-controld-from-disconnecting-my-session
However, I don't think this variable exists in fish. Does anybody know how I can force iterm2(with default fish shell) to not close on ctrl-D?
This is the default key binding for control-D:
bind \cd delete-or-exit
you can find this by just running bind.
(delete-or-exit is just a function, which you can read with functions delete-or-exit.)
So it's exiting because that's what the default behavior is. You can make control-D do something else. For example, maybe it should delete the character under the cursor:
bind \cd delete-char
If you want to make this permanent, add it to your fish_user_key_bindings function:
Run funced fish_user_key_bindings which starts editing
Put bind \cd delete-char within the function
Hit return to create the function
Run funcsave fish_user_key_bindings to save it
Shell approach
After reading this question and answer I updated my delete-or-exit function to ask for confirmation rather than completely deactivate it:
cd ~/.config/fish/functions/
cp /usr/share/fish/functions/delete-or-exit.fish .
Then edit/replace:
function delete-or-exit
set -l cmd (commandline)
switch "$cmd"
case ''
read --nchars 1 --local -P 'Do you want to exit? [y/N] ' confirm
switch $confirm
case Y y
exit 0
case '' N n
echo -n (fish_prompt)
end
case '*'
commandline -f delete-char
end
end
It has a minor issue in that it displays the prompt twice when you finish, but it seems better than no times if you don't print it (see N case above). Perhaps someone has a solution to that.
Terminal approach
iTerm and many other terminals have a setting says something like, "keep terminal open when program exits."
Instead of assigning a different action to Ctrl+d you could also unbind it through bind --erase --preset \cd. With all steps:
Run funced fish_user_key_bindings which starts editing
Put bind --erase --preset \cd within the function
Hit return to create the function
Run funcsave fish_user_key_bindings to save it
Note that I'm NOT talking about previous lines in the history. I'm talking about previous lines in the current multiline command.
When I type a multiline command in zsh, for instance (_ indicates the cursor):
$ for i in {1..5}; do
for> echo i_
At this point I might change my mind and want to let i loop through {1..10} instead, so I need to go back to the previous line. However, I can't figure out how to do this, as ⌫, or ←, or C-b, or whatever I can think of all stops at the beginning of the second line. So, is it possible at all to move back? Thanks in advance.
In fact, this is not limited to zsh. For instance, I've never figured this out in bash either.
I've already spent a fair amount of time Googling without any findings, so please excuse me and blame my Google-fu if this is obvious.
For your reference, I'm using Emacs keybinding, and
$ bindkey | grep delete
"^D" delete-char-or-list
"^H" backward-delete-char
"^[[3~" delete-char
"^?" backward-delete-char
Not sure whether this will help.
Here is how you would visual mode in bash's vim mode.
Inside .bashrc put the following lines.
set -o vi
# I do not remember which one of these is used by `v`, so I set both
export VISUAL=/usr/bin/nano
export EDITOR=/usr/bin/nano
Reload bash, and then push Esc to go into normal mode.
Type v, and nano should load. You can now enter your multiline command into nano. When you save and exit, the multiline command will be executed.
For zsh, the following lines in your rc may do it. I stole the last three lines from this answer and tested it on my zsh.
bindkey -v
autoload -U edit-command-line
zle -N edit-command-line
bindkey -M vicmd v edit-command-line
Actually this is possible.
While on that second line, pres Esc + X in order to execute a command,
and type push-line-or-edit (note that you can use Tab key for completion)
, then press return.
The prompt will change to a traditional one ditching your for> part from
continuation line, and you will have a new buffer filled with all your
previously typed lines, which of course now you can easily edit using well
known C-a or C-b keys.
I do not think this is possible, but here are other workarounds:
You can just write everything in one line. It is not easy to edit, but multiline strings are not easily editable either.
You can bind some key (e.g. <C-Enter> if you can tell your terminal not to output <C-m> in this case) to
function _-add-newline()
{
LBUFFER="$LBUFFER"$'\n'
}
zle -N add-newline _-add-newline
bindkey "\Ca" add-newline
. This way if you press <C-a> newline will appear in the buffer, but it will not trigger accept-line widget and previous line will still be editable. You can e.g. use left/right arrows to move to it.
If you type for x in y ; do<CR>echo foo<CR><C-c><Up> you will see for x in y ; do\necho foo in your buffer and all text will be editable. Note: you need <CR> to preserve last line (line aborted by <C-c> is not saved) and <C-c> to abort input; this variant will not work if you have already typed done (first <CR> will run the cycle). In last case you may discard last line with <C-c> and retype it.
Theoretically you may override accept-line widget with a code that parses your input and determines whether you have written command completely and if not then it will just append \n to the buffer (like in the above function) and do not run original accept-line, otherwise original accept-line is run. This variant is much harder to implement though, so I am saying “theoretically”.