How to present editable input to a user with BASH and ZSH? - bash

I need to accept user input within a shell script that can run both in BASH and ZSH. I'm accustomed to using readline in other languages, but this doesn't seem to be a viable option in shell scripting.
An example prompt might be:
Please enter the value> 1234_
How can I present a user with editable input that has a default value that can be edited (backspaced) that's compatible with both shells?

User #cyrus mentioned in the comments, but is better directly as an answer instead:
read -e -p 'Please enter the value> ' -i '1234' myvar
echo $myvar
results in an editable default of 1234
Please enter the value> 1234
1234

If I did not misunderstand ...
In terms of "Backspace" functionality, to erase the last characters and add some new ..
This works in both shells with a simple "read" in the script.
echo -n "Enter value > "
read v
echo $v
If you have other issues, could you kindly rephrase to make your question clearer ?
What do you use (Example Code).
What do you perform exactly in terms of editing (only BS or do you mean line editing commands CTRL-A, etc)
What is the result / error, what you want to get fixed.
Thanks.

Related

In bash, after `bind` a command to a key, without echo the command which is executing

First you may refer to this great post here: Bash bind for better understanding what I am trying to achieve.
I want to achieve that when I hit the enter then update the shell prompt dynamically, now I already had is:
__update_shell_prompt () {
# according what I had done in the terminal regenerate the new shell prompt
# new_shell_prompt
export PS1=$new_shell_prompt
}
bind '"\C-M":"\n__update_shell_prompt\n"'
It works as expected, just after each command I executed it would echo the text __update_shell_prompt also, like:
I also tried to use the bind with the -x option like this:
bind -x '"\C-M":"\n__update_shell_prompt\n"''
Then it doesn't respect the \n, so it totally doesn't work, if I remove the \n, it still doesn't work, and bring another serious problem in, the command I tried to execute just remain in the screen and never would get executed, like so: all commands just get stuck there...
So:
How can I make that don't echo the command text...
If this is not the right way to achieve this(dynamically update shell prompt upon enter hitting), then how should I do this?
First thanks #Charles Duffy, I followed his suggestion and used the tput.
The solution is simple, use tput cup to move the cursor up, then use tput el to delete that line, as below:
# Get the current cursor coordinate.
IFS=';' read -sdR -p $'\E[6n' ROW COL; local current_row=`echo "${ROW#*[}"`
# Move the cursor up then delete that line
tput cup $((current_row-2)) 0 && tput el
Tips:
To prevent the potential commands history pollution, you can put export HISTCONTROL=ignorespace into your ~/.bash_profile or ~/.bashrc;
Then invoke the command with prefixed space.
The problem I used this scenario to solve can be referred here: docker-machine PR #4127.

How to modify one’s input in a bash interactive script? [duplicate]

