DEV Community

Cover image for Shellscripting: Functions

Shellscripting: Functions

Darkø Tasevski on August 26, 2018

This is part three of my post series about Shellscripting, you can check out previous posts here: Shellscripting: Introduction Shellscripting: Co...
Collapse
 
ikirker profile image
Ian Kirker

One veeeery useful and slightly weird thing to add here is that $*, "$*", $@, and "$@" all mean "all the arguments", but get expanded in different ways.

Here's an example:

#!/bin/bash

function print_args() {
    echo "-- Unquoted, asterisk --"
    for i in $*; do
        echo $i
    done

    echo "-- Quoted, asterisk --"
    for i in "$*"; do
        echo $i
    done

    echo "-- Unquoted, atpersand --"
    for i in $@; do
        echo $i
    done

    echo "-- Quoted, atpersand --"
    for i in "$@"; do
        echo $i
    done
}

print_args "a" "b c" "d e f"
Enter fullscreen mode Exit fullscreen mode

gives:

-- Unquoted, asterisk --
a
b
c
d
e
f
-- Quoted, asterisk --
a b c d e f
-- Unquoted, atpersand --
a
b
c
d
e
f
-- Quoted, atpersand --
a
b c
d e f
Enter fullscreen mode Exit fullscreen mode

The quoted atpersand ("$@") option is the only option that preserves argument groupings including spaces, while "$*" treats all the arguments as one string, and $* and $@ both expand to the fully split, ungrouped, unquoted arguments.

"$@" behaves as if it were expanded with each element quoted separately, rather than "$*" which behaves as if it were expanded and then the whole expansion quoted.

(Note that this is the same as the behaviour for using arrays, in "${array[*]}" vs "${array[@]}".)

Collapse
 
puritanic profile image
Darkø Tasevski

I would agree on weird 😄but interesting nevertheless.
I didn't know about $* nor that quoting/unquoting can have such different results. Going to take note of this, thanks for sharing!

Collapse
 
moopet profile image
Ben Sinclair

Just to be picky about positionals, when you say

NOTE: You cannot change the values of these variables

You kinda can, because you can use shift to reposition them,

foo() {
  echo $1, $2, $3
  shift
  echo $1, $2, $3
}

foo one two three four five

# one, two, three
# two, three, four
Enter fullscreen mode Exit fullscreen mode
Collapse
 
puritanic profile image
Darkø Tasevski

Each day you learn something new :D Thanks!

Collapse
 
polyluxus profile image
Martin Schwarzer

You might want to check out Shellcheck to avoid simple errors, probably unwanted behaviours, and deprecated code, and maybe even more. The online wiki is really helpful in explaining why things may go wrong, or why some code has been replaced.

Collapse
 
ferricoxide profile image
Thomas H Jones II

Shellcheck is a great to add to a Git probject's .travis.yml (if you're using Travis.CI as a test-framework for commits, obvs.; otherwise call it from whatever else you're using to test uploaded code).

Collapse
 
vlasales profile image
Vlastimil Pospichal
#!/bin/bash
for i in $@; do
    echo "$i"
done

$ ./args.sh alfa "beta gamma"
alfa
beta
gamma
Enter fullscreen mode Exit fullscreen mode

That's wrong!

Right version:

#!/bin/bash
for i in "$@"; do
    echo "$i"
done

$ ./args.sh alfa "beta gamma"
alfa
beta gamma
Enter fullscreen mode Exit fullscreen mode
Collapse
 
puritanic profile image
Darkø Tasevski

Whoops, missed that one, thanks for noticing!

Collapse
 
avalander profile image
Avalander

I've been waiting for this post my whole life, thank you so much for sharing!

Collapse
 
bernhardwebstudio profile image
Bernhard Webstudio

Hmmm. functions might be a better way than my current approach: just moving repeated instructions into a new shell script which I invoke when I need the "function" 😁