Loops
Bash loops
$ cd /path/to/data-shell/creatures
$ ls # shows basilisk.dat unicorn.dat -- let's pretend there are several hundred files hereLet’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 errorRemember 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
> doneLet’s simplify the previous loop:
$ for f in *.dat
> do
> ls -l $f
> doneLet’s include two commands per each loop iteration:
$ for f in *.dat
> do
> echo $f
> head -3 $f
> doneNow to renaming basilisk.dat ⮕ original-basilisk.dat, unicorn.dat ⮕ original-unicorn.dat:
$ for f in *.dat
> do
> cp $f original-$f
> doneThe general syntax is
$ for <variable> in <collection>
> do
> commands with $variable
> donewhere 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 loopsIn a directory the command ls returns:
fructose.dat glucose.dat sucrose.dat maltose.txtWhat 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.datandsucrose.datwould be concatenated and saved to a file calledsugar.dat - The text from
sucrose.datwill be saved to a file calledsugar.dat - All of the text from
fructose.dat,glucose.dat,sucrose.dat, andmaltose.txtwould be concatenated and saved to a file calledsugar.dat - All of the text from
fructose.dat,glucose.datandsucrose.datwill be printed to the screen and saved into a file calledsugar.dat
diff
Using diff to compare files and directories.
Discuss brace expansion. Try nested braces. Paste an example that works. What will this command do:
touch 2022-May-{0{1..9},{10..30}}.mdWrite a loop that concatenates all .pdb files in data-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.
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
doneWhat will the loop for i in hello 1 2 * bye; do echo $i; done print? Try answering without running the loop.
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.Why mv *.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.
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.