bash: parsing F-key (F1-F10) in script - bash

I wrote a bash script, which has "buttons" similar to "mc" or Norton Commander. Right now, I use the number keys (1-0) to simulate the buttons being pressed. However, I would rather have the F-keys (F1-F10) to do it.
I saw this question and answers, but I am not certain this can be used in a script to trigger a function (e.g. by using "read").
Does bash support it? If so, is there a fairly easy way to implement it?
UPDATE
The script is kind of a dash, which needs to be refreshed in order to keep the contents current. However, at the same time I would like to keep the "channel" open to allow user input (therefore sleep would not be adequate).
The read line currently in use to get the user input looks like this:
read -n 1 -s -t "${iRefresh}" sReturnVar.
Here "iRefresh" is set to 2 seconds in order to time out when no input is given to refresh the display and return to the read line thereafter. In essence the read line doubles as a content refresher while waiting for user input.

This will dynamically determine the values for F1-F24, then use them in the context you're looking for. My system coopts F11, so I did not show that. This is somewhat brittle in that it depends on terminfo and sane terminal codes -- YMMV.
#!/usr/bin/env bash
read_key_press() {
if read -sN1 key_press; then
while read -sN1 -t 0.001 ; do
key_press+="${REPLY}"
done
fi
}
declare -a fnkey
for x in {1..24}; do
raw=$(tput kf$x | cat -A)
fnkey[$x]=${raw#^[}
done
while read_key_press; do
case "${key_press}" in
$'\e'${fnkey[1]}) echo 'F1';;
$'\e'${fnkey[2]}) echo 'F2';;
$'\e'${fnkey[3]}) echo 'F3';;
$'\e'${fnkey[4]}) echo 'F4';;
$'\e'${fnkey[5]}) echo 'F5';;
$'\e'${fnkey[6]}) echo 'F6';;
$'\e'${fnkey[7]}) echo 'F7';;
$'\e'${fnkey[8]}) echo 'F8';;
$'\e'${fnkey[9]}) echo 'F9';;
$'\e'${fnkey[10]}) echo 'F10';;
$'\e'${fnkey[11]}) echo 'F11';;
$'\e'${fnkey[12]}) echo 'F12';;
$'\e'${fnkey[13]}) echo 'F13';;
$'\e'${fnkey[14]}) echo 'F14';;
$'\e'${fnkey[15]}) echo 'F15';;
$'\e'${fnkey[16]}) echo 'F16';;
$'\e'${fnkey[17]}) echo 'F17';;
$'\e'${fnkey[18]}) echo 'F18';;
$'\e'${fnkey[19]}) echo 'F19';;
$'\e'${fnkey[20]}) echo 'F20';;
$'\e'${fnkey[21]}) echo 'F21';;
$'\e'${fnkey[22]}) echo 'F22';;
$'\e'${fnkey[23]}) echo 'F23';;
$'\e'${fnkey[24]}) echo 'F24';;
^D) exit ;; # note: this is a real <ctrl>-<d>
*) echo "Key pressed: ${key_press}";;
esac
done
For me, this produces:
<~> $ /tmp/so8897.sh
Key pressed: q
Key pressed: w
Key pressed: e
Key pressed: r
Key pressed: t
Key pressed: y
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F12
F13
F14
F15
F16
F17
F18
F19
F20
F21
F22
F23
F24

Related

Reading in input from text file, Bash script

I am new to writing scripts in Bash and I am working on an assignment to make an inventory system. I have written all the scripts and they all work from using the standard input terminal. I now am looking to take the inputs from a text file called a1Input.txt which has all inputs on a new line.
r
9823
r
5430
c
7777
sml_widget
Small Widget (1 oz.)
6.99
15
50
Small, white, widget w/o packaging
r
7777
d
9823
r
9823
r
3293
u
3293
29.99
33
75
r
3293
The code for my initial bash script is this
#!/bin/bash
# assign1.bash
shopt -s nocasematch
option=""
until [ "$option" = "F" ]; do
echo "C - create a new item"
echo "R - read an existing item"
echo "U - update an existing item"
echo "D - delete an existing item"
echo "T - total price of an item"
echo "Choose a option"
read option
case $option in
C)
./create.bash
;;R)
./read.bash
;;U)
./update.bash
;;D)
./delete.bash
;;T)
./total.bash
;;*)
echo "Invalid input"
esac
done
What i would do to inject inputs from file to an interactive script is just:
cat a1Input.txt | ./interactive_script.sh
For example, imagine this simple script (copy-paste on terminal to create):
cat << EOF > questions.sh
#!/bin/bash
echo "What's your name ?"
read name
echo "What is your favourite movie ?"
read movie
echo "Hi \$name, i also love '\$movie' movie !!"
EOF
And these inputs:
cat << EOF > inputs.txt
Edu
Interstellar
EOF
Then, just execute:
chmod a+x questions.sh
cat inputs.txt | ./questions.sh
If your script is more complicated, consider using "expect", although this is quite complex.
BRs

