I'm starting to get sick of shell scripts to perform automations and glue codes between stuff. I love using it for quick and dirty data processing, but even for simple 3 line code that spawns a process and remembers 开发者_JS百科its process id, it's taking me very long time to program it correctly.
- For every commands, if I don't explicitly check for return codes, the script might terminate with exit code 0 even when I don't want to. so each shell command gets followed by if statement to see whether the program terminated correctly or not..
- Passing variables, and writing robust command line argument parser is hard (something like
optparse
in Python). - It's very hard to debug.
I use python for most of my work, and yet, it feels bit verbose when I'm trying to use it for shell-scripting purposes, if I start to use the subprocess
module.
I was thinking whether there's a good middle ground between this. Like, either writing robust shell script without being so verbose, or writing less verbose automation scripts in higher level language such as Python.
Have you thought about using "set -e" if you can depend on the exit status of the programs you're running?
Have you looked at using ruby. It has a couple of bits of syntaxtic sugar to make writing shell like scripts easy. In particalar back tick quoted strings work the same way and %x{..}
as well. In fact there is five ways to run an external command in ruby.
What is the question? I don't think many would consider Python 'verbose'. It is brought up often to show how a language can NOT be verbose compared to, say, Java.
By the way, Perl, syntactically and historically, can be placed between shell-scripting and Python, I think.
All good suggestions above, especially set -e
, which would be a good basic solution to your objection #1.
Another approach to trapping shell sub-process errors is to surround with an if statement, i.e.
if true ; then
printf "success\n"
else
printf "failure\n"
fi
would be the most basic illustration of this idea.
Edit
Here is a more advanced example, modified from another posting here on S.O.
if ls /path/to/files* /dev/null 2>&1 ; then
echo "files do exist"
else
echo "files do not exist"
fi
Finally, is is possible to embed full pipe-line commands with the if cmd ; then
syntax, but the last call in the pipe line is the what returns the true or falseness of the whole pipeline.
end of edit
Unfortunately, some Unix utility programs are not perfect (for this purpose) with their return codes, but that is very platform specific (Maybe I'm thinking of Sun4 sed, and definitely many ftp clients). It could be that a good Linux distro doesn't have this issue, but you need to check (and maybe document) each utility as you start to use it.
For objection #2, argument parsing, I can offer a complete side-step of that issue, with the negative being code that other may find hard to comprehend at first.
Rather than relying on while loop with getargs and case statements to process the arguments look to see if prepending environment variables to calling the subprocess will help you, i.e.
code with flags
cat myProgram.sh
#! /bin/bash
set -e
if ${testingMode:-false} ; then
printf "${0##*/}:TestingMode:Starting:args=${@}"
fi
# more code
Now, rather than having any internal processing embedded to handle testingMode, you pre-pend the variable to the script command line, and 'turn it on' temporarily, i.e.::
testingMode=true ./myProgram.sh otherargs that makes sense as args
you can have as many variables pre-pended to the front of the command as you like, so its conceivable to build a script that doesn't have any argument processing. Sometimes it makes sense and sometimes it doesn't. (Incidentally, I got this technique from reading Kernighan and Pike's 'The Unix Programming Environment')
Again, if you have to worry about passing this code off as you move to another job in the same organization, you might have to have a training class on using these little used feaure of the shell.
Your final objection, 'hard to debug' is more difficult to defend, especially errors related to mismatched {}, (), ", and '. There are numerous ksh debuggers that work along the lines of the early c-language debuggers, dbx, and gdbx.
Good luck, and let us know when you find a programming language that can 'do what I mean' :-)!!
I hope this helps.
This feels like a subjective, open-ended question, but I'll add my two cents.
Error Handling
set -e
is unreliable; different implementations interpret POSIX's definition of -e
differently.
Here's what I do to catch errors:
log() { printf '%s\n' "$*"; }
error() { log "ERROR: $*" >&2; }
fatal() { error "$*"; exit 1; }
try() { "$@" || fatal "command '$@' failed"; }
try echo "before the false"
try false
try echo "after the false"
outputs:
before the false
ERROR: command 'false' failed
Argument Parsing
The template I use for argument parsing can be found in this answer.
精彩评论