0

Note: please give feedback on the title, I honest didn't know how to title this question.

Im trying to design a code that everytime the user drop a file in the tools folder, it automatic gets add into the main code at menu.sh script, so it's simpler for the user.

The menu.sh is working fine, but everytime the user adds a new script he needs to do add two lines of code in the main script(menu.sh)

for example, lets say the user want to add a script called run-scrape.sh

"run-scrape" need to be added:
-options variable
-added in the source command inside the case statment

I am trying to automate this two tasks so it's simpler for the user.

I am open to a different approach to do this or link to something similar, I couldn't find anything like.

Background:

folder structure:

~ 
scripts  
menu.sh, tools 
> cd tools
pushing-code.sh,  purge-data.sh, run-scrape.sh

menu.sh :

title="title"

prompt="Pick an option(number): "

options=(         
        "pushing code" \
        "purge-data" \
        "run-scrape" \
         )

echo "$title"
PS3="$prompt"
select opt in "${options[@]}" "Quit"; do 
    case "$REPLY" in
    0) source $(pwd)/scripts/tools/pushing-code.sh; my_func;;
    0) source $(pwd)/scripts/tools/purge-data.sh; my_func;;
    0) source $(pwd)/scripts/tools/run-scrape.sh; my_func;;

    $((${#options[@]}+1))) echo "Goodbye!"; break;;
    *) echo "Invalid option. Try another one.";continue;;
    esac
Working the Solution:

basically some how need to create this two components in the menu.sh code:

  1. The variable: options

    myFileNames=$(ls ~/$(pwd)/scripts/tools)
    for file in $myFileNames;
    
  2. The case statement
    the following line of code will be the same for every file except for the file name, in this example: pushing-code.sh

    so adding this to the menu.sh code:

    #sample:  0) source $(pwd)/scripts/tools/pushing-code.sh; my_func;
    
    path=$(pwd)
    args=()
    for i in myFileNames; do
        args=("0) source $path/scripts/tools/$i ; my_func;")
    done
    
    select opt in "${options[@]}" "Quit"; do 
        case "$REPLY" in args
    
        $((${#options[@]}+1))) echo "Goodbye!"; break;;
        *) echo "Invalid option. Try another one.";continue;;
        esac
    

I don't think it's well put together, does anyone have done something similar in the past that would share the code :)

Peter
  • 544
  • 5
  • 20
  • 2
    Do you have to call the dynamically added scripts as functions with sourcing, or could you make them executable stand-alone scripts that don't have to be sourced? – Benjamin W. Nov 11 '22 at 18:40
  • hi @BenjaminW. Im more used with python, new to bash. I will search about: `executable stand-alone scripts that don't have to be sourced`. The goal is to make it easier for the user to chose an option in the menu and press enter, so is more user friendly and save time – Peter Nov 11 '22 at 18:48
  • 2
    When you do `source`, the behaviour is as if you typed everything from the file you're sourcing; importantly, sourcing allows you to change the environment by adding functions and variables. A stand-alone script is run in a subshell and cannot change the environment of the shell it's being called from. I suspect the functionality you want to offer from the menu doesn't have to run in the same environment, i.e., wouldn't have to be sourced, which makes everything a little simpler. – Benjamin W. Nov 11 '22 at 19:32

1 Answers1

1

1: The variable: options

myFileNames=$(ls ~/$(pwd)/scripts/tools)
for file in $myFileNames;
  • Use pathname expansion (globbing), not ls.
  • And use it to populate an array, not a flat string. Also
  • ~/$(pwd)/scripts/tools does not make sense.

Overall, something along these lines

# Create an array of all the *.sh files in the tools directory
options=(/path/to/tools/*.sh)

You can check the result by, for example,

echo "${options[@]}"

2: The case statement the following line of code will be the same for every file except for the file name, in this example: pushing-code.sh

Having the options in an array, you can access each one via its index. That pairs nicely with using select, something along these lines:

# Select from among elements of the options array
select choice in "${options[@]}"; do
  if [[ -n "$choice" ]]; then
    # read and evaluate the commands from the selected file
    # select options are numbered from 1, but array indices from 0
    source "${options[$((choice - 1))]}"
    break
  else
    echo "$REPLY" is not a valid selection
    # ...
  fi
done

The select command will automatically loop until a break command is executed or EOF is reached on the standard input.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • It totally makes sense, it's a very well put together and detailed answer. I will test the code and mark as the answer in about one hour from now, sorry im eating lunch – Peter Nov 11 '22 at 20:04
  • @Jonh Bollinger, Im getting a error: `scripts/menu.sh: line 38: scripts/tools/pushing-code.sh: division by 0 (error token is "tools/pushing-code.sh")` " The error occurr in the line: `source "${options[$((choice - 1))]}"` do you have any hints on how should I adjust it? – Peter Nov 11 '22 at 22:51
  • 1
    @Peter, the question is tagged [bash]. If you're not actually running this in `bash` (as in, if the script's shebang line is not `#!/bin/bash`) then you may need to change `source` to `.`. – John Bollinger Nov 11 '22 at 22:58
  • thank you for the hint, I was running `sh script_name`, it's my bad – Peter Nov 11 '22 at 22:59