Error while assigning a value in second case statement for interactive script

I am trying to write a user interactive script. I am doing it by letting the user type the values of the flags. My first case statement works fine but the second won't and I don't understand why? In the last two lines of this code I am suppose to see the values of my variables but I can only see the assigned value from the first statement and the second case statement give me no value. I have more case statements in my code but the first one is the only one that works. Do I need to do something in between statements?
echo "`tput bold`What machine was used?`tput sgr0`"
select Machine in "M1" "M2" "M3" "M4" "M5" "quit"
do
case $Machine in
M1) version=10;;
M2|M5) version=15;;
M4|M3) version=27;;
quit) exit;;
*) echo -e '\033[5;31;40mERROR: \033[0m\033[31;40mWrong option.\033[0m';;
esac
break
done
echo "`tput bold`Do you want to run default settings`tput sgr0`"
select settings in "yes" "no" "quit"
do
case $settings in
yes) run=1;;
no) run=0;;
quit) exit;;
*) echo -e '\033[5;31;40mERROR: \033[0m\033[31;40mWrong option.\033[0m';;
esac
break
done
echo "Version: $version"
echo "Default settings: $run"
This should be working fine. If you press 1 for each option, you get
Version: 10
Default settings: 1
Did you perhaps habitually press y instead of 1 to answer "yes"?

