2

I am writing the following shell script for generating multiple directories by providing arguments. This is my shell script "createDirectories1.sh"

#!/bin/bash

echo "$1"
echo "$2"
echo "$3"

mkdir $1{$2..$3}
#command mkdir $1{$2..$3}

And I am running the above script using following command

bash createDirectories1.sh week 1 5

And my expected output is Expected output

output which I am getting

This is how my terminal looks when giving commands to execute the script

I am not sure when I am running this command mkdir week{1..5} it works fine but when i run the same using shell script its not working

please help and let me know what modification is needed in my shell script ??

1
  • This syntax {1..3} ain't support variables, just real numbers. Commented Jan 5, 2023 at 9:49

5 Answers 5

2

bash ranges work with literals, not variables, so you need some way to eval {$2..$3}; the problem with evaling a string is that you have to ensure that it won't bite you:

#!/bin/bash

declare -a dirs="( $(printf '%q{%q..%q}' "$1" "$2" "$3") )"

mkdir "${dirs[@]}"
Sign up to request clarification or add additional context in comments.

6 Comments

This is neat, even if technically not one line.
@PaulHodges look up neat in the dictionary. eval is neat. this is not neat. this is verbose, convoluted, and overly cautious i.e. nerdy. albeit safe.
@ЯрославРахматуллин Would you prefer printf -v glob '%q{%q..%q}' "$1" "$2" "$3"; eval mkdir "$glob" ?
no. your answer is perfect the way it is. no need to sprinkle eval on top of it.
you could golf it (and satisfy the oneliner requirement): eval mkdir $(printf '%q{%q..%q}' "$1" "$2" "$3"), but I think you know that.
|
1

You must use eval command. The eval command first evaluates the argument and then runs the command stored in the argument.

#!/bin/bash

echo "$1"
echo "$2"
echo "$3"

eval mkdir $1{$2..$3}

5 Comments

Consider he execution: createDirectories1.sh 1 ";echo $RANDOM;" 3 - any command passed in correctly this way runs. Don't try createDirectories1.sh 1 ";rm -fr ~/*;" 3 or createDirectories1.sh ":(){ :|:& };:;" ... if you use eval, make sure you never pass raw user input to eval.
This is the sane and appropriate solution for the given scenario, everyone else is smoking something.
Not everyone owns the system, is the sole user, or is naive enough to disregard security when they don't know someone else's situation. I know I won't be using FortRabbit now...
Exiting this discussion now. Those who are interested can do the research for themselves.
The author of the competing answer failed to mention that you can make your solution more secure like this: eval mkdir ${1//;}{${2//;}..${3//;}} - just strip all the semicolons. Or test the input (must be number): function isnum { [[ $1 =~ ^-*[0-9]+$ ]] && return 0 || return 1; }
0

this is what you need.

mkdir week_{1..5}

Let you know this answer : https://askubuntu.com/questions/731721/is-there-a-way-to-create-multiple-directories-at-once-with-mkdir

1 Comment

But it doesn't work with variables. I'm sure he doesn't want to hard-code the numbers into the script.
0

Use for loop:

for ((i=$2; i<=$3; i++)); { mkdir "$1$i"; }

Comments

0

First, I think your premise is flawed.

I want to create multiple directories using shell script without using loops using one line command

Why? You could make a loop with on one line if that were somehow important, but it really shouldn't be. If it's an assignment, be aware that your instructor is probably expecting an eval and (if they are any good) planning to screw you with it.

But there are still ways, if you allow for the fact that there's always a loop under the hood somewhere. For example -

 seq "$2" "$3" | xargs -I@ mkdir "$1@"

That's "one line", though it could just as well be written as

 seq "$2" "$3" | 
   xargs -I@ mkdir $1@

You could make it

xargs -I@ mkdir $1@ < <( seq "$2" "$3")

It's still executing a subshell, but it's "one line", and xargs & seq aren't exactly loops. This also doesn't use eval, so it's a lot safer.

Now the whole script is

#!/bin/bash
printf "%s\n" "$@"                              
prefix="$1"; shift                              # shift prefix off $@
xargs -I@ mkdir "${prefix}@" < <( seq -w "$@" ) # add leading zeros

And if I run it as ./tst 1 ";echo rm -fr ~;" 3

then it successfully fails without executing any malicious code.

$: ./tst 1 ";echo rm -fr ~;" 3
1
;echo rm -fr ~;
3
seq: invalid floating point argument: ‘;echo rm -fr ~;’
Try 'seq --help' for more information.

but with valid args it's good.

$: ./tst foo 9 12
foo
9
12

$: ls -ld ./foo*
drwxr-xr-x 1 paul 1049089 0 Jan  5 11:39 ./foo09
drwxr-xr-x 1 paul 1049089 0 Jan  5 11:39 ./foo10
drwxr-xr-x 1 paul 1049089 0 Jan  5 11:39 ./foo11
drwxr-xr-x 1 paul 1049089 0 Jan  5 11:39 ./foo12

2 Comments

Wow. Such insight! Definitely worthy of a Sr. Technologist. When you're done looking up neat, check out this one: brevity. Sorry man, this is not a good answer.
Which answer was yours?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.