Consider the following minimised Bash Script:
echo Enter your name:
read NAME
echo $NAME
Now if I run the script and enter a name and want to navigate through my input with my arrow keys, [[D^ characters are getting returned.
How would you rewrite that script to cater for such behaviour, i.e. let me navigate with keys instead of winning an ASCII contest ?
These character sequences are the way that the terminal communicates that "cursor left" has been pressed. If the program receiving it does not interpret it as such and instead just displays them (after filtering the escape character), that's what you get.
Luckily for you, the read command of bash has the option -e to enable use of Readline for reading in the line. Readline performs all that handling (as it does on the normal bash command input).
Thanks to Andreas and some due dilligence with a search engine, I was able to rewrite my script:
echo Enter your name:
read -e NAME
echo $NAME
Now navigating through the input with arrow keys, works like a expected.
Here you can learn more about the read builtin command.

Bash script Arrow Keys produces weird characters

Consider the following minimised Bash Script:
echo Enter your name:
read NAME
echo $NAME
Now if I run the script and enter a name and want to navigate through my input with my arrow keys, [[D^ characters are getting returned.
How would you rewrite that script to cater for such behaviour, i.e. let me navigate with keys instead of winning an ASCII contest ?
These character sequences are the way that the terminal communicates that "cursor left" has been pressed. If the program receiving it does not interpret it as such and instead just displays them (after filtering the escape character), that's what you get.
Luckily for you, the read command of bash has the option -e to enable use of Readline for reading in the line. Readline performs all that handling (as it does on the normal bash command input).
Thanks to Andreas and some due dilligence with a search engine, I was able to rewrite my script:
echo Enter your name:
read -e NAME
echo $NAME
Now navigating through the input with arrow keys, works like a expected.
Here you can learn more about the read builtin command.

Set screen-title from shellscript

Is it possible to set the Screen Title using a shell script?
I thought about something like sending the key commands ctrl+A shift-A Name enter
I searched for about an hour on how to emulate keystrokes in an shell script, but didn't find the answer.
You can set the screen / xterm title using the following lines:
#!/bin/bash
mytitle="Some title"
echo -e '\033k'$mytitle'\033\\'
[UPDATE] - by request I'm also including the solution proposed by #Espo below:
Depending on your xterm version or your linux distribution the line above may or may not work and you can try the xterm-defaults:
#!/bin/bash
mytitle="Some title"
echo -e '\033]2;'$mytitle'\007'
For more on the details see: http://www.faqs.org/docs/Linux-mini/Xterm-Title.html#s3 or refer to the answer by #Espo below.
From http://www.faqs.org/docs/Linux-mini/Xterm-Title.html#s3
xterm escape sequences
Window and icon titles may be changed
in a running xterm by using XTerm
escape sequences. The following
sequences are useful in this respect:
ESC]0;stringBEL -- Set icon name and window title to string
ESC]1;stringBEL -- Set icon name to string
ESC]2;stringBEL -- Set window title to string
where ESC is the escape character
(\033), and BEL is the bell character
(\007).
Printing one of these sequences within
the xterm will cause the window or
icon title to be changed.
Note: these sequences apply to most
xterm derivatives, such as nxterm,
color-xterm and rxvt. Other terminal
types often use different escapes; see
the appendix for examples. For the
full list of xterm escape sequences
see the file ctlseq2.txt, which comes
with the xterm distribution, or
xterm.seq, which comes with the rxvt
distribution.
Printing the escape sequences
For information that is constant
throughout the lifetime of this shell,
such as host and username, it will
suffice to simply echo the escape
string in the shell rc file:
echo -n "\033]0;${USER}#${HOST}\007"
should produce a title like
username#hostname, assuming the shell
variables $USER and $HOST are set
correctly. The required options for
echo may vary by shell (see examples
below).
For information that may change during
the shell's lifetime, such as current
working directory, these escapes
really need to be applied every time
the prompt changes. This way the
string is updated with every command
you issue and can keep track of
information such as current working
directory, username, hostname, etc.
Some shells provide special functions
for this purpose, some don't and we
have to insert the title sequences
directly into the prompt string. This
is illustrated in the next section.
The following are other ways to script the renaming of screen titles:
Adding the following settings to .ssh/config sets the screen title automatically upon logging in to a system using SSH:
Host *
PermitLocalCommand yes
LocalCommand [ "$TERM" == 'screen' ] && echo -ne "\033k%h\033\\"
Instead of %h, which represents the hostname of the machine you are connecting with, you may use %n, which is the actual name / alias you used to connect to the machine.
NOTE: You need OpenSSH >= v5.1 to be able to use the Localhost %n and %h parameters. Check out 'man ssh_config' for more info on LocalCommand.
To automatically revert the title, back to that of the hostname of the localhost, after closing the SSH session, you can add an escape sequence to you prompt variable PS1 in .bashrc :
export PS1='you_favorite_PS1_here'
if [ "$TERM" == 'screen' ]; then
export PS1=${PS1}'\[\033k\h\033\\\]'
fi
These tricks are especially useful when using a .screenrc config that shows you in what screen 'tab' you are currently working. Add something like the following to .screenrc to get this working:
caption always "%{= kY}%-w%{= Yk}%n %t%{-}%+w%{ kG} %-= #%H - %LD %d %LM - %c"
Try the below commands, no need to edit any file or configuration like ~/.bashrc, Can be used at runtime.
Set static text as title: (My Title)
export PS1='\[\e]0;My Title\a\]${debian_chroot:+($debian_chroot)}\u#\h:\w\$ '
Set local/global variable as title: ($USER)
export PS1='\[\e]0;$USER\a\]${debian_chroot:+($debian_chroot)}\u#\h:\w\$ '
Set command output as title: (hostname)
export PS1='\[\e]0;`hostname`\a\]${debian_chroot:+($debian_chroot)}\u#\h:\w\$ '
Set to default (Revert back):
export PS1='\[\e]0;\u#\h: \w\a\]${debian_chroot:+($debian_chroot)}\u#\h:\w\$ '
set_screen_title ()
{
echo -ne "\ek$1\e\\"
}
You can also call screen and tell it to set a title:
screen -X title "new title"
If you're in a screen window, it will set that window's name. If you're not in screen, it will set the most recently opened window's name.
To add to Espo's answer, the xterm escape sequences can also be applied to the Bash PS1 variable
ESC]0;stringBEL -- Set icon name and window title to string
ESC]1;stringBEL -- Set icon name to string
ESC]2;stringBEL -- Set window title to string
Example
PS1='\e]0;string\a'
To enable automatic title updating when jumping around with ssh, add this to ~/.bashrc:
ssh() {
echo -n -e "\033k$1\033\\"
/usr/bin/ssh "$#"
echo -n -e "\033k`hostname -s`\033\\"
}
echo -n -e "\033k`hostname -s`\033\\"
See http://linuxepiphany.blogspot.com.ar/2010/05/good-screenrc-config-setup.html
# add the following in your ~/.bashrc or ~/.bash_profile
PROMPT_COMMAND='printf "\033]0;%s#%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"'
or even better copy the whole concept for customizing your bash configs between a lot of hosts from here
My solution to this problem was to create a bash script and add it to my ~/.bashrc file:
set-title() {
ORIG==$PS1
TITLE="\e];$#\a"
PS1=${ORIG}${TITLE}
}
Now when I'm in any bash shell session, I type "set-title desired_title" and it changes to "desired title".
This works for multiple versions of Ubuntu, currently on Kinetic 16.04
I got this solution from here. I was looking for it again, couldn't find it and thought I'd post it here for anyone interested.
I got this solution from experimenting with others, like #flurin-arner I started the #weston-ganger set-title(). I also used #imgx64 PROMPT_DIRTRIM suggestion. I'm also using #itseranga git branch prompt, though this has nothing to do with the question it does show what you can do with the prompt.
First as shown by weston and above
TITLE="\[\e]2;$*\a\]"
can be used to manually set the Terminal Title, "$*" is commandline input, but not what we want.
2nd as stated I'm also adding git branch to my prompt, again not part of the question.
export PROMPT_DIRTRIM=3
parse_git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/ (\1)/'
}
export PS1="\u#\h \[\033[32m\]\w\[\033[33m\]\$(parse_git_branch)\[\033[00m\] $ "
3rd, by experiment I copied the TITLE code above, set the $* to a fixed string and tried this:
see: \[\e]2;'SomeTitleString'\a\]
export PS1="\u#\h \[\033[32m\]\w\[\033[33m\]\$(parse_git_branch)\[\033[00m\]\[\e]2;'SomeTitleString'\a\] $ "
This had the desired effect! Ultimately, I wanted the base path as my title.
PS1 Params shows that \W is the base path so my solution is this:
export PS1="\u#\h \[\033[32m\]\w\[\033[33m\]\$(parse_git_branch)\[\033[00m\]\[\e]2;\W\a\] $ "
without the git branch:
export PS1="\u#\h \[\033[32m\]\w\[\033[33m\]\[\033[00m\]\[\e]2;\W\a\] $ "
resulting in a prompt with git-branch:
user#host ~/.../StudyJava (master) $
resulting in a prompt without parse_git_branch:
user#host ~/.../StudyJava $
where pwd gives
/home/user/somedir1/otherdir2/StudyJava
and Terminal Title
StudyJava
NOTE: From #seff above I am essentially replacing the "My Title" with "\W"
export PS1='\[\e]0;My Title\a\]${debian_chroot:+($debian_chroot)}\u#\h:\w\$ '
I tried this on Ubuntu 18.10 and it only worked with PROMPT_COMMAND in ~/.bashrc.
And if you override PROMPT_COMMAND, the behavior of the title changes slightly. I decided to change only if necessary:
t() {
TITLE="$#"
PROMPT_COMMAND='echo -ne "\033]0;${TITLE}\007"'
}
enter image description here