./test: line 22: syntax error near unexpected token `done'

#!/bin/bash
# script to create new instance by taking inputs from user
source /root/keystonerc_admin
unset http_proxy
function main {
echo "Please choose:
1. Create instance from an image
2. Create instance from a volume
3. Exit"
While true do
read SELECT
case "$SELECT" in
1) SELECT=func_create_from_image;;
2) SELECT=func_create_from_volume;;
3) SELECT=exit;;
*) echo Invalid selection.; continue
esac
break
done
}
function func_create_from_image {
echo "List of flavors"
nova flavor-list
echo "Enter flavor id from the list"
read flavor_id
echo "List of images"
nova image-list
echo "Enter image id from the list"
read image_id
echo "List of security groups"
nova secgroup-list --all-tenants
echo "Enter security group name from the list"
read secgroup_name
echo "List of keypairs"
nova keypair-list
echo "Enter keypair name from the list"
read keypair_id
echo "Enter display name of instance"
read display_name
## use "set" for debugging
##set -x
nova boot --display $display_name --flavor $flavor_id --image $image_id \
--key_name $keypair_id --security_group $secgroup_name
##set +x
}
function func_create_from_volume {
echo "Please choose:
1. Use existing bootable volume
2. Create new bootable volume
3. Exit"
while true do
read SELECT
case "$SELECT" in
1) SELECT=create_instance_from__existing_volume;;
2) SELECT=create_instance_from_new_volume;;
3) SELECT=exit;;
*) echo Invalid selection.; continue
esac
break
done
}
create_instance_from_new_volume {
echo "Specify image id from the below list"
nova image-list
read image_id
echo "specify size in GB, only specify number"
read vol_size
echo "Specify image name to be displayed in cinder list"
read vol_disp_name
set -x
cinder create --image-id $image_id --display-name $vol_disp_name $vol_size
set +x
echo "volume created"
cinder list
echo "create instance"
create_instance_from__existing_volume
}
create_instance_from_existing_volume {
echo "Specify volume id from the below list"
cinder list
read vol_id
echo "Pick flavor id from the below list"
nova flavor-list
read flavor_id
echo "specify size in GB, only specify number or leave blank for system to decide"
read vol_size
echo "Specify type of volume either snap or other or leave blank if not known"
read vol_type
echo "Specify 1 if volume should be deleted when the instance terminates or specify 2 if volume remains after
instance terminates"
read vol_del
if [$vol_del!=0 or $vol_del!=1] then
echo "Specify either 1 or 0"
fi
echo "Specify display name for instance"
read inst_disp_name
echo "Creating instance"
nova boot --flavor $flavor_id --block_device_mapping vda=$vol_id:$vol_type:$vol_size:$vol_del $inst_disp_name
}
While true do
Bash is case-sensitive - you need while rather than While. And you also need to either move the do to the next line or put a semicolon before it
while true; do
Also your if condition further down is suspect:
if [$vol_del!=0 or $vol_del!=1] then
Aside from the syntax errors, this condition will always be true (since if the value is 1 then it is not 0 and vice versa). I suspect you wanted and rather than or:
if [ $vol_del != 0 -a $vol_del != 1 ]; then
Use
while true; do
instead of
While true do
because do isn't an argument to true command
In theory, do should be on a separate line:
while ....
do
....
done
Same with then:
if .....
then
.....
fi
However, people like putting them on the same line in the K&R Style. To do that, you need a semicolon to show the shell when the while line ends, so the do is on a different line:
while ....; do
....
done
This may no longer be necessary in newer versions of BASH, but it should be done. I prefer using an extra line just because the shell prefers it.
The other problem and the actual problem has to do with the While on line #13 as While (uppercase W and not lowercase w).
Two thing will help catch this in the future:
Use set -xv to turn on debugging and set +xv to turn off debugging. In this case, you'll see the error happens when you enter the while loop when Line #13 gets executed. You can set export PS4=\$LINENO+ to show line numbers when this debugging is on. To turn off debugging, you set +xv.
Use a program editor that has syntax highlighting. I saw this immediately because the While wasn't in the correct color. I took a closer look and saw the capitalized While.

How to print and edit variable content at the same place on runtime of a Unix shell script?

I have been looking all around (with no avail) to perform the following:
I want to display and able to edit if necessary the content of a variable in runtime for a Unix shell script after giving it a value. The idea goes like this:
Suppose we have a variable value either defined in script or by user input
var=12345
Print the variable value, but also leave the cursor in that position of printing and either press just enter to leave it intact or enter a new value in runtime
Edit variable content (press Enter to leave intact) : 12345
at this point at runtime, I want to leave the cursor in the position of number 1, while showing the variable, and working in the way that if I press Enter, leave the original content (12345) or read for a new value in that same place and modify. Clearing the display of the variable when typing anything else than Enter would be a big plus.
I have looked all around for a way to do this, but just haven't found anything. Anyone willing to provide a solution?
I would suggest you doing this in another way:
var=12345
echo "Change the value of Var (current value: $var)"
read -p "New value (Press Enter to skip):" nvar
if [[ "$nvar" != "" ]]; then
var="$nvar"
fi
echo $var
with this, it will prompt:
Change the value of Var (current value: 12345)
New value (Press Enter to skip):
what you wanted in the question (without the "big plus" part), could be achieved too:
var=12345
echo -ne "Now you may want to change the value: \n$var\r"
read nvar
if [[ "$nvar" != "" ]]; then
var="$nvar"
fi
echo $var
with this, it will prompt (cursor sits between ** )
Now you may want to change the value:
*1*2345
With bash, you could use the readline option for read, but that does not put the cursor at the beginning. It does however do what you want
var=12345
read -ep "Enter new value: " -i $var var
If you need the cursor to go back, you can do this:
var=12345
prompt="Enter new value: $var"
for ((i=1; i<=${#var}; i++)); do prompt+=$'\b'; done
read -p "$prompt" new
[[ -z $new ]] && new=$var

Bash user prompt while reading file

I am trying to create a user prompt while reading a file a line by line in Bash. The idea is for me to plot various files one-by-one using Gnuplot. Here is what I have:
#!/bin/bash
echo "Enter filename that contains the filenames:"
read fname
xr="[1e8:1e20]"
yr="[1:1e13]"
while read line
do
echo -e "reset\nset log\nset xrange$xr\nset yrange$yr\nset xlabel \"Frequency [Hz]\"\nset ylabel \"F_{/Symbol n} [Jy Hz]\"\nset key top left\nplot \"$line.dat\" u 3:(\$3*\$4)*1e26 w l ti \"$line^o\" \n"> plt.gp
gnuplot plt.gp
done < $fname
I would like to enter a user input/"continue?" type thing before "gnuplot plt.gp" command, because at the moment it just plots everything rapidly and then exits. The standard read -p command does not work here. I read somewhere I may need to use file descriptor exec 5 command, but I don't understand. Thanks.
#!/bin/bash
read -p 'Enter filename that contains the filenames: ' fname
xr="[1e8:1e20]"
yr="[1:1e13]"
while read line
do
echo -e "reset\nset log\nset xrange$xr\nset yrange$yr\nset xlabel \"Frequency [Hz]\"\nset ylabel \"F_{/Symbol n} [Jy Hz]\"\nset key top left\nplot \"$line.dat\" u 3:(\$3*\$4)*1e26 w l ti \"$line^o\" \n"> plt.gp
gnuplot plt.gp
read -p 'Do you want to continue? [Y/n]: ' want_to_continue </dev/tty
case "${want_to_continue}" in
Y|y)
continue
;;
*)
echo "OK. Bye!"
break
;;
esac
done < ${fname}

Resources