Write a script

INPUT · Slides

Taking arguments (`$1`)

01 / 08

Not being able to choose is inconvenient

The script you made last lesson only ever did the same thing.

#!/bin/shwc -l red.txt

This can only count red.txt. To count another file you would have to rewrite it.

But the commands you type every day are not like that.

wc -l red.txtwc -l blue.txt

They count whatever you write after them. That is the shape you want.

~ $ ./count.sh red.txt3~ $ ./count.sh blue.txt2

The words written after are called arguments. Today you learn how to take them.

With this, a script becomes a tool.

a fixed procedure     can do one thing onlyone that takes args   the same procedure, for all sorts of things

One thing to learn, and the places you can use it multiply.

02 / 08

Take the first with $1

The words written after can be fetched by number.

#!/bin/shecho one=$1echo two=$2

Let us run it.

~ $ ./a.sh aa bbone=aatwo=bb

They are numbered in the order you wrote them.

./a.sh aa bb       |  `- $2       `---- $1

Used in the same shape as a variable, right down to the $ in front.

A number you did not write comes out empty.

~ $ ./a.sh aaone=aatwo=

Not an error, just empty. Something to watch. There is a guard against it later.

And one thing to mind when writing.

echo 'echo one=$1' >> a.sh     yes, wrap in singlesecho "echo one=$1" >> a.sh     no, it expands now and goes empty

The quoting of chapter 10. You want $1 to expand when the script runs, so you do not let it expand while writing.

03 / 08

$# and $@ and $0

Besides the numbers there are three handy ones.

#!/bin/shecho count=$#echo all=$@echo name=$0

Run it and you get this.

~ $ ./b.sh x y zcount=3all=x y zname=./b.sh
WrittenMeaning
$#how many arguments
$@all the arguments
$0the name it was called by

The use of $# is plain at once.

if there are none, show how to use it and finish

$@ is for "hand what I was given straight on to another command".

#!/bin/shwc -l "$@"        count all the files I was given

$0 is handy when you show how to use something.

echo "usage: $0 file"

Rename the file and the message follows along by itself. Nothing to fix.

One more thing to mind: from the tenth, the shape changes.

$10     -> read as "$1 followed by 0"${10}   -> the tenth

When in doubt, wrap it in { }.

04 / 08

Use "$@" wrapped

This part matters. Hand it a name with a space in.

#!/bin/shfor A in $@; do echo [$A]; done

Run it and you get this.

~ $ ./d.sh "my thing" other[my][thing][other]

It split. You wrapped it when you handed it over and it still became two.

Wrapping inside puts it right.

for A in "$@"; do echo [$A]; done
~ $ ./c.sh "my thing" other[my thing][other]

The reason is from chapter 10.

expand $@    ->  my thing othersplit on spaces  ->  my / thing / other

It is split after it expands. So you wrap it to stop that.

"$@" is special: it expands keeping the joins between arguments.

"$@"    ->  "my thing" "other"  (the joins remain)$@      ->  my thing other      (the joins go)

There is a similar $*, but that is the one where the joins go.

> When you hand arguments on, always "$@"

Remember that and you are fine. And you can explain why.

05 / 08

Guarding against a missing one

People forget to write an argument. You will too.

~ $ ./count.sh(nothing named, and something odd happens)

There are two guards.

1. Settle on a default

#!/bin/shecho [${1:-plain}]
~ $ ./e.sh[plain]        <- left out, so the default~ $ ./e.sh mine[mine]         <- written, so that

The ${VAR:-default} of the first lesson of chapter 10. It works with numbers too.

2. Refuse when there is none

#!/bin/sh[ $# -eq 0 ] && echo "please name a file" && exit 1wc -l "$1"

[ $# -eq 0 ] is "is the count equal to 0". -eq is short for equal.

~ $ ./g.shplease name a file~ $ echo $?1

It tells you how, and finishes as a failure. That is how a proper tool is built.

exit 1 means "finish in failure". What the numbers mean comes in a later lesson.

06 / 08

Send them along with shift

One more tool for you.

#!/bin/shshiftecho [$1]
~ $ ./f.sh aa bb[bb]

It says $1 and the second one came out. shift sends the arguments forward by one.

at first:      $1=aa  $2=bbafter shift:   $1=bb

What is it for? This shape.

#!/bin/shPLACE=$1shiftwc -l "$@"        count all the rest

Treat the first one apart, and hand the rest on together. The same shape as the commands you type.

grep word file1 file2     `- first  `--- the rest

Inside grep something similar is probably going on.

shift works with repetition too.

while [ $# -gt 0 ]; do  echo [$1]  shiftdone

Take one, send along, take again, until there are none. That comes in the while lesson. Today, just remember that a tool for sending along exists.

07 / 08

The quotes when you write

This is where people trip most in this lesson.

You want $1 in the script, and typing this makes it vanish.

~ $ echo "wc -l $1" >> a.sh~ $ cat a.shwc -l                  <- the $1 is gone!

The shell you are typing in expanded the $1. Your terminal has no arguments, so it came out empty.

Wrapping in singles puts it right.

~ $ echo 'wc -l "$1"' >> a.sh~ $ cat a.shwc -l "$1"             <- in as it was

Think back to the table of chapter 10.

singles  expand nothing   ->  into the file as it isdoubles  expand $         ->  it expands here and now

When you want the letters kept as they are, singles. Exactly the same story as writing $PATH in .profile.

And do not forget the "$1" inside.

echo 'wc -l "$1"' >> a.sh            ^ doubles inside

Singles outside, doubles inside. Two layers of wrapping, with different jobs.

the outer  do not let it expand nowthe inner  do not let it split on spaces when it runs

08 / 08

Now have a go

Here are the shapes for this lesson.

$1 $2         the first, the second${10}         the tenth$#            how many"$@"          all of them (joins kept)$0            the name it was called by${1:-default} the value when it is left outshift         send them forward by one

The manner of writing is this.

echo 'wc -l "$1"' >> a.sh     ^ singles outside

And here is the conclusion for today.

> Take arguments and a script becomes a tool

Last lesson's was "a record of a procedure"; from today you can make things in the same shape as ls and wc.

./count.sh red.txt      a tool you madewc -l red.txt           a tool somebody made

They are called the same way. The difference is fading.

From the next lesson you make the insides cleverer.

next: branch on a condition with if  -> say so when the file is not there

The [ $# -eq 0 ] of today is the doorway to that. Let us make one.