Bash script Arrow Keys produces weird characters - bash

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.

Related

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 place cursor beginning of line

How can I set the cursor to the beginning of line in a bash script. It should have the same behavior as pressing Ctrl-a.
I tried something like this echo -e "\e[H" but it didn't work.
Here is what I'm trying to do. Let's say I have a command that I want to perform an action on it (doesn't matter what) before executing it. So I associated a Key (using bind -x ) to a function that will perform that action. However, before executing that action, I need to place the cursor to the beginning of that command (as if pressed Ctrl-a)
While Deanie's answer of
echo -ne "\r"
is correct, I found I had to ensure that my hash bang was correct:
#!/bin/bash
NOT
#!/bin/sh
Wouldn't it just be
echo -ne "\r"
Sorry, forgot to suppress the newline.
Let say want bind the /some/path to shift-alt-W and want move to the beginning of the line:
bind '"\eW":"/some/path\C-a"'
Pressing the shift-alt-w will enter the /some/path into the terminal, and the \C-A cause to move to the beginning of the line so you can type cd before the /some/path.

Bash script user input prompt

I am having issues with my known methods of generating user input prompts:
read -p "Input something: " variabile
This causes issues if attempting to use the arrow keys, it echoes the ANSI code for each arrow key stroke
read -e -p "Input something: " variable
This fixes the arrow keys issue but when reaching the width of the terminal, text input doesn't continue on a newline but on the same line, overwriting (visually) the existing input
echo -n "Input something: "; read -e variable
This apparently fixes both formerly described issues... until I found that typing something then hitting backspace overwrites the prompt and also when the input is longer, from the second newline of input the visual overwriting manifests again.
So is there a good way of producing prompts without the above issues?
UPDATE
After re-checking, I now know what's causing the input overwrite for read -e -p
I am using these variables for highlighting text for the read prompt:
highlight=$(echo -e "\e[1;97m")
clear=$(echo -e "\e[0m")
read -e -p "Input$highlight something$clear: " variable
This is the only way I could make the highlighting work inside read prompt (assigning escape sequences to the variables doesn't work, I need to echo them like I did) but they also seem to cause the input overwrite issue.
As dimo414 mentions, readline thinks the prompt is longer than it is. It counts every character in the terminal escape sequence in computing the length. You can see how long it thinks the escape sequence is as follows
echo ${#highlight}
In the bash PS1 prompt, surrounding such an escape sequence with "\[" and "\]" instructs readline to ignore everything between when calculating current line length, but these are not the right escapes for the bash read built-in.
The escapes for read are $'\001' and $'\002', as mentioned in BashFAQ, but in my experience, you need the -e option on read, as well. The brute force way to do what you want would be:
read -e -p "Input "$'\001'"${highlight}"$'\002'something$'\001'"${clear}"$'\002'": "
You should use tput rather than hard-coded escape sequences, for the sake of terminal independence. Read man 5 termcap.
See my dotfiles for elegant bash functions to do the begin/end quoting above for you.
The shell keeps track of how long it thinks the prompt is, in order to know where the user's input starts and stops. Unfortunately when you print color escape codes in a prompt you throw of Bash's counting, since it expects the escape characters to take up space in the terminal.
To avoid that, you just need to wrap all color sequences in \[ and \], which tells your shell the enclosed characters are non-printing, and should not be counted.
For example, your highlight variable should be:
highlight=$(echo -e "\[\e[1;97m\]")
Personally, I use the color and pcolor functions from my Prompt.gem project, which handles the proper escaping and would make your command much easier to read:
read -e -p "Input $(pcolor DEFAULT BOLD)something$(pcolor): " variable

Pass argument with newline to external commands in vim

When I'm interacting with the shell or writing a bash script I can do:
somecmd "some
arg"
Say now that I want to do the same in vim command-line mode:
:!somecmd "some<Enter>arg"
obviously won't work: as soon as I press <Enter> the command is executed. But neither the following do:
:!somecmd "some<C-V><Enter>arg"
:!somecmd "some<C-V>x0Aarg"
The first one inserts a carriage return instead of a line feed, which is right. The second one will break the command in two, trying to execute somecmd "some<C-V> first and then arg", both of which fail miserably.
I guess I could work around this using some echo -e command substitution, or embedding $'\n', but is it possible to type it directly in vim's command-line? I don't fully understand why the "some<C-V>x0Aarg" form doesn't work while $'some\narg' does. Is vim parsing the string previously to shell evaluation?
Well, I've found the answer myself, but I'm leaving here for further reference anyway. The documentation of :! states:
A newline character ends {cmd}, what follows is interpreted as a following ":" command. However, if there is a backslash before the newline it is removed and {cmd} continues. It doesn't matter how many backslashes are before the newline, only one is removed.
So you (I) should type "some\<C-V>x0Aarg" instead of "some<C-V>x0Aarg".
Plus, I could have done it using the system() function instead of the :! command:
:call system("somecmd 'some<C-V>x0Aarg'")

Prevent typed characters from being displayed (like disabling "echo" attribute in termios)

I'm writing a bash script in which I read single characters from the input. I do so using read -n 1 -s. -n 1 is to read only a single character; -s is "silent" mode, in which the typed characters won't be visible.
The problem is, that when the currently executed command isn't read (whenever some other commands in the bash script are being executed), the character gets displayed in the terminal.
This is the normal behaviour of a program in the terminal. To disable this, one normally disables the echo mode, for example using the termios library.
How can I achieve this in a bash script?
I prefer solutions in pure bash / Unix commands (without other scripting languages like python, perl etc.).
stty -echo
# Anything they type won't output here
stty echo
# Now it will

Resources