0

New in Linux bash script. Here I tried to create some files with getopts. For example I'd like to create 3 files called xyzfile, in command line ./createfiles -n xyzfile 3should be given (2 arguments after the option -n). The result should be 3 files with the names xyzfile_1, xyzfile_2 and xyzfile_3.

I tried to put my createfile() function outside the while-loop and as well as inside the while-loop. But the option -n doesn't work. I also tried to create another function called foo() with included the function createfile(), but still something wrong there. I have no idea anymore what I can do. Hope I can get some advices from you guys. Thank you very much!

#!/bin/bash

    while getopts :n:bc opt; do
        case $opt in
            n) echo test 3333333
                 createfile() {
                    echo "$OPTARG"
                    sum=$2

                 for((i=1;i<=sum;i++))
                    do
                    touch "$OPTARG_${i}"
                done
                 }
                 createfile $OPTARG ${2};;
            b) echo "test 1111111";;
            c) echo "test 2222222";;
            *) echo error!;;
        esac
    done
cchi
  • 71
  • 6
  • 2
    `getopts` only supports flags and single-argument options. As a general rule, don't try to *do* anything while parsing arguments; just record the values you find, and use those values after parsing is complete. – chepner Sep 19 '19 at 13:40
  • @chepner so I have to put the ```createfile()``` function outside of the while-loop? so how can i realize the result with the given conditions? – cchi Sep 19 '19 at 13:48

3 Answers3

1

Parse the options first, then use the values you discover. An option can take only a single argument, so -n only gets the first one (I'll keep that as the file-name stem here). The count will be an ordinary positional argument found after parsing the options.

while getopts :n:bc opt; do
  case $opt in
    n) stem=$OPTARG; shift 2;;
    b) shift 1;;
    c) shift 1;;
    *) shift 1; echo error ;;
  esac
done

count=${1?No count given}

createfile () {
  for ((i=$1; i<=$2; i++)); do
      touch "${1}_${i}"
  done
}


createfile "$stem" "$count"
chepner
  • 497,756
  • 71
  • 530
  • 681
  • in the for-loop it shows as an error with the ```i=$1```, so i remove the ```$``` and wrote just ```i=1```, the result files are like ```_1```, ```_2``` without file name. seems like the ```${1}```in line```touch``` doesn't work @chepner – cchi Sep 20 '19 at 06:33
  • without ```-n``` just ```./createfile.sh filename 2``` works with the above result, with ```-n```doesn't work @chepner – cchi Sep 20 '19 at 06:36
  • I misspelled `stem` in the `case` statement. – chepner Sep 20 '19 at 13:13
  • I've also switched to shift arguments as they are read, rather than all at once after they are parsed. – chepner Sep 20 '19 at 13:16
  • I just modified your code from ```i=$1``` to ```i=$1+1```, so that it would not create filename_0. your anwer is also quite helpful. Thank you ! – cchi Sep 23 '19 at 06:39
1

Use a separate option for the count, and create your files after the option processing.

Something like:

while getopts "n:c:" opt; do
    case $opt in
        n) name="$OPTARG";;
        c) count=$OPTARG;;
        # other options...
    esac
done

shift $((OPTIND -1))

while (( count > 0 )); do
    touch "${name}_$count"
    (( count-- ))
    # ...
done
mivk
  • 13,452
  • 5
  • 76
  • 69
  • it works partly, if i put ```./createfile.sh -c 3```in command line, it works with the 3 files with the name 1, 2, 3 as result. other options or processes didn't work – cchi Sep 20 '19 at 06:41
  • @cchi: the name variable was ignored because of the underline following it. I corrected it now to `${name}_`... So `./createfile.sh -n xyzfile -c 3` should now do what you expect. If you add other options in the `case` statement, don't forget to also add them to the `getopts` line. – mivk Sep 20 '19 at 07:54
  • this modification works great as i expected. One thing i don't understand is, as i already wrote a function named ```createfile()``` before without ```getopts``` but with a ```for-``` loop. in the for-loop it worked exactlly as i want ```for((i=1;i<=count;i++)) do touch "$name_${i}" done``` Because i think the variable ```count``` is actually the one whiched would be changed by every run but not the ```name```. That's why i also tried to modify your code like ```touch "$name_${count}"``` of course it didn't work. I'd like to know why ```name``` not ```count```? – cchi Sep 20 '19 at 10:49
  • @cchi The variable `$name_` is a different variable than `$name` (and the former is undefined). That is why the brackets are needed: to separate the variable name from the litteral `_` you want to use. The brackets woud not be needed if you would use `$name$count`, `$name-$count`, `$name.$count` etc. But `_` is a valid character in a variable name so you need `${name}_...` – mivk Sep 20 '19 at 11:00
  • got it. Thank you so much! – cchi Sep 23 '19 at 06:28
1

getopts supports only options without, or with one argument. So you'll have to decide on which way you want your script to work. You have multiple options:

  • add a new option -m or similar to pass the maximum number of files you want to create: createfile -n xyzfile -m 3
  • you can also use the arguments that are not passed as an option, if you do your parsing well then createfile 3 -n xyzfile or createfile -n xyzfile 3 would mean the same. In my scripts I often use such positional argument if there is one option that the user always needs to pass.
  • You might even consider changing your way of calling the script to createfile xyzfile -n 3 or even createfile xyzfile where the name is a positional argument and the number of files optional (choose a logical default value, probably 1)...
Chris Maes
  • 35,025
  • 12
  • 111
  • 136
  • u r right. I just achieved the right solution for this problem. ```./createflie.sh -n xyzfile -m 3 ``` maybe the most suitable way to solve the problem. Thank you very much for your ideas and tipps! – cchi Sep 20 '19 at 10:41
  • @cchi if any of these answers solves, your problem, consider accepting it (checkmark) to indicate the solution to your problem. If other answers were helpful, you can upvote them to indicate that they are helpful and to say "thank you" :) – Chris Maes Sep 20 '19 at 10:51
  • 1
    just gave the checkmark and also voted your answer. Thank you for your explanation! – cchi Sep 23 '19 at 06:30