Input from within shell script

I have a script that calls an application that requires user input, e.g. run app that requires user to type in 'Y' or 'N'.
How can I get the shell script not to ask the user for the input but rather use the value from a predefined variable in the script?
In my case there will be two questions that require input.
You can pipe in whatever text you'd like on stdin and it will be just the same as having the user type it themselves. For example to simulating typing "Y" just use:
echo "Y" | myapp
or using a shell variable:
echo $ANSWER | myapp
There is also a unix command called "yes" that outputs a continuous stream of "y" for apps that ask lots of questions that you just want to answer in the affirmative.
If the app reads from stdin (as opposed to from /dev/tty, as e.g. the passwd program does), then multiline input is the perfect candidate for a here-document.
#!/bin/sh
the_app [app options here] <<EOF
Yes
No
Maybe
Do it with $SHELL
Quit
EOF
As you can see, here-documents even allow parameter substitution. If you don't want this, use <<'EOF'.
the expect command for more complicated situations, you system should have it. Haven't used it much myself, but I suspect its what you're looking for.
$ man expect
http://oreilly.com/catalog/expect/chapter/ch03.html
I prefer this way: If You want multiple inputs... you put in multiple echo statements as so:
{ echo Y; Y; } | sh install.sh >> install.out
In the example above... I am feeding two inputs into the install.sh script. Then... at the end, I am piping the script output to a log file to be archived and viewed for later.

Resources