3
\$\begingroup\$

I wrote the following script in the hopes of streamlining the finding and reading of multiple manual pages. Since I am always looking up different utilities' manual pages I thought this would a good learning aid.

#!/bin/bash
while true
do
 echo
 echo "1) /bin"
 echo "2) /sbin"
 echo "3) /usr/bin/"
 echo "4) /usr/sbin"
 echo "5) /usr/local/bin"
 echo "6) /usr/local/lib"
 echo "7) /usr/local/share"
 echo "8) /usr/local/include"
 echo "9) exit"
 echo
 read -p 'Select a directory ' d
 if [[ $d = 1 ]]; then
 dir=/bin
 elif [[ $d = 2 ]]; then
 dir=/sbin
 elif [[ $d = 3 ]]; then
 dir=/usr/bin
 elif [[ $d = 4 ]]; then
 dir=/usr/sbin
 elif [[ $d = 5 ]]; then
 dir=/usr/local/bin
 elif [[ $d = 6 ]]; then
 dir=/usr/local/lib
 elif [[ $d = 7 ]]; then
 dir=/usr/local/share
 elif [[ $d = 8 ]]; then
 dir=/usr/local/include
 elif [[ $d = 9 ]]; then
 exit
 else
 echo 'No such directory'
 exit 1
 fi
 echo
 while true
 do
 menu=( $(ls -1 ${dir}) )
 i=0
 for m in ${menu[@]}
 do 
 echo "$(( i++ ))) $(basename $m)"
 done | xargs -L3 | column -t
 echo
 echo 'Select from the list above'
 echo 'Type b to go back to main menu'
 read -p 'Type q to quit at anytime ' n
 echo
 if [[ $n = 'b' || $n = 'B' ]]; then 
 break 1
 elif [[ $n = 'q' || $n = 'Q' ]]; then
 exit
 else 
 for item in ${menu[$n]}
 do 
 if [[ $item =~ '.txt' ]]; then
 item="$(echo ${item%.*})"
 fi
 man $item
 done
 fi
 done
done

One thing I should point out. My /usr/local/bin directory has several .txt files. That is the reason for if [[ $item =~ '.txt' ]]; then item="$(echo ${item%.*})"; fi Maybe it is normal to have plain text files in this directory or maybe this is something I did unintentionally when experimenting writing this script. (An earlier version of this script wrote all the man pages to plain text files.) I really don't know but that's why that part of the script is there.

I think this script does what I hoped it would do so now I would like other people's opinion. Is there anything I could do better? Am I overlooking anything?

asked Sep 16, 2016 at 3:28
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Use a table driven menu

The menu to select from /bin, /sbin and other is very repetitive. A table-based approach would be more compact, easier to write and maintain. Try to use an array, as you did in other places of the script.

First, put the directories in an array, for example:

dirs=('' /bin /sbin /usr/bin) # fill the rest

Note that I added a dummy empty first element. This is to make the 1-based indexes of your menu work with the 0-based array indexes.

Build the menu from this array using a counting loop from 1, the first non-empty element:

for ((index = 1; index < ${#dirs[@]}; index++)); do
 echo "$index) ${dirs[$index]}"
done

To validate the user input, simply check if ${dirs[$index]} is empty. If yes, the input is invalid.

Security

Maybe it is normal to have plain text files in this directory or maybe this is something I did unintentionally when experimenting writing this script. (An earlier version of this script wrote all the man pages to plain text files.)

No, it is not normal to have .txt files in any of those directories. So I guess your earlier script put them there. But a normal user should not have write access to these directories. Which seems to suggest that you're playing with a privileged account, possibly root. That's a bad idea, avoid using root casually.

Simplify

You can simplify [[ $n = 'b' || $n = 'B' ]] using pattern matching: [[ $n == [bB] ]].

Strangeness

You don't need the -1 here.

menu=( $(ls -1 ${dir}) )

When inside $(...), ls does not columnize its output.


In this code, I don't see why you need basename:

echo "$(( i++ ))) $(basename $m)"

It seems the value of $m will always be a simple filename without a path part.

answered Sep 16, 2016 at 23:20
\$\endgroup\$
4
  • \$\begingroup\$ Thank you I have put the directories into an array as you suggested. But I don't understand what you mean by "To validate the user input, subtract 1 to offset the +1 we printed for display purposes, and then check if ${dirs[$index]} is empty. If yes, the input is invalid" Could you explain this a little more? \$\endgroup\$ Commented Sep 19, 2016 at 22:50
  • \$\begingroup\$ What I meant was, be careful that array indexes are 0-based, but your menu options are 1-based. So when displaying the menu, we show array index+ 1, and when parsing user input, we must do the reverse and apply -1 to convert user input to array index. Actually there's a simpler way, I now realize: make the first array element an empty dummy element. That way you can display the menu starting from index 1, and then the user input must directly correspond to array indexes, no more math is necessary \$\endgroup\$ Commented Sep 20, 2016 at 5:12
  • \$\begingroup\$ I updated my answer with this latest idea to simplify the index manipulations \$\endgroup\$ Commented Sep 21, 2016 at 5:37
  • \$\begingroup\$ Way late to the party, but $dir/* and select would be nicer than ls and a hand-built menu. \$\endgroup\$ Commented Jun 4, 2019 at 3:18

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.