LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Slackware (https://www.linuxquestions.org/questions/slackware-14/)
-   -   Bash scripting and user input (https://www.linuxquestions.org/questions/slackware-14/bash-scripting-and-user-input-377983/)

Woodsman 10-29-2005 03:58 AM

Bash scripting and user input
 
Looking for some nudging to help me write a bash script.

I'm browsing the bash tutorials, but am coming up short with additional examples that will help me. I want to do the following:

Code:

if [ ! -z $PS1 ] ; then
    # Is this script being run manually? Don't ask this question if non-interactive.
    echo "Warning message: blah, blah, blah."
    echo -n "Do you want to continue? (any response other than y/yes will terminate the script)."
    read response
    if [ $response != y ] && [ $response != Y ] && [ $response != yes ] && [ $response != YES ] ; then
      # would be nice if I could parse the first letter typed
      exit
    fi
fi
# continue script . . .

umount /mnt/*
# terminate script if umount fails to unmount any partition

# continue script . . .

How do I ignore case (similar to BASIC LCase command)?
How do I parse the response (similar to BASIC Left(String,1) command)?

Additionally, the script will unmount /mnt partitions using umount /mnt/*. I would like the script to terminate gracefully, with an error message to the screen, if the umount command fails while trying to unmount any partition.

I'm not asking for a full fledged script, just some help or nudging in the correct direction or some links that will help me learn. I am the only one running this script. Therefore I "know" what responses will work properly, but I'd like to use this exercise to improve my knowledge of bash and write a more [strikethrough]idiot-proof[/strikethrough] robust script.

TIA!

P.S. Is there no strikethrough bbcode at this board like at other boards?

unSpawn 10-29-2005 07:21 AM

How do I ignore case
Say you want to match responses y, yes and yeah, you could grep case-insensitive:
echo "$response"|grep -qie "y[a-z]\{0,3\}" && do_cmd
or convert case:
case "$(echo "$response"|tr "[A-Z]" "[a-z]")" in y|yes|yeah) do_cmd;; *) exit 1;; esac


How do I parse the response
See "man test"?, BTW write this:
if [ $response != y ] && [ $response != Y ] && [ $response != yes ] && [ $response != YES ] ; then
as:
if [ $response != y -o $response != Y -o $response != yes -o $response != YES ] ; then
, but convert to lower + "case" should be easier here.


I would like the script to terminate gracefully, with an error message to the screen, if the umount command fails while trying to unmount any partition.
You're looking for the exit status of a command (echo "$?"), but using "umount /mnt/*" isn't gonna work right if you want to show the status for all devices, besides you don't want to exit if it fails on the first umount?

# Since everything is mounted through /proc we'll take input from there.
# This way we avoid globbing symlinks and don't umount stuff that isn't mounted.
grep "^/dev/[h,s]d" /proc/mounts | while read garbage part; do
echo -en "Umounting ${part}: "; umount "$part" 2>&1>/dev/null; case "$?" in 0) r=OK;; *) r=FAILED;; esac; echo $r
done

If you OTOH would only want to be shown errors on exit:
parts=""; grep "^/dev/[h,s]d" /proc/mounts | while read no part; do
umount "$part" 2>&1>/dev/null; case "$?" in 0) ;; *) parts="$parts $part";; esac
done; echo "These partitions failed to umount properly: ${parts}"


improve my knowledge of bash and write a more idiot-proof robust script.
That starts IMHO by not trusing sanitising user input.
Here's a stupid example that only allows some chars to be used:
# Read-only variable
declare -r allowedchars="/._-1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
expr index "$response" "${allowedchars}" >/dev/null || ( echo "Input contains illegal chars, exiting."; exit 1 )


P.S. Is there no strikethrough bbcode at this board like at other boards?
No. Use a felt tip pen :-]

Alien Bob 10-29-2005 08:47 AM

Re: Bash scripting and user input
 
Quote:

Originally posted by Woodsman
How do I parse the response (similar to BASIC Left(String,1) command)?
Use ${response:0:1} if you're interested in only the 1st character of the response.
Like this:
Code:

if [ ${response:0:1} != y -o ${response:0:1} != Y ]; then ........
Eric

Rojon 10-29-2005 09:39 AM

Not sure if this will help or not but the last time i wanted to do something depending on user input i used the case statement (i suppose it is similar to a previous example).

Code:

read ANSWER
        # determine user input
        case "$ANSWER" in
              "Y"|"y")
                      Do something...
                      echo "Done something!"
                      ;;
              "N"|"n")
                    echo "Exiting!"
                    exit 0
                    ;;
      esac

Hope that helps..

Cheers

Woodsman 10-30-2005 06:57 PM

Wow. Way too much for my puny brain. :) Actually, there is some good info here that I want to sift and use.

For example, I like the idea of limiting user input. From the above example, if I want to limit input to n/N/y/Y do I merely delete all of the characters except those four? That is, would I type:

allowedchars="nyNY"

Additionally, if one of those four characters is not entered, how do I loop back to my originating question, rather than exiting the script? Yeah, I know, I did not ask or infer that in my original post. :)

Parsing the first character of the response: ${response:0:1}. That looks slick. With that tip I eventually found the convention mentioned in the Advanced Bash Scripting guide. Cool! :)

My original question is backup related. Thus, I want to still test if my umount command works (using /proc/mount as described above), but I also want to still query the user (me) if I am sure I want to continue. Grep is still A Strange Thing to me, but I'll use the examples provided to hopefully go further.

Regarding the ability to return the exit status of a command, the above example used echo "$?" as part of a case-esac procedure. After some quick surfing I think I can conclude that this command is a special 'nix variable? I'm trying to learn more about this because I have another script where I need to know this information.

Question: I notice that many people often end their scripts with exit 0. Is this A Good Thing or common practice? I notice PV does this in most or all of his rc.d scripts. What is the reasoning behind this practice?

Alien Bob 10-31-2005 03:54 AM

You could try this:

Code:

allowedchars="YyNn"
response=
while ! [[ "${response:0:1}" =~ "[$allowedchars]" ]] ; do
  read -er -p "Do you want to continue? (y/n): " response
done

This code block will keep asking the same question until the answer starts with either N,n,Y or y.
Then you can evaluate the response and act upon it.

Eric

Rojon 10-31-2005 12:16 PM

Quote:

Question: I notice that many people often end their scripts with exit 0. Is this A Good Thing or common practice? I notice PV does this in most or all of his rc.d scripts. What is the reasoning behind this practice?
exit ends the shell script with a status code.

- zero equals success
- nonzero equals failure

Hope that helps.

Woodsman 10-31-2005 07:12 PM

Quote:

This code block will keep asking the same question until the answer starts with either N,n,Y or y.
Then you can evaluate the response and act upon it.
Thanks Eric! This will help.

Quote:

exit ends the shell script with a status code.

- zero equals success
- nonzero equals failure
I understand that part, but I was seeking a response geared more about the overall philosophy of ending a script with exit 0. Why bother? That is, I presume after ample testing of a script everything works as expected and if one embeds reasonable error trapping within the script, then the script always terminates gracefully. So why include exit 0 as the last line in a script?

Alien Bob 11-01-2005 03:00 AM

Quote:

Originally posted by Woodsman
I understand that part, but I was seeking a response geared more about the overall philosophy of ending a script with exit 0. Why bother? That is, I presume after ample testing of a script everything works as expected and if one embeds reasonable error trapping within the script, then the script always terminates gracefully. So why include exit 0 as the last line in a script?
It can even have unexpected results to end a script with the exit statement.
Several scripts are being "sourced" in Slackware's init scripts (i.e. run like ". somescript" which can also be written as "source somescript"). The "." or "source" command is a shell internal command. Sourcing a script means that the script is executed in the current shell instead of spawning another shell to run the script's commands.
Now, if the sourced script calls "exit", your parent script will exit too, right there!

Try this:

Copy this into a shell script, call it test1.sh and make it executable:
Code:

#!/bin/sh
echo "About to take test2"
. test2.sh
echo "I passed test2"

Copy this into a second shell script, call this one test2.sh and make it executable:
Code:

#!/bin/sh
echo "I am inside test2 now"
exit 0

And then test these by running

Code:

./test1.sh
Now, change the line ". test2.sh" in the test1.sh script so that it reads "test2.sh" and run test1.sh again. See the difference when test2.sh is sourced?

Eric

Woodsman 11-01-2005 04:17 PM

Quote:

It can even have unexpected results to end a script with the exit statement.
That all makes sense. I'm grokking. I was only curious if ending a script with exit 0 is a common programming practice and if so, why.

In a much related question to this scripting business, thanks to the previous information, I know how to parse text from a variable. But how do I assign text to a variable extracted through grep? I've been searching for an hour or so for this, but to no avail. :( Maybe the answer in obvious to all casual observers but me. What I want is:

VARIABLE=(grep -i "text to find" "file to search")

Additionally, how do I calculate the string length of a variable?

LENGTH=(count the number of characters in VARIABLE)

I imagine some kind of For-Do loop, but the syntax of all of this bashing is still new to me. If this info is covered in one of the primary bash guides, then just point me to the appropriate sections of those guides. Thanks again!

Alien Bob 11-01-2005 04:23 PM

Like this:
Code:

VARIABLE=`grep -i "text to find" "file to search"`
and the length of a variable:
Code:

${#VARIABLE}
Cheers, Eric

Woodsman 11-01-2005 05:07 PM

Quote:

VARIABLE=`grep -i "text to find" "file to search"`
Ah! The infamous back ticks! Makes so much sense with only one example. Thank you!

Quote:

${#VARIABLE}
Works like a charm! Again, makes so much sense with only one example. Groovious!

Alien Bob 11-02-2005 03:04 AM

By the way, a must-have for your bookmarks is this URL: the Advanced Bash-Scripting Guide

Cheers, Eric

Woodsman 11-02-2005 02:20 PM

Quote:

Code:

allowedchars="YyNn"
response=
while ! [[ "${response:0:1}" =~ "[$allowedchars]" ]] ; do
  read -er -p "Do you want to continue? (y/n): " response
done


This works great! However, I admit that I am perplexed by the absense of a simple CHOICE command. When I started writing this script the first thing I looked for was an equivalent CHOICE command. Unusual! Seems the 'nix world is otherwise full of these small one-task commands.

Quote:

By the way, a must-have for your bookmarks is this URL: the Advanced Bash-Scripting Guide
Agreed! Been there done that! :)

But I'm stuck on stinking dialup. Therefore I downloaded both the HTML and PDF versions so I can browse more conveniently.

I've provided technical writing services for two decades and I learned long ago that even the best of documentation is useless if one does not form the question that suits the style of the documentation. I think most of the answers provided in this thread, and in a few others I've posted recently, actually are documented in the three bash guides, but to a beginning basher who does not yet know the language and syntax of bash I was unable to find my solution in the guides until after all of you provided the necessary hints. And I'm grateful for that!

Yes, I could sit and actually read the entire guide, all of them, but as I'm sure many will agree, reading is not the same as doing. Actually writing a script paints one into a corner that necessitates asking specific questions, and that process far outpaces trying to read and sponge an entire document into memory!

I do not consider myself a noob at computers (been using them for more than two decades) or Slackware (been using for a year), but the entire 'nix environment is far too complex for anybody to master fully. For that reason alone, I'm grateful for all of you expert Slackers here at LQ. Thank you!

Oh, and FWIW, I now have some nice bash scripts thanks to all of the bite-size hints and answers provided in these threads. Again thank you all!

I only hope as time progresses that I too can provide some meaningful help to other people.


All times are GMT -5. The time now is 06:32 AM.