I'm writing a script to perform common tasks I like to do with a fresh install of Linux. It has functions for each phase from updating the system to installing common software.
Right now I'm trying to have the script read a list of software from a text file, ask the user if they would like to install it. If they say "yes" it would run apt to install that software. The current draft of the software has echo statements with the commands to avoid making changes while I test the script. Here is the function I'm trying to set up.
InstallAptSW () {
file="./apps/apt-apps"
while read -r line; do
read -p "Would you like to install $line? [Y/n]" yn
yn=${yn:-Y}
case $yn in
[Yy]* ) echo "sudo apt install -y $line";;
[Nn]* ) printf "\nSkipping";
break;;
* ) echo 'Please answer yes or no.';;
esac
done < $file
}
The apps file is just a list of software such as
code
gparted
snapd
neofetch
etc...
Here is the current result of the function:
$USER@$HOSTNAME:~/Documents/popOS-post-install$ ./PopOS-Post-Install.sh
Please answer yes or no.
Please answer yes or no.
Please answer yes or no.
Please answer yes or no.
Please answer yes or no.
Please answer yes or no.
Please answer yes or no.
Goodbye
CodePudding user response:
I would use a new file descriptor for reading the file; that way you'll be able to read the user input inside the loop:
Edit: @LéaGris method is more standard, you should use it instead of mine. You can test my code though, I changed the behavior a little bit.
InstallAptSW () {
local file="./apps/apt-apps"
local fd appname yn
exec {fd}< "$file"
while IFS='' read -r appname <&$fd
do
while true
do
read -N 1 -p "Would you like to install '$appname'? [Y/n]" yn
[[ $yn ]] && echo
yn=${yn:-Y}
case $yn in
[Yy]) echo "sudo apt install -y $(printf %q "$appname")"
# sudo apt install -y "$appname"
break
;;
[Nn]) echo "skip"
break
;;
esac
done
done
exec {fd}<&-
}
CodePudding user response:
Because the input for the while; do...done < "$file" code block is handled from the file containing the software names to install; the read -rp "Would you like to install $line? [Y/n]" yn which was not given a specific input handler, just inherits the file input from its outer code block.
Rather than reading user input, it reads (consumes) lines from the software list file.
Just adding <&1 to your read for user input should fix your script.
Here it is with a couple other fixes:
#!/usr/bin/env bash
InstallAptSW() {
file='./apps/apt-apps'
while read -r line; do
# printf before read -r avoid using the read -p bashism
printf 'Would you like to install %s [Y/n]? ' "$line"
# Gives read the standard input handler
# rather than having it inherit the file handler
# from the outer while loop
read -r yn <&1
yn=${yn:-Y}
case $yn in
[Yy]*) echo sudo apt install -y "$line" ;;
[Nn]*)
# Single-quotes are better
# when no expansion occurs within string
printf '\nSkipping'
break
;;
*) echo 'Please answer yes or no.' ;;
esac
# Double quote the "$file" variable to prevent
# word splitting and globing pattern matching
done < "$file"
}
InstallAptSW
