skip to Main Content

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 ??

5

Answers


  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}
    
    Login or Signup to reply.
  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[@]}"
    
    Login or Signup to reply.
  3. Use for loop:

    for ((i=$2; i<=$3; i++)); { mkdir "$1$i"; }
    
    Login or Signup to reply.
  4. 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 "%sn" "$@"                              
    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
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search