14

For example,

elif [[ $append = $1 ]]
then
  touch ~/directory/"$2".txt
  echo "$variable_in_question" >> ~/directory/"$2".txt

to either create a text file containing all input following "$2" , or append an existing text file with all input following "$2", what would I use in place of the "$variable_in_question" in line 4?

I basically want "$*", but omitting "$1" and "$2".

31
+500

You can use bash Parameter Expansion to specify a range, this works with positional parameters as well. For $3$n it would be:

"${@:3}" # expands to "$3" "$4" "$5" …
"${*:3}" # expands to "$3 $4 $5 …"

Be aware that both $@ and $* ignore the first argument $0. If you wonder which one to use in your case: it’s very probably you want a quoted $@. Don’t use $* unless you explicitly don’t want the arguments to be quoted individually.

You can try it out as follows:

$ bash -c 'echo "${@:3}"' 0 1 2 3 4 5 6
3 4 5 6
$ echo 'echo "${@:3}"' >script_file
$ bash script_file 0 1 2 3 4 5 6
2 3 4 5 6

Note that in the first example $0 is filled with the first argument 0 while when used in a script $0 is instead filled with the script’s name, as the second example shows. The script’s name to bash of course is the first argument, just that it's normally not perceived as such – the same goes for a script made executable and called “directly”. So in the first example we have $0=0, $1=1 etc. while in the second it’s $0=script_file, $1=0, $2=1 etc.; ${@:3} selects every argument starting with $3.

Some additional examples for possible ranges:

 # two arguments starting with the third
$ bash -c 'echo "${@:3:2}"' 0 1 2 3 4 5 6
3 4
 # every argument starting with the second to last one
 # a negative value needs either a preceding space or parentheses
$ bash -c 'echo "${@: -2}"' 0 1 2 3 4 5 6
5 6
 # two arguments starting with the fifth to last one
$ bash -c 'echo "${@:(-5):2}"' 0 1 2 3 4 5 6
2 3

Further reading:

27

You can use the shift builtin:

$ help shift
shift: shift [n]
    Shift positional parameters.

    Rename the positional parameters $N+1,$N+2 ... to $1,$2 ...  If N is
    not given, it is assumed to be 1.

    Exit Status:
    Returns success unless N is negative or greater than $#.

Ex. given

$ cat argtest.bash 
#!/bin/bash

shift 2

echo "$*"

then

$ ./argtest.bash foo bar baz bam boo
baz bam boo
  • 3
    @Oreoplasm I think it's worth mentioning that the shift approach will make it impossible to access $1 and $2 after you shifted them. In your script you use $2 alongside the $variable_in_question, you either need to change that or use the Parameter Expansion approach. – dessert Mar 3 '18 at 23:19
  • 6
    shift is good when the first one or more args are special and it makes sense to pick them out into separate variables (foo="$1"; bar="$2";, or if [[ something with $1 ]];then blah blah; shift. But @dessert's answer with the non-destructive approach is nice in other cases when you do still want the full list later, or when you use fancier syntax to pick out a limited range of args, not out to infinity, without introducing empty args to a command if $@ doesn't have that many elements. – Peter Cordes Mar 4 '18 at 2:00
13

Generally, you can copy the positional parameters to an array, delete arbitrary indices of the array and then use the array to expand to exactly those indices you want, without losing your original arguments.

For example, if I wanted all the args except the first, fourth and fifth:

args=( "$@" )
unset args[0] args[3] args[4]
echo "${args[@]}"

In the copy, the indices are shifted by 1, since $0 is not part of $@.

  • 1
    Of course it's possible to combine that, e.g. echo "${args[@]:2}" for the third to last argument. – dessert Mar 4 '18 at 7:49

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.