Loops
Bash loops
$ cd /path/to/data-shell/creatures
$ ls # shows basilisk.dat unicorn.dat -- let's pretend there are several hundred files here
Let’s say we want to rename:
- basilisk.dat ⮕ original-basilisk.dat
- unicorn.dat ⮕ original-unicorn.dat
We could try
$ mv *.dat original-*.dat # getting an error
Remember if more than two arguments to mv, the last argument is the destination directory, but there is no directory matching original-*.dat, so we are getting an error. The proper solution is to use loops.
$ for filename in basilisk.dat unicorn.dat # filename is the loop variable here
> do
> ls -l $filename # get the value of the variable by placing $ in front of it
> done
Let’s simplify the previous loop:
$ for f in *.dat
> do
> ls -l $f
> done
Let’s include two commands per each loop iteration:
$ for f in *.dat
> do
> echo $f
> head -3 $f
> done
Now to renaming basilisk.dat ⮕ original-basilisk.dat, unicorn.dat ⮕ original-unicorn.dat:
$ for f in *.dat
> do
> cp $f original-$f
> done
The general syntax is
$ for <variable> in <collection>
> do
> commands with $variable
> done
where a collection could be a explicit list of items, a list produced by a wildmask, or a collection of numbers/letters.
$ echo {1..10} # this is called brace expansion
$ echo {1,2,5} # very useful for loops or for including into large paths with multiple items, e.g.
$ cd /path/to/data-shell/creatures
$ ls -l ../molecules/{ethane,methane,pentane}.pdb
$ echo {a..z} # can also use letters
$ echo {a..z}{1..10} # this will produce 260 items
$ echo {a..z}{a..z} # this will produce 676 items
$ seq 1 2 10 # step=2, so can use: for i in $(seq 1 2 10)
$ for ((i=1; i<=5; i++)) do echo $i; done # can use C-style loops
Question 7.1
In a directory the command ls
returns:
fructose.dat glucose.dat sucrose.dat maltose.txt
What would be the output of the following loop?
for datafile in *.dat
do
cat $datafile >> sugar.dat
done
- All of the text from
fructose.dat
,glucose.dat
andsucrose.dat
would be concatenated and saved to a file calledsugar.dat
- The text from
sucrose.dat
will be saved to a file calledsugar.dat
- All of the text from
fructose.dat
,glucose.dat
,sucrose.dat
, andmaltose.txt
would be concatenated and saved to a file calledsugar.dat
- All of the text from
fructose.dat
,glucose.dat
andsucrose.dat
will be printed to the screen and saved into a file calledsugar.dat
Question `diff`
Usingdiff
to compare files and directories.
Question `nested braces`
Discuss brace expansion. Try nested braces. Paste an example that works. What will this command do:
touch 2022-May-{0{1..9},{10..30}}.md
Question 7.4
Write a loop that concatenates all .pdb files indata-shell/molecules
subdirectory into one file called
allmolecules.txt
, prepending each fragment with the name of the corresponding .pdb file, and separating different
files with an empty line. Run the loop, make sure it works, bring it up with the ↑ key and paste into the
chat.
Question `infinite loop`
Use Ctrl-C to kill an infinite (or very long) loop or an unfinished command.
while true
do
echo "Press [ctrl+c] to stop"
sleep 1
done
Question `looping through a collection`
What will the loopfor i in hello 1 2 * bye; do echo $i; done
print? Try answering without running the loop.
Question `writing to chapters`
Create a loop that writes into 10 files chapter01.md
, chapter02.md
, …, chapter10.md
. Each file should contain
chapter-specific lines, e.g. chapter05.md
will contain exactly these lines:
## Chapter 05
This is the beginning of Chapter 05.
Content will go here.
This is the end of Chapter 05.
Question `renaming with wildmask`
Whymv *.txt *.bak
does not work? Write a loop to rename all .txt files to .bak files. There are several solutions for
changing a file extension inside a loop you know by now.
Question `spaces to underscores`
Using knowledge from the previous question, write a loop to replace spaces to underscores in all file names in the current directory.
touch hello "first phrase" "second phrase" "good morning, everyone"
ls -l
ls *\ *
You can watch a video for this topic after the workshop.