http://freedompenguin.com/articles/how-to/bashing-your-shell-shell-scripting-boot-camp
Spend enough time on the command line and you’ll eventually want to do many tasks…that take some intricate commands…repeatedly. A good example of this, is making thumbnails of photos. Basically, our workhorse of this script is not ImageMagick (which provides convert, identify and mogrify), but the for loop in bash itself. Ready? Grab your pen-knife and let’s whittle out a script:
If you just copy-pasted that block of commands…you just executed a shell script and we’re done. See you next week!
Oh, OK, I’ll keep going since all that created was a file and got you into your editor. A proper shell script begins with a “shebang” (#!/bin/bash), (also pronounced in its Batman fight-scene parts: “pound, bang, slash, bin, slash, bash!”).
That’s pretty much how most shell scripts get going: looping over a
series of file names. No matching file names? Let’s make a quick one:
Some safety tips:
1) start all your for loops this way, with your for-echo.
2) exit on errors immediately. Use touch asdf.jpg
3) need more help? use set -x, and watch it in action
4) double quote your variables
5) test for files with -f
6) test for error conditions (non-zero exit conditions)
7) need more help? Install the bash debugger (Debian/Ubuntu using apt): apt-get install bashdb
Let’s add these to our thumbnail program:
Now we can test for to see if the files actually exist, since there is no point calling a script on something that isn’t there.
Why do we put quotes around the variable? Consider that file names
can have spaces, yet when we give arguments to for, it separates things
with spaces.
Will output:
# a; # b; # c.txt;
So putting double quotes around for variables doesn’t work, does it? You’re right. That’s safer to use while read loops. We can pipe the output of ls to read in a while loop. The ls command outputs a file per line, and read ingests a whole line. Now we’ll do it right:
Next, let’s remove asdf.jpg
and now we have no pictures in our directory. The set -e in our script
will break and exit our script right before we get to the while. This is
because ls exits with error code 2 if there are files not found. We can
be strict and detect this and exit early:
ls *jpg &>/dev/null || ( echo "Nothing to thumbnail, bye"; exit 0)
ls *jpg | while read f ; do … done
Getting tough yet? If you’re keeping up with me, you’re a bad-ass. Most people skip this stuff. If they script enough, they have a bugger of a time figuring out where their script takes a dump. Let’s put some more meat on this skeleton and make a thumbnail:
What we did if [ -f $f ]? For file a b c.txt, that would evaluate in the script as:
if [ -f a b c.txt ] and return "a: file not found".
See what I mean about convert not actually being the meat of the script? The script is the meat. Convert is merely the hammer that strikes the iron. Some things you might not have seen before:
# set -e
# set -x
Be sure to leave that in so that any other shell script lover will appreciate our careful work!
Spend enough time on the command line and you’ll eventually want to do many tasks…that take some intricate commands…repeatedly. A good example of this, is making thumbnails of photos. Basically, our workhorse of this script is not ImageMagick (which provides convert, identify and mogrify), but the for loop in bash itself. Ready? Grab your pen-knife and let’s whittle out a script:
- [ ! -d ~/bin ] && mkdir ~/bin
- cd ~/bin
- touch makeThumbs.sh
- chmod +x makeThumbs.sh
- vim makeThumbs.sh
Oh, OK, I’ll keep going since all that created was a file and got you into your editor. A proper shell script begins with a “shebang” (#!/bin/bash), (also pronounced in its Batman fight-scene parts: “pound, bang, slash, bin, slash, bash!”).
- #!/bin/bash
- for f in *.jpg ; do
- echo $f
- done
- touch asdf.jpg
1) start all your for loops this way, with your for-echo.
2) exit on errors immediately. Use touch asdf.jpg
3) need more help? use set -x, and watch it in action
4) double quote your variables
5) test for files with -f
6) test for error conditions (non-zero exit conditions)
7) need more help? Install the bash debugger (Debian/Ubuntu using apt): apt-get install bashdb
Let’s add these to our thumbnail program:
- #!/bin/bash
- set -e # this makes any command returning !0 exit the script
- set -x # shows all script commands
- for f in *.jpg ; do
- echo "$f"
- done
- for f in *.jpg ; do
- if [ -f "$f" ]; then
- echo "$f"
- fi
- done
- touch "a b c.txt"
- for f in a*txt; do
- echo "# $f;"
- done
# a; # b; # c.txt;
So putting double quotes around for variables doesn’t work, does it? You’re right. That’s safer to use while read loops. We can pipe the output of ls to read in a while loop. The ls command outputs a file per line, and read ingests a whole line. Now we’ll do it right:
- ls *jpg | while read f ; do
- if [ -f "$f" ]; then
- echo "$f"
- fi
- done
ls *jpg &>/dev/null || ( echo "Nothing to thumbnail, bye"; exit 0)
ls *jpg | while read f ; do … done
Getting tough yet? If you’re keeping up with me, you’re a bad-ass. Most people skip this stuff. If they script enough, they have a bugger of a time figuring out where their script takes a dump. Let’s put some more meat on this skeleton and make a thumbnail:
- #!/bin/bash
- set -e
- set -x
- ls *jpg &>/dev/null || ( echo "Nothing to thumbnail, bye"; exit 0)
- size=450x300
- options="-quality 96 \
- -thumbnail $size \
- -bordercolor gray \
- -border 10x10 \
- -strip"
- ls *jpg | while read f ; do
- if [ -f "$f" ]; then
- thumbnail="${f/.jpg/}-$size.jpg"
- convert "$f" $options $thumbnail || true
- fi
- done
if [ -f a b c.txt ] and return "a: file not found".
See what I mean about convert not actually being the meat of the script? The script is the meat. Convert is merely the hammer that strikes the iron. Some things you might not have seen before:
- Line continuation looks like this: this \
is the same line - || true lets a command fail but not have set -e kill the script
- ${f/.jpg/} means ${variable/pattern/replacement} Basic pattern replacement! You do not need to call out to sed or perl to do regular expressions.
# set -e
# set -x
Be sure to leave that in so that any other shell script lover will appreciate our careful work!
No comments:
Post a Comment