Ceci la réponse montre un menu bash génial et intuitif où vous appuyez simplement sur le numéro et l’article sélectionné Mais c'est un peu gênant pour une liste de fichiers, car tout est codé en dur. Je préfère remplir mes fichiers dans une sorte de tableau, puis laisser l'utilisateur choisir un nombre qui correspond à nouveau au décalage de tableau.
Fondamentalement, voici ce que j'imagine:
Following `*.war` archives were found, select one:
1) old.war
2) debug.war
3) release.war
Use number to select a file or 'stop' to cancel: blah
'blah' is not a number
Use number to select a file or 'stop' to cancel: 2
debug.war installed
Mais comment transformer une liste de fichiers dans ce tableau:
options=("Option 1" "Option 2" "Option 3" "Quit")
Comment obtenir une chaîne à un certain décalage dans options
? Comment puis-je m'assurer que l'utilisateur est invité à réessayer? Puis-je autoriser la chaîne stop
à arrêter le mode de sélection?
Pour enregistrer les sorties de find
dans un tableau bash
, utilisez ceci:
unset options i
while IFS= read -r -d $'\0' f; do
options[i++]="$f"
done < <(find /dir/ -maxdepth 1 -type f -name "*.war" -print0 )
read
lit les entrées de find
délimitées null (-d $'\0'
). $options
est rempli avec les noms de fichiers.find
recherche uniquement les fichiers (-type f
) dans le répertoire donné (-maxdepth 1
) avec la fin .war
(-name "*.war"
) et les imprime délimités par le caractère nul (-print0
).Le menu de sélection peut être fait comme ceci:
select opt in "${options[@]}" "Stop the script"; do
case $opt in
*.war)
echo "War file $opt selected"
# processing
;;
"Stop the script")
echo "You chose to stop"
break
;;
*)
echo "This is not a number"
;;
esac
done
Cela fonctionne comme suit:
1) /dir/old.war
2) /dir/debug.war
3) /dir/release.war
4) Stop the script
#? test
This is not a number
#? 2
War file /dir/debug.war selected
#? 4
You chose to stop
Vous pouvez également utiliser un glob Shell pour obtenir la liste des fichiers. Cette approche présente l’avantage de ne pas utiliser de programme externe (find
) et de ne pas avoir besoin de restrictions sur le type de fichier (*war
, par exemple) étant donné:
#!/usr/bin/env bash
## Collect the files in the array $files
files=( ~/foo/war/* )
## Enable extended globbing. This lets us use @(foo|bar) to
## match either 'foo' or 'bar'.
shopt -s extglob
## Start building the string to match against.
string="@(${files[0]}"
## Add the rest of the files to the string
for((i=1;i<${#files[@]};i++))
do
string+="|${files[$i]}"
done
## Close the parenthesis. $string is now @(file1|file2|...|fileN)
string+=")"
## Show the menu. This will list all files and the string "quit"
select file in "${files[@]}" "quit"
do
case $file in
## If the choice is one of the files (if it matches $string)
$string)
## Do something here
echo "$file"
## Uncomment this line if you don't want the menu to
## be shown again
# break;
;;
"quit")
## Exit
exit;;
*)
file=""
echo "Please choose a number from 1 to $((${#files[@]}+1))";;
esac
done
select
peut faire la plupart de ces tâches pour vous, sans trop d'efforts.
Comment transformer une liste de fichiers dans ce tableau?
Vous n'avez pas réellement besoin de. select
prend une série de mots à afficher en tant qu'options. Ceux-ci peuvent être donnés directement (select color in red green blue
) ou proviennent de l'expansion d'un fichier glob (select file in *.war
), ainsi que de développer un tableau en mots comme le fait l'exemple que vous avez trouvé (select option in "${options[@]}"
).
Comment puis-je obtenir une chaîne à un certain décalage dans les options?
select
le fait automatiquement et le stocke dans la variable que vous fournissez. (Si l'entrée de l'utilisateur n'est pas valide, il stocke la chaîne vide.)
Comment puis-je m'assurer que l'utilisateur est invité à réessayer?
Encore une fois, select
le fait pour vous, car select crée une boucle, comme while
name__. Il continuera à vous demander jusqu’à ce que vous ayez break
sorti de la boucle (ou jusqu’à ce qu’il lise EOF, généralement entré par Ctrl+D).
Puis-je autoriser la chaîne
stop
à arrêter le mode de sélection?
Oui, l'entrée de l'utilisateur est placée dans la variable REPLY
name__, qu'elle corresponde ou non à l'une des options. Vous pouvez donc rechercher des valeurs spécifiques et les gérer différemment.
Mettre tous ensemble:
echo "The following `*.war` archives were found; select one:"
# set the Prompt used by select, replacing "#?"
PS3="Use number to select a file or 'stop' to cancel: "
# allow the user to choose a file
select filename in *.war
do
# leave the loop if the user says 'stop'
if [[ "$REPLY" == stop ]]; then break; fi
# complain if no file was selected, and loop to ask again
if [[ "$filename" == "" ]]
then
echo "'$REPLY' is not a valid number"
continue
fi
# now we can use the selected file
echo "$filename installed"
# it'll ask for another unless we leave the loop
break
done