0

I am tweaking a script so that it will ask the user for a file of a specific type. If the file entered does not match the specific file type, it goes into a while loop and repeats until the user enters the correct file.

In this case, I'm asking the user for a .zip file in:

read -p "Enter zip file: " package1
 while [ package1 != *.zip ] ; do
     read -p "Please enter file ending in '.zip': " package1
 done <<< package1
echo $package1

I've had errors until this point. Now, I just get a blank cursor, no output. Whether I enter *.zip or not, it proceeds to the next line with a blank cursor and no output.

Thoughts?

EDIT:

Alright, I've decided to go with the select loop and it's a much better option than while in this case.

But what if that zip file (or any file-type prompted) could be in any other directory? I want it to give the option to change directories while in the select-loop shell.

I tried so far:

echo Select zip file:
                options=(".." *.zip)
                select package1 in "${options[@]}" ; do
                        case $package1 in
                                1 ) cd ..; echo Select zip file: ;;
                                # Different Directory ) ;;
                                * ) break ;;
                        esac
                done

But the loop won't re-display the menu items after changing directories. I am not sure how to offer the user to cd to a totally different path other than ../ if they want, nor what to use for

"All other existing \# file items" ) ... ; break;;

inside of case

2
  • Just a personal pet peeve, but I often grumble when the computer knows it wants a ZIP file, but requires me to type in the .zip at the end of the filename. Since you explicitly require a ZIP file, why not prompt for a regular filename, and then have the script append the .zip? That seems more user-friendly to me. YMMV.
    – Jim L.
    Commented Jun 3, 2019 at 22:34
  • 1
    Why not let the user pass the name of the file or files on the command line where they could just say *.zip or use filename completion? Having a user type in a pathname is just a sure way to frustrate the user, as they can't correct what they type after pressing Enter.
    – Kusalananda
    Commented Jun 4, 2019 at 5:48

4 Answers 4

2

Make it logically easier by examining only the final extension:

while [ "${package1##*.}" != "zip" ]
do
    read -e -p "Specify a .zip file: " package1
done
echo $package1
2

A more elegant solution has been posted but I think it is important to point out some of the errors which are causing your original script to fail and how to fix them.

First, when you are testing the variable package1 in your while loop, you need to put the $ in front of it in order to read the actual data stored in that variable.

Next use [[...]] instead of [...] in your while loop test as this allows you to do the pattern matching using your wildcard *. However, this only works in bash not in sh. Please see this answer for a thorough explanation of using [[...]] versus [...].

Lastly you do not need to pass the string <<< package1 at the end of your while loop.

Here is a modified version of your script which works as intended.

#!/bin/bash

read -p "Enter zip file: " package1
while [[ "$package1" != *.zip ]]; do
    read -p "Please enter file ending in '.zip': " package1
    done 
echo $package1
2
  • Thank you @justincs. What is your opinion on the use of select vs. read in this context, as suggested by @pLumo below?
    – obidyne
    Commented Jun 4, 2019 at 13:14
  • @obidyne I personally prefer to have the user enter in filename as a command line argument when calling the script as others have suggested. Perhaps check for correct extension using SHawarden's method and then exit with an error message if no zip file is specified.
    – justinjt
    Commented Jun 5, 2019 at 3:16
1

Never get the user to type in pathnames of files interactively. It's easier for them to call your script with the pathnames of their Zip archive files on the script's command line. This allows them to use the filename completion facility and filename globbing patterns of the shell.

The script would be called using e.g.

./myscript /some/path/myarchive.zip

or

./myscript folder/archive-*.zip

or similar.

In the script, you would do

#!/bin/bash

for archive_path do
    printf 'Processing archive "%s"...\n' "$archive_path"

   # code to process "$archive_path" goes here
done

The loop in the script would loop over all arguments passed to the script, and in each iteration, "$archive_path" would be the pathname of an individual archive passed to the script.

There's no need to check whether the name of the "$archive_path" file ends with any particular string, as the tools that you would use on the archive may not even care about the filename suffix. If they do, then they would fail with the appropriate exit code, which you easily could catch:

if ! unzip "$archive_path"; then
    printf 'Failed to run unzip on "%s"\n' "$archive_path" >&2
    exit 1
fi

If you use set -e in the script (probably as its first statement), it would exit automatically when any command (which is not part of a conditional statement) returns with a non-zero exit code.

set -e

# The following would terminate the script if it failed:
unzip "$archive_path"

Regarding your added "edit": What you are doing there is starting to implement a file browser tool or file manager for selecting Zip archives. Depending on what you actually want to use this code for, this may well be overkill.

0

If you want the user to select one from existing files, I'd use select instead of read, to let the user select from a list, instead of writing a filename that he might not exactly know.

select package1 in *.zip; do
    break;
done
echo "$package1"

User will be presented with a list and will type the number.

6
  • Thanks @pLumo, this might be the better way to go. What if the zip file, package1 that the user wants is in another directory? Is there a way to cd to it while in select? That might be a dumb question.
    – obidyne
    Commented Jun 4, 2019 at 13:54
  • sure, use relative or absolute path, e.g.: select package1 in /path/to/*.zip; do ...
    – pLumo
    Commented Jun 4, 2019 at 14:04
  • It is worth noting that this will only work for accessing existing files. If they are creating a new file, manual user input will still be required.
    – SHawarden
    Commented Jun 4, 2019 at 22:19
  • Sure, I clarified this.
    – pLumo
    Commented Jun 5, 2019 at 7:52
  • Existing files are exactly what I want to stick with, so that's good. What if the file that the user wants exists in a different directory/ any directory? I'd like the option to cd from the select-loop during script execution, and have it display menu items afterward. It stays blank after I try ) cd .. ; Also, is there a way to have multiple choice inputs for one or more menu item, like ```
    – obidyne
    Commented Jun 12, 2019 at 13:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .