LinuxQuestions.org
Review your favorite Linux distribution.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 08-26-2019, 06:48 PM   #1
upnort
Senior Member
 
Registered: Oct 2014
Distribution: Slackware, Proxmox, Debian, CentOS, Ubuntu MATE
Posts: 1,111

Rep: Reputation: Disabled
Shell script: Creating a numbered vertical select list with strings rather than single words


I'm trying to create a select list with verbosity. I want the list items to be strings -- have spaces.

I have the following snippet that works fine.

Code:
PS3="Select a task: "
# Present the tasks in a menu.
ALL_TASKS="Backup Upload Update Other"
select task in $ALL_TASKS; do
  if [ "$task" != "" ]; then
    echo
    TASK="$task"
    break
  else
    echo "Please select a valid task number."
  fi
done
The output:

Code:
1) Backup
2) Upload
3) Update
4) Other
Select a task:
I want more verbosity in the list so the output is like this:

Code:
1) Backup device configuration
2) Upload a file to the device
3) Update the device software
4) Other
Select a task:
I tried all sorts of Twister combinations of quotation marks and the tr command. No luck. I end up with a wide screen horizontal list like this:

Code:
1) Backup device configuration	3) Update the device software
2) Upload a file to the device	4) Other
Or a partially numbered list:

Code:
1) "Backup device configuration
Upload a file to the device
Update the device software
Other"
I looked around the web. The examples presume single words for the list.

I appreciate suggestions.

Thanks.

Last edited by upnort; 08-26-2019 at 06:55 PM.
 
Old 08-26-2019, 07:10 PM   #2
BW-userx
LQ Guru
 
Registered: Sep 2013
Location: Somewhere in my head.
Distribution: FreeBSD/Slackware-14.2+/ArcoLinux
Posts: 9,078

Rep: Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903
one way..
defaulting to bash shell script.
Code:
#!/usr/bin/env bash

flag=1

list=(
"1) Backup device configuration"
"2) Upload a file to the device"
"3) Update the device software"
"4) Other"
)

message()
{
	echo
	printf "%s\n" "${list[@]}"
	read -p "Select a task: " task
	echo
}
message


while [[ $flag = '1' ]]  
do
{
	case $task in
		1)
			echo "1" 
			flag=0
			;;
		2)
			echo "2" 
			flag=0
			;;
		3) 
			echo "3" 
			flag=0
			;;
		4)
			echo "4" 
			flag=0
			;;
		*)
			echo "enter a number in selection"
			flag=1
			message
			 
			;;
	esac
}
done

Last edited by BW-userx; 08-26-2019 at 07:24 PM.
 
1 members found this post helpful.
Old 08-26-2019, 07:21 PM   #3
michaelk
Moderator
 
Registered: Aug 2002
Posts: 18,927

Rep: Reputation: 2851Reputation: 2851Reputation: 2851Reputation: 2851Reputation: 2851Reputation: 2851Reputation: 2851Reputation: 2851Reputation: 2851Reputation: 2851Reputation: 2851
How select displays the menu is determined by the value of the COLUMNS environment variable which sets the terminal column width. In your case set COLUMNS to anything less or equal to the number of characters of your longest line should work.

Code:
#!/bin/bash

PS3="Select a task: "
COLUMNS=30
# Present the tasks in a menu.
ALL_TASKS=( "Backup device configuration" "Upload a file to the device" "Update the device software" "Other" )
select task in "${ALL_TASKS[@]}"; do
  if [ "$task" != "" ]; then
    echo
    TASK="$task"
    break
  else
    echo "Please select a valid task number."
  fi
done

Last edited by michaelk; 08-26-2019 at 07:46 PM.
 
2 members found this post helpful.
Old 08-26-2019, 07:51 PM   #4
Firerat
Senior Member
 
Registered: Oct 2008
Distribution: Debian sid
Posts: 1,992

Rep: Reputation: 549Reputation: 549Reputation: 549Reputation: 549Reputation: 549Reputation: 549
here you go

Code:
unset a_bash_array ## just incase 
a_bash_array=("Backup device configuration") 
a_bash_array+=("Upload a file to the device")
a_bash_array+=("Update the device software")
a_bash_array+=("Other")
## note ----^ the + 
select task in "${a_bash_array[@]}"; do
  if [ "$task" != "" ]; then
    echo
    TASK="$task"
    break
  else
    echo "Please select a valid task number."
  fi
done
you could just do
Code:
select task in "Backup device configuration" "Update the device software" "Upload a file to the device" "Other";do
but the bash array is more fun , and more useful since it needn't be hardcoded
you can fill the array progmatically

Last edited by Firerat; 08-26-2019 at 10:37 PM. Reason: fixed as per rnturn's post, I call it a NoVimo instead of a typo..
 
Old 08-26-2019, 08:11 PM   #5
Firerat
Senior Member
 
Registered: Oct 2008
Distribution: Debian sid
Posts: 1,992

Rep: Reputation: 549Reputation: 549Reputation: 549Reputation: 549Reputation: 549Reputation: 549
something I forgot to mention

your $TASK will be the whole string, which might get ugly when you want act upon it

so BW-userx's is better for hardcoded stuff like your menu entries suggest
 
Old 08-26-2019, 08:22 PM   #6
BW-userx
LQ Guru
 
Registered: Sep 2013
Location: Somewhere in my head.
Distribution: FreeBSD/Slackware-14.2+/ArcoLinux
Posts: 9,078

Rep: Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903Reputation: 1903
I had to go look up that select seeing that it had to be a bash tricky thing.

mod:
and what @Firerat said, as this is what it is going to look like to get that setup to work using select.
Code:
PS3="enter number yo "

unset a_bash_array ## just in case 
a_bash_array=("Backup device configuration") 
a_bash_array+=("Upload a file to the device")
a_bash_array+=("Update the device software")
a_bash_array+=("Other")

select task in "${a_bash_array[@]}"; do
case $task in
		"Backup device configuration")
			echo "1" 
			break
			;;
		"Upload a file to the device")
			echo "2" 
			break 
			;;
		"Update the device software") 
			echo "3" 
			break 
			;;
		"Other")
			echo "4" 
			break
			;;
		*)
			echo "enter a number in selection"
			
			;;
	esac
done
source:
https://bash.cyberciti.biz/guide/Select_loop

Last edited by BW-userx; 08-26-2019 at 08:25 PM.
 
Old 08-26-2019, 09:45 PM   #7
Firerat
Senior Member
 
Registered: Oct 2008
Distribution: Debian sid
Posts: 1,992

Rep: Reputation: 549Reputation: 549Reputation: 549Reputation: 549Reputation: 549Reputation: 549
@BX
yeah, it can be done with case, for a for loop
Code:
for i in ${!array[@]};do
    [[ ${array[i]} == ${TASK} ]] && echo $i
done
${!array[@]} returns the index

by default index starts at 0
we could do
Code:
array=( a="foo bar" b="ffoo bbar")
array+( c="foobar" )
must admit I never use that, I think there is some odd thing where you have to initialise it first


the form of select I presented has worked for me in the past
where I had json data
I presented human readable part of the json
which I could then use to get the needed id from the json

hope that makes sense

edit: this may make more sense
Code:
id=$(
    while read name;do
        S_Names+=("${name}")
    done < <(<.info jq -j ".data[]|.seriesName,\"\n\"")

    select Series in "${S_Names[@]}" "quit";do
        [[ ${Series} == quit ]] && exit
        <.info jq -j ".data[]|if .seriesName == \"${Series}\" then .id else empty end"
        break
    done
)
if you have ever greped/seded/awked json data
https://stedolan.github.io/jq/
jq is much easier

Last edited by Firerat; 08-26-2019 at 10:06 PM.
 
Old 08-26-2019, 10:15 PM   #8
rnturn
Senior Member
 
Registered: Jan 2003
Location: Illinois (Chicago area)
Distribution: CentOS, MacOS, [Open]SuSE, Raspian, Red Hat, Slackware, Solaris, Tru64
Posts: 1,478

Rep: Reputation: 154Reputation: 154
[QUOTE=Firerat;6029880]here you go

Code:
unset a_bash_array ## just incase 
a_bash_array=("Backup device configuration") 
a_bash_array+=("Upload a file to the device")
a_bash_array+=("Update the device software")
a_bash_array+=("Other")
## note ----^ the + 
select task in "${a_bash_array[@]}"; do
  if [ "$task" !=  ]; then
    echo
    TASK="$task"
    break
  else
    echo "Please select a valid task number."
  fi
done
The above results in a " [: Other: unary operator expected" error in the "if" test. Changing it to:
Code:
if [ "$task" != "" ]; then
or
Code:
if [ -n "$task" ]; then
eliminates that.
 
Old 08-26-2019, 10:34 PM   #9
Firerat
Senior Member
 
Registered: Oct 2008
Distribution: Debian sid
Posts: 1,992

Rep: Reputation: 549Reputation: 549Reputation: 549Reputation: 549Reputation: 549Reputation: 549
Quote:
Originally Posted by rnturn View Post
The above results in a " [: Other: unary operator expected" error in the "if" test. Changing it to:
Code:
if [ "$task" != "" ]; then
or
Code:
if [ -n "$task" ]; then
eliminates that.
yeap

at some point I must have clicked with mouse, started typing realised I was typing in wrong place
I may have hit escape and ctrl-u at some point, which does something different in firefox to vim
 
Old 08-27-2019, 04:58 AM   #10
GazL
LQ Guru
 
Registered: May 2008
Posts: 5,156
Blog Entries: 18

Rep: Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849
Here's one without using arrays:
Code:
#!/usr/bin/bash

choose()
{
  local PS3

  PS3="Make your choice [1-$#]? "
  select CHOICE in "$@"
  do
    [[ "$CHOICE" ]] && break
    echo "\"${REPLY}\" is not a valid option." >&2
  done
}

if choose "Option one" "Option two" "Option three" ; then
  echo -e "\nYou chose: $REPLY \"$CHOICE\""
fi
 
1 members found this post helpful.
Old 08-27-2019, 05:52 AM   #11
Firerat
Senior Member
 
Registered: Oct 2008
Distribution: Debian sid
Posts: 1,992

Rep: Reputation: 549Reputation: 549Reputation: 549Reputation: 549Reputation: 549Reputation: 549
Quote:
Originally Posted by GazL View Post
Here's one without using arrays:
Nice
but is it not an array ?
Code:
$0 = choose
$1 = "Option one"
$2 = "Option two"
$3 = "Option three"


I do like that REPLY var,

I can abuse that
Code:
action1(){
echo did 1
}
action2(){
echo did 2
}
array=( "Option one=action1" "Option two=action2" )
select CHOICE in "${array[@]%=*}";do
    [[ "$CHOICE" ]] && break
done;${array[$(($REPLY-1))]#*=}
That will also work well for OP

Code:
Backup(){
back up script
}
ALL_TASKS=( "Backup device configuration=Backup" )
ALL_TASKS+=( "Upload a file to the device=Upload" )
ALL_TASKS+=( "Update the device software=Update")
....
${ALL_TASKS[$(($REPLY-1))]#*=}

I think I need to read up on the internal vars, I either never knew or forgot REPLY

although I almost never use select.
I think I will be inventing reasons to use it now

Last edited by Firerat; 08-27-2019 at 05:57 AM. Reason: removed some white lines to rid scroll bar
 
1 members found this post helpful.
Old 08-27-2019, 07:29 AM   #12
GazL
LQ Guru
 
Registered: May 2008
Posts: 5,156
Blog Entries: 18

Rep: Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849Reputation: 2849
Quote:
Originally Posted by Firerat View Post
Nice
but is it not an array ?
Code:
$0 = choose
$1 = "Option one"
$2 = "Option two"
$3 = "Option three"
touché. I suppose it is at that.

And yes, I rarely use it either.

Last edited by GazL; 08-27-2019 at 07:34 AM.
 
Old 08-28-2019, 01:45 PM   #13
upnort
Senior Member
 
Registered: Oct 2014
Distribution: Slackware, Proxmox, Debian, CentOS, Ubuntu MATE
Posts: 1,111

Original Poster
Rep: Reputation: Disabled
Thanks all for the replies. Always something new to learn!

For the moment I settled on the COLUMNS solution.

With some tinkering I noticed in my Xfce terminal that setting COLUMNS=77 resulted in single lines and COLUMNS=78 in a wrapped list. My Xfce terminal window is set to 120 columns wide. I presumed the terminal window preference would be controlling but the switch over occurred at 77-78 characters. Explaining why goes deep down the terminal emulator rabbit hole and is not important here. I do have shopt -s checkwinsize set in my .bashrc and that is our default for all systems at work.

Thanks everybody!
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
LXer: Words, Words, Words--Introducing OpenSearchServer LXer Syndicated Linux News 0 08-07-2019 02:13 PM
how i can fetch some strings like words or some words Farah_s Linux - Newbie 6 03-14-2012 01:23 AM
[SOLVED] Select words with alphabetical-order character strings danielbmartin Programming 7 02-17-2012 12:08 AM
List 4 names from users list and output them to fbusers in numbered ascending order? fezzie Programming 4 02-10-2010 01:05 PM
how to find duplicate strings in vertical column of strings markhod Programming 7 11-02-2005 04:04 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 06:45 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration