8 votes

Créer un menu bash basé sur la liste des fichiers (associer les fichiers à des numéros)

Ce site la réponse montre un menu bash génial et intuitif où il suffit d'appuyer sur un numéro pour que l'élément soit sélectionné. Mais c'est assez peu pratique pour la liste des fichiers, parce que tout est codé en dur. Je préfèrerais remplir mes fichiers dans une sorte de tableau, puis laisser l'utilisateur choisir un numéro qui correspond au décalage du tableau.


En gros, c'est 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 en un tableau ?

options=("Option 1" "Option 2" "Option 3" "Quit")

Comment puis-je 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 de caractères stop pour arrêter le mode de sélection ?

14voto

chaos Points 25386

Pour sauvegarder les sorties de find dans un bash Le réseau utilise 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 l'entrée de find délimité par un zéro ( -d $'\0' ).
    • Le tableau $options est rempli avec les noms de fichiers.
  • find ne recherche que les fichiers ( -type f ) dans le répertoire donné ( -maxdepth 1 ) avec une terminaison .war ( -name "*.war" ) et les imprime en les délimitant par le caractère nul ( -print0 ).

Le menu de sélection peut être fait comme suit :

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

Il 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

8voto

Stewart Points 1385

Vous pouvez également utiliser un glob Shell pour obtenir la liste des fichiers. Cette approche a l'avantage de ne pas utiliser un programme externe ( find ) et de ne pas avoir besoin de restriction sur le type de fichier ( *war par exemple) donnés :

#!/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

6voto

deltab Points 578

select peut faire la plupart de ces choses pour vous, sans grand effort.

Comment transformer une liste de fichiers en un tableau ?

Vous n'en avez pas vraiment besoin. select prend une série de mots à afficher comme options. Ceux-ci peuvent être donnés directement ( select color in red green blue ) ou proviennent de l'expansion d'un glob de fichier ( select file in *.war ), ainsi que l'expansion d'un tableau en mots comme le fait l'exemple que vous avez trouvé ( select option in "${options[@]}" ).

Comment 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 est invalide, il stocke la chaîne vide).

Comment puis-je m'assurer que l'utilisateur est invité à réessayer ?

Encore une fois select fait cela pour vous, car select fait une boucle comme while . Il continuera à demander jusqu'à ce que vous break en dehors de la boucle (ou jusqu'à ce qu'il lise EOF, généralement entré par Ctrl + D ).

Puis-je autoriser la chaîne stop pour arrêter le mode de sélection ?

Oui, l'entrée de l'utilisateur est mise dans la variable REPLY indépendamment du fait qu'il corresponde ou non à l'une des options, de sorte que vous pouvez vérifier des valeurs spécifiques et les traiter différemment.

Tout mettre en place :

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

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X