Can someone please help me understand the following code segment?

set -- ${BACKUPDIR}/backup-???.tgz

I found it in this backup script https://wiki.ubuntuusers.de/Skripte/inkrementelles_Backup/ where it is used to get the number of the filename of the last stored backup. The filenames of the backups are "backup-ccc.tgz" where ccc is a three digit number that is incremented every time a new backup is created.

I do understand lines three and four they cut off the rest of the filename so just the needed part with the number is left. What I don't get is how the first two lines are working. I read the manpage of set but now I'm confused more then I had been before. What does the command set -- filename do? Also what exactly is the option ${!#} for, that is being stored in lastname. I think ${} is to dereference a given parameter, but which parameter does the option !# point to?

Secondly I don't get why I need the command in line 5. As I understand this is used to replace all question marks with an 0 that are stored in the variable backupnr, but why is this done? If there hasn't been an error in the commands before backupnr should be a three digit number, if there has been an error and the content of the variable is corrupt, you can't know what is the actual value of backupnr. So if I want to scan for corruption why don't I have to scan for everything thats not a number, but just for a question mark? Is this maybe somehow related to whats happening in the first two lines?


set is often used to set the positional parameters ($1, $2, $3, etc., which are usually provided as arguments to the script). By using these parameters, we can get a simple way to count things via the $# variable, which stores the number of set positional parameters.

? is a wildcard matching any single character, so ??? matches any combination of three characters.

Combined: set -- ${BACKUPDIR}/backup-???.tgz sets every file named backup-xxx.tgz, where xxx are any three characters, as the parameters.

${!#}, uses variable indirection:

If the first character of parameter is an exclamation point (!), it introduces a level of variable indirection. Bash uses the value of the variable formed from the rest of parameter as the name of the variable; this variable is then expanded and that value is used in the rest of the substitution, rather than the value of parameter itself. This is known as indirect expansion.

Since $#, which is what you get when you remove the !, is the count of parameters (and the number of the last parameter), ${!#} is a simple way to get the last argument.

As for command 5, what happens when no matching files are present?

$ bash -c 'set -- backup-???.tgz; echo "$@"'

The shell leaves that word alone, so now we have just a literal backup-???.tgz. The author handles that edge case by replacing the ? with 0, to mark the first backup file.

  • So if I use set, I will create a new positional parameter, which will always have the index of the last already existing parameter incremented by one? But why is the author using set -- ${BACKUPDIR}/backup-???.tgz and not just set ${BACKUPDIR}/backup-???.tgz without the --? – Asco Jun 15 '16 at 19:29
  • @Asco there's not telling whet BACKUPDIR might contain. set can also be used to set the shell options (set -e, set -f, etc.). To be safe, the -- tells set to stop treating the remaining arguments as options ot set. – muru Jun 15 '16 at 19:30

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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