Your first script
Once you have typed the same three commands for the fourth time, it is time to put them in a file. A shell script is nothing more exotic than that: a list of commands, saved, so you can run them again without retyping.
Open a new file with nano:
$ nano count_reads.sh
and put this in it:
#!/bin/bash echo "counting reads" wc -l reads.fastq echo "done"
The .sh ending is only a convention to help humans. What actually matters is the first line.
#!/bin/bash is called the shebang. Those first two characters tell the kernel that this file is not a program itself, it is a script, and the rest of the line says which interpreter should run it. Without it the system has to guess, and you may get a different shell than the one you wrote for.
Everywhere else, a # starts a comment and the rest of the line is ignored. Use them.
Now try to run it:
$ ./count_reads.sh bash: ./count_reads.sh: Permission denied
This is the payoff of the permissions section. New files are not executable, because it would be a bad idea if they were. Add the execute bit:
$ chmod +x count_reads.sh $ ./count_reads.sh counting reads ... done
Why ./ in front? Because when you type a bare command name, the shell only looks for it in a fixed list of system directories, and the directory you are standing in is deliberately not one of them. Writing ./count_reads.sh says "the file right here", rather than asking the shell to go looking. That list is called $PATH, and the section on your environment covers how to change it.
A script that always counts the same file is not very useful. You can pass arguments to it, and inside the script they arrive as $1, $2 and so on:
#!/bin/bash # $1 is the first thing typed after the script name echo "counting reads in $1" wc -l "$1"
$ ./count_reads.sh sample1.fastq counting reads in sample1.fastq 4000 sample1.fastq
Note the quotes around "$1". If someone hands your script a filename with a space in it, unquoted $1 falls apart in exactly the way you saw earlier.
A couple of other useful ones: $0 is the name of the script itself, and $# is how many arguments were given. That lets you catch the case where somebody forgets to pass one. The syntax below is covered properly in the conditionals lesson shortly, so read it as "if fewer than one argument was given, complain and stop" and do not worry about the brackets yet:
#!/bin/bash
if [ $# -lt 1 ]; then
echo "usage: $0 FASTQ_FILE"
exit 1
fi
wc -l "$1"
Exercise
- Write a script that prints a message and runs pwd and ls.
- Try to run it, get the Permission denied error, then fix it with chmod +x.
- Change it to take a directory as $1 and list that directory instead.
Quiz Question
What do you call the #!/bin/bash line at the top of a script?
Show answer
the shebang