Commands from last week:
mkdir,rm -r— create/delete directories (avoid spaces in names)nano— text editor;Ctrl+Osaves,Ctrl+Xexitsmv,cp— move and copy filesecho "text" > file.txt— create file;>>appends*(any chars),?(one char) — wildcard patternswc,sort,cut,uniq— counting, sorting, extracting columns|chains commands;>saves output to file
Commands are building blocks. Combine them with pipes to create powerful workflows.
Navigate to exercise-data/creatures/ — you'll find three files: basilisk.dat, minotaur.dat, unicorn.dat.
The Problem: Repeating a Command
$ head -n 5 basilisk.dat minotaur.dat unicorn.dat
# Works, but what if you had 100 files?
$ head -n 5 *.dat # wildcard helps, but...
# What if you wanted only the CLASSIFICATION line from each?
$ head -n 2 basilisk.dat minotaur.dat unicorn.dat | tail -n 1
# Oops — only gets unicorn! This is why we need loops.
Your First Loop
# Multi-line version (easier to read)
$ for filename in *.dat
> do
> echo $filename
> head -n 2 $filename | tail -n 1
> done
# One-line version (same thing)
$ for filename in *.dat; do echo $filename; head -n 2 $filename | tail -n 1; done
In one-line loops: semicolons go after the list, between commands inside the loop, and before done. If you forget, type the multi-line version first, then press the up arrow — the shell shows you the one-liner with semicolons already in place.
When the shell prints > or $, it's waiting for your input (a prompt). When you type them, you're redirecting output or getting a variable's value. Context matters.
Brace Expansion
Instead of listing values one by one, use {} to generate sequences automatically.
# Frustrating way — list every number
for loop_variable in 0 1 2 3 4 5 6 7 8 9; do echo $loop_variable; done
# Better way — brace expansion
for loop_variable in {0..9}; do echo $loop_variable; done
# More brace expansion examples
for i in {1..10}; do mkdir experiment_$i; done
for i in {10..1}; do echo $i; sleep 1; done; echo "Blastoff!"
Loop Challenges
Navigate to exercise-data/alkanes — six *.pdb files. Predict the output:
# Challenge 1: what does this print, and why?
for datafile in *.pdb; do ls *.pdb; done
# Challenge 2: fix it to list each file once
for datafile in *.pdb; do ls $datafile; done
# Wildcard position matters
for filename in c*; do ls $filename; done # starts with c
for filename in *c*; do ls $filename; done # c anywhere in name
Saving Output in a Loop
# > overwrites on each iteration (bad)
for alkanes in *.pdb; do cat $alkanes > alkanes.pdb; done
# >> appends — this is what you want
for datafile in *.pdb; do cat $datafile >> all.pdb; done
Nested Loops Challenge
# Create a directory structure for experiments
for species in cubane ethane methane
do
for temperature in 25 30 37 40
do
mkdir $species-$temperature
done
done
# Add a script file to each temperature directory
for species in octane pentane propane
do
mkdir $species
for temperature in 25 30 37 40
do
mkdir $species/$temperature
touch $species/$temperature/experiment.sh
done
done
Navigate to exercise-data/alkanes. Shell scripts let you save a sequence of commands and run them again later.
Writing Your First Script
$ nano middle.sh
Type the following into nano, save with Ctrl+O, exit with Ctrl+X:
#!/bin/bash
head -n 15 octane.pdb | tail -n 5
$ ls -lh middle.sh # note permissions: rw-r--r-- (not executable)
$ chmod +x middle.sh # make it executable
$ ls -lh middle.sh # now shows rwxr-xr-x
$ ./middle.sh # run it
Script Arguments: $1, $2, $3, $@
Hard-coding filenames is inflexible. Use variables to accept arguments from the command line.
# Edit middle.sh to use $1 (first argument)
head -n 15 "$1" | tail -n 5
$ ./middle.sh octane.pdb
# Edit again to use all three arguments
head -n "$2" "$1" | tail -n "$3"
$ bash middle.sh pentane.pdb 15 5
$ bash middle.sh pentane.pdb 20 5
Adding Comments
Comments (lines starting with #) explain what a script does. Always include them.
# select lines from the middle of a file
# usage: bash middle.sh filename end_line num_lines
head -n "$2" "$1" | tail -n "$3"
The $@ Variable — All Arguments
$ nano sorted.sh
# sort files by their length
# usage: bash sorted.sh one_or_more_filenames
wc -l "$@" | sort -n
$ bash sorted.sh *.pdb ../creatures/*.dat
Now it's your turn. Instead of writing this script from scratch, describe what you want to AI and let it write the code — then read it, understand it, and run it.
Once you have the script from AI, save it as experiment_setup.sh, make it executable with chmod +x experiment_setup.sh, and run it with ./experiment_setup.sh. Check the output and the log file.
Here is what the finished script should look like. Compare it to what AI generated for you.
#!/bin/bash
# script to set up experimental directory structure
log_file="setup_log.txt"
echo "Experiment setup started: $(date)" > $log_file
for species in cubane ethane methane
do
mkdir $species
echo "Created $species directory" >> $log_file
for temperature in 25 30 37 40
do
mkdir $species/$temperature
touch $species/$temperature/experiment.sh
echo " - Created $species/$temperature/" >> $log_file
done
done
echo "Setup completed: $(date)" >> $log_file
echo "Setup complete! Check $log_file for details."
Create a free GitHub account if you don't have one. We'll use it on Day 4 for remote repositories.