-1

I have a folder with a lot of shell scripts to configure my server.

My main script is named "setup.sh" and all others scripts are named 0.script.sh, 1.script.sh.... More generally N.SCRIPT_NAME.sh, where N is the execution number.

So, I would like to list all shell scripts from the folder, and execute them in the order specified by the N numbers.

  • A simple for s in *.script.sh; do printf '%s\n' "$s"; done should list the scripts in lexicographical order. Is that the order you need? If that is not what you need, the questions multiply: Are the numbers N only one digit? Do the numbers have leading zero(s)? etc … etc … – Isaac Nov 16 '18 at 18:52
  • There are no leading zero, and N can have more than one digit. – graille Nov 16 '18 at 18:54
  • Read man run-,parts. – waltinator Nov 16 '18 at 19:44
  • @don_crissti Based on which reasoning you have decided to remove the OP tag of bash? Could (s)he not choose which shell to use/ask about? – Isaac Nov 16 '18 at 20:31
  • 2
    @Isaac - based on the fact that almost all newcomers tag their questions linux & bash regardless of the content and there's nothing bash-specific here (and it's already tagged shell). You can always add it back if you think this is a bash question. – don_crissti Nov 16 '18 at 20:40
2

There's probably an easier way to glob filenames in sequence with zsh, but here's a brute-force way in bash:

for script in ?.script*.sh ??.script*.sh ???.script*.sh ????.script*.sh
do
  printf "Executing script: %s\n" "$script"
  ./"$script"
done

This assumes that you have no more than 9999 scripts -- or, more precisely, none with more than 4 numbers (characters) in the prefix part of the filename.

It also assumes that the prefixes are numbered -- it will pick up scripts named, e.g. go.script_X.sh, if you had one. To work around that, replace the ? with [0-9]'s:

for f in [0-9].script*.sh [0-9][0-9].script*.sh ...

The shell's natural globbing mechanism will be to sort the resulting filenames alphabetically, which results in them sorting numerically as well. The various filenames are spelled out in increasing scale so that "9" sorts before "101" & etc.

In zsh, glob the filenames in numerical order of their prefix with:

for script in *.script*.sh(oe_'REPLY=${REPLY%%.*}'_n)
do
  printf "Executing script: %s\n" "$script"
  ./"$script"
done

The globbing expansion first picks up all of the *.script*.sh files, then says to order (sort) them using the expression that extracts the prefix by stripping as much of "dot followed by anything" as possible, and using a numeric sort.

  • Ideally OP would zero-pad the file names, then using a simple glob like ./* should work with any shell... – don_crissti Nov 16 '18 at 19:31
  • Thanks for the edit, don; I'm still experimenting with zsh. – Jeff Schaller Nov 16 '18 at 19:32
  • No problem. You could also use a function as shown here – don_crissti Nov 16 '18 at 19:35
  • Ahhh; I had searched here for zsh filename sorting and landed on one of Gilles' answers and ran with it. Credit to Gilles, blame to me! – Jeff Schaller Nov 16 '18 at 19:38
  • 1
    Why the complexity in zsh? This zsh -c 'for script in folder/*.script*.sh(n);do printf "Executing script: %s\n" "$script";done' seems to work correctly. Reference – Isaac Nov 16 '18 at 19:53
2

If the files names are simple (no spaces, no newlines, no characters that ls needs to encode with a ?) then, maybe !! , you can get away with:

IFS=$' \t\n'; for s in $(ls -v folder/*.script.sh); do printf '%s\n' "$s"; done

In this case, ls will sort by version number -v (quite similar to numeric value).

But in general, it is a bad idea to parse (process) the output of ls.

This is better:

shopt -s nullglob
for   s   in   folder/?{,?{,?{,?}}}.script.sh
do    printf '%s\n' "$s"
done

Which will list (in alphabetical order, which is equivalent to numerical order if the characters before .script.sh are only numbers) by each character up to four characters (numbers).

The idea is that brace expansions happen before pathname expansion. The brace expansion will convert and expand the ?{,?{,?{,?}}}.script.sh to

?.script.sh ??.script.sh ???.script.sh ????.script.sh

And then, the pathname expansion will find all file that match each pattern sorting each pattern in turn.

If none of the options above are a solution, you will need to use sort:

printf '%s\n' *.script.sh | sort -n | xargs echo

Which becomes even more complex if there is a folder prefix:

printf '%s\n' folder/*.script.sh | sort -n -k 1.8 | xargs echo

Where the 8 is the number of characters (bytes) in the word folder and the following / plus one (1).

You will need to replace the echo with sh to execute each script. This will work correctly if no file name contains newlines.

If it is possible that filenames may contain newlines, use this:

printf '%s\0' folder/*.script.sh | sort -zn -k 1.8 | xargs -0 -I{} echo '{}'

Again, replace the echo with a sh (or bash) to execute the scripts.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.