Shell scripting and CGI

Last edit

Changed:

< = Online resources =
< [http://ldp.nl.uu.net/LDP/abs/html/ Advanced Bash-Scripting Guide]
< = Testing for a certain extension =
< If you want to test whether a file has a certain extension, use the following trick:
< # Test for the extension "plist"
< if [ "${x%%plist}" == "$x" ]
< then
< echo "Does not have the extension"
< else
< echo "Has the extension"
< fi
< Basically, you try to strip off the extension (in this case, "plist"), and if stripping succeeded, it won't be the same as the original filename.
< = Disabling or handling CTRL-C =
< Sometimes you
want to temporarily disable signals like Interrupt (CTRL-C). The solution:
< trap '' INT # Disable interruptions
< # Do whatever you want
here which must be uninterruptible
< sleep 10
< trap - INT # Restore normal function of the interrupt signal
< To do a cleanup or similar after the user hits CTRL-C
:
< trap "rm the_temporary_file; exit;" INT TERM
< sleep 10 # Do lots of nifty stuff here
< trap - INT TERM
< = Testing configuration of the webserver =
< Place
[[test.cgi]] in a directory which is supposed to be configured to run CGI scripts and test with browser.
< = Using functions =
< function print_error ()
< {
< echo "Parameter 1: $1"
< echo "Parameter 2: $2"
< }
< print_error "First param" "Second param"
< = Return values =
< You can't really return values in a function in bash, however you ''can'' echo text and then catch output with backticks.
< function return_a_string ()
< {
< echo "This is the return value"
< }
< RETURN_VALUE=`return_a_string()`
< echo $RETURN_VALUE
< = Saving a text file =
< To save a text file, create the following HTML file:
< <form action="/cgi-bin/post.sh" method="post"
< enctype="multipart/form-data">
< <input name="file" file">
< <input type="submit" value="Submit">
< </form>
< Then, in your cgi-bin, create a shellscript with the following lines:
< #!/bin/sh
< while read var
< do
< echo $var >> outputfile
< done
< echo "Content-type: text/plain"
< echo ""
< echo "Thanks!"
< Of course, stuff gets pasted by the browser around the text. And this is a security hole to leave it wide open like this. So don't.
< = Saving a binary file =
< To save a binary or text file, create the HTML file like mentioned in the previous example. Then, in your cgi-bin, create a shellscript with the following lines:
< boundary=$(export | \
< sed '/CONTENT_TYPE/!d;s/^.*dary=//;s/.$//')
< filename=$(echo "$QUERY_STRING" | \
< sed -n '2!d;s/\(.*filename=\"\)\(.*\)\".*$/\2/;p')
< file=$(echo "$QUERY_STRING" | \
< sed -n "1,/$boundary/p" | sed '1,4d;$d')
< The uploaded file is now contained in the $file variable.
< = Debugging =
< Debugging is easiest done by writing to stderr. When the script is run through CGI, this ends up in the web server's error log:
< echo "This is a debugging message" >&2
< = Setting cookies =
< Before printing the Content-type, set a cookie with:
< value="some value"
< echo Set-Cookie: name=$value
< = Useful preset parameters =
< SERVER_SOFTWARE = Apache/2.0.52 (Fedora)
< SERVER_NAME = localhost
< SERVER_PORT = 80
< REQUEST_METHOD = GET
< SCRIPT_NAME = /~b.kuik/cgi-bin/test.cgi
< QUERY_STRING = name=value1&name2=value2
< REMOTE_HOST =
< REMOTE_ADDR = 127.0.0.1
< REMOTE_USER =
< AUTH_TYPE =
< CONTENT_TYPE =
< CONTENT_LENGTH =
< = Parsing parameters and cookies =
< To parse parameters and cookies, include [http://bashlib.sourceforge.net/ Bashlib] like this:
< . ./bashlib
< Then, read cookies and parameters like this:
< sender=`param sender`
< recipient=`cookie recipient`
< And test whether they're actually passed with:
< if [ -z $sender ]; then
< echo "Sender param not passed!" >&2
< fi
< = Testing a button pressed =
< The HTML looks like this :
< <input type="submit" value="Save" name="button1">
< Then test like this:
< button1=`param button1`
< if [ "x$default" != "x" ]; then
< # do the default
< fi
< = Starting a job in the background =
< Problem: you want to start a process in the background, but want your CGI script to finish:
< #!/bin/sh
< process_that_takes_a_long_time >/dev/null 2>&1 &
< echo Content-type: text/plain
< echo
< echo Finished!
< This doesn't work. The reason is that the started process keeps its stdout and stderr open, it's just redirected to the null device. Use the following syntax to close them:
< process_that_takes_a_long_time >&- 2>&- &
< Your script will immediately finish.
< = Passwords and kill catching =
< trap "stty echo ; exit" 1 2 15
< stty -echo
< read password
< stty echo
< trap "" 1 2 15
< If the user press Ctrl+C in the password prompt, the normal stty mode will be restored
< = Run commands as another user =
< To run a single command as another user in a script run by root:
< su mysql -c mysql_install_db
< If you want to run multiple commands, a HERE document is very useful:
< su - mysql <<HERE
< execute
< some
< commands
< as
< user
< HERE
< Be careful though; environment variables are taken from the environment outside of the su statement, ''not'' the user that was switched to. Take, for example, the following script:
< #!/bin/sh
< su - nobody << HERE
< echo "User is: $USER"
< echo "Output of id: ${ID}"
< HERE
< When running as root, this will print:
< User is: root
< Backticks also seem problematic, better not use them inside the su HERE block.
< = Hiding passwords =
< Sometimes, you need to put a password in a shell script. There are several methods to hide passwords from the unintentional glance by those who you trust. And you need to trust them, since the password can easily be recovered.
< * [http://www.datsi.fi.upm.es/%7Efrosal/ shc] the shell script encrypter
< * Use something like rot13; i.e. first "encrypt" your password and in the shell script, "decrypt" it:
< $ echo secret_password | rot13
< frperg_cnffjbeq
< In the shell script, do something like:
< PASSWORD=`echo frperg_cnffjbeq | rot13` # Colleagues, please don't look at this password
< = Dialog boxes =
< To bring up a dialog box through a shellscript in X, use the xdialog package:
< Xdialog --msgbox "Don't forget your coffee" 10 50
< = Creating temporary files and directories =
< The safest method is to use <tt>mktemp</tt>:
< TMPFILE=`mktemp`
< echo "Very important data" > $TMPFILE
< The <tt>mktemp</tt> command creates a unique file in <tt>/tmp</tt> and prints the name. The backticks catch the name and put it in the TMPFILE variable.
< If you need a directory, add the <tt>-d</tt> option.
< If you need a prefix, pass a template where the row of capital X'en is replaced by a unique string:
< TMPFILE=`mktemp -t hello_XXXXXXXXXX` || exit 1
< echo "Very important data" > $TMPFILE
< = Logging logins =
< # If we're logging in through SSH, write this down
< if [ -n "$SSH_CLIENT" ]; then
< LOGFILE=".ssh/.mylog"
< # The variable SSH_CONNECTION has the form
< # FROM_IP FROM_PORT TO_IP TO_PORT
< if [ -e $LOGFILE ]; then
< echo "`date`: SSH_CONNECTION $SSH_CONNECTION" >> $LOGFILE
< else
< echo "`date`: SSH_CONNECTION $SSH_CONNECTION" > $LOGFILE
< fi
< # Alternative settings
< SRON0311="172.16.140.14"
< FROM=`echo $SSH_CLIENT | cut -f1 -d" "`
< case $FROM in
< *$SRON0311)
< export TMOUT=180 #Logout after 3 minutes
< ;;
< esac
< fi
< = Removing prefixes and extensions =
< The other substring operator is "#" which removes prefix patterns. If you think about it, "#" is used before a number, e.g. #6, and "%" appears afterwards, e.g. "6%". This will help keep it clear which one removes prefixes and which suffixes.
< The other thing to note about these operators is that a _single_ # or % means match the shortest substring and that doubling the operator means match the longest substring. i.e. "%%" and "##".
< These let you avoid a lot of external programs in shell scripts. e.g. dirname(1) and basename(1) can be more effeciently done within sh as "${file%/*} and "${file##*/}.
< FYI, here's the chunk from the "Paremter Expansion" section of the Bourne shell sh(1) manpage. Note the below are all part of the Single Unix Specification standard and have been for years.
< ${parameter%word}
< Remove Smallest Suffix Pattern. The word is expanded to produce
< a pattern. The parameter expansion then results in parameter,
< with the smallest portion of the suffix matched by the pattern
< deleted.
< ${parameter%%word}
< Remove Largest Suffix Pattern. The word is expanded to produce a
< pattern. The parameter expansion then results in parameter, with
< the largest portion of the suffix matched by the pattern deleted.
< ${parameter#word}
< Remove Smallest Prefix Pattern. The word is expanded to produce
< a pattern. The parameter expansion then results in parameter,
< with the smallest portion of the prefix matched by the pattern
< deleted.
< ${parameter##word}
< Remove Largest Prefix Pattern. The word is expanded to produce a
< pattern. The parameter expansion then results in parameter, with
< the largest portion of the prefix matched by the pattern deleted.
< -- From a post by Adrian Filipi
< Some examples...
< To print the current directory name without the leading path:
< $ echo ${PWD##*/}
< In a script, to print the script name:
< echo ${0##*/}
< To remove the extension from a filename:
< FILENAME="some_package.tar.gz"
< echo ${FILENAME%.tar.gz}
< = Script displaying feedback =
< The following bash function can be included in your scripts to provide feedback to the user whenever an action (such as starting a process) takes time. Instead of the ''while [ true ]'', you should put a condition there which can be periodically checked.
< function bounce() {
< dot=1
< while [ true ]; do
< sleep 0.5
< if [ $dot -eq 0 ]; then
< echo -n $'\b'
< echo -n "."
< dot=1
< else
< echo -n $'\b'
< echo -n " "
< dot=0
< fi
< done
< }

to

> You may want to go here: [[Bash]]


You may want to go here: Bash