bash


This would be easy in kde, but in gnome some extra steps are needed. I changed to gnome just recently being impressed by the clarity and simplicity of the user interface. Being used to the “kde way” I needed some time to figure that out.

This is an example for kdissert:

Create a wrapper script for example kdissert.sh in ~/bin/launch and make it executable

#!/bin/bash                                                                                                     
cd ~/Known
nohup kdissert > /dev/null 2>&1 &
exit 0

Then use the panel menu to create a Custom Application Launcher : Add to panel -> Custom Application Launcher -> Choose Type: Application, Command: /home/yourusername/bin/launch/kdissert.sh

Another way is to use shell directly. But watch out – you will need to source your .bashrc if needed:

[Desktop Entry]
Version=1.0
Encoding=UTF-8
Name=netbeans with some environment settings
Exec=/bin/bash -c  '. ~/.bashrc; ~/workarea/nb_run.sh'
# to run it in a terminal:
# Exec=gnome-terminal --command "/bin/bash -c  '. ~/.bashrc; ~/workarea/nb_run.sh' "
Icon=/home/kostja/bin/netbeans-6.5rc2/nb6.5/netbeans.png
Categories=Development;Java;IDE
StartupNotify=true
Type=Application

Recently I had to face a limitation of subversion – you can set files and patterns for ignoring files (using svn:ignore) but you can not specify certain files or patterns for files to be included. In my situation, I wanted to build an Ivy repository and put only the ivy files under the version control cause all other artifacts could be rebuild using SCM.

So I came up with a simple bash script, which is run just before commit and is actually called from an ant script in a build environment:

#!/bin/bash
#
# adds only certain files to repository, adds
# all intermediate directories on their paths,
# it also works with empty space in path (except newline)
#
# @todo Add some bells and whistles!
 
REPO_NAME=<some_repo>
echo "Running $0 on" `date` "with $REPO_NAME"
 
# this file will help to limit the search, if it's modified on commit
# for example the log file of last commit, the idea here is to run 
# this script always before commit
REF_TIME_FILE=<some_file>
REP_PATH=/some/path/to/your/copies/root
 
FILE_PATTERN='ivy.xml*'
DIR=`pwd`
 
cd $REP_PATH
IFS_ORIG=$IFS
# this allows paths with whitespace, but not with newline
IFS=$'\n'
#IFS="$(echo)" another way to get newline
for ivy in `find ./ -newer $REF_TIME_FILE  -type f  -name "$FILE_PATTERN"  -print`
do
	echo "found fresh file $ivy"
	p=$ivy
	while [ $p != "." ]
	do 
			all_paths=${all_paths}${del}${p}
			p=${p%/*}	
			del=:
	done
done
 
IFS=$IFS_ORIG
#echo $all_paths | tr ':' '\n' | sort | uniq |  tr '\n' '\0' | xargs -0 -i echo {}
echo $all_paths | tr ':' '\n' | sort | uniq |  tr '\n' '\0' | xargs -0  svn add --non-recursive  2>&1 | grep "^A"
 
 
cd $DIR
echo "Finish $0 on" `date` "with $REPO_NAME"

It’s not a big deal but might help you to spare some time. There might be better ways to do it and I wonder if same can be done using “plain ant”.

Here is an init script to control hudson start up at boot time. It is tested on SLES 10 . I am not commenting it in detail here, nevertheless you are welcome to ask and leave comments. It’s rather beta but I hope that it could be useful as a starting point.

#!/bin/bash
# Author: Konstantin.rekk@gbv.de, 2008
# Using ideas from http://developer.novell.com/wiki/index.php/Writing_Init_Scripts
#
# install:
# copy to /etc/init.d/gbv-hudson-integration
# ln -s /etc/init.d/gbv-hudson-integration /sbin/rcgbv-hudson-integration
# insserv /etc/init.d/gbv-hudson-integration
 
 
### BEGIN INIT INFO
# Provides:          gbv-hudson-integration
# Required-Start:    $local_fs $network
# Required-Stop:
# Default-Start:     3 5
# Default-Stop:      0 1 2 6
# Short-Description: Starts the hudson server for CI 
# Description:       Starts Continuous Integration System for VZG SOA
### END INIT INFO
 
# to avoid conflict with functions local $1
ARG=$1
 
APP="hudson"
 
HUDSON_USER=soarun
HUDSON_GROUP=soarun
export HOME=/home/${HUDSON_USER}
export VZGSOA_HOME=${HOME}/soa-workarea
 
# include rc.status functions 
test -s /etc/rc.status && . /etc/rc.status && rc_reset
 
# use test_for_app <appl|file> (x|f)
test_for_app ()
{
    app_found=0
    if [ "f" = "$2" ] && [ -f $1 ]; then
        app_found=1
    elif [ "x" = "$2" ] && [ -x $1 ]; then
        app_found=1
    fi
    if [ 0 = $app_found ]; then
        echo -n "Warning:  Couldn't find $1"
        if [ "$ARG" = "stop" ] ; then
            rc_failed 0
        else
            rc_failed 5
        fi
        rc_status -v
        rc_exit
    fi
}
 
test_for_app $VZGSOA_HOME/vzgsoa.env f
test_for_app $VZGSOA_HOME/integration/integration.env f
. $VZGSOA_HOME/vzgsoa.env
. $VZGSOA_HOME/integration/integration.env
 
HUDSON_CMD="${JAVA} -jar ${HUDSON_WAR} --httpPort=${CI_HTTP_PORT} --argumentsRealm.passwd.hudson=${HUDSON_PW} --argumentsRealm.roles.hudson=admin"
 
HUDSON_PIDFILE=/var/run/hudson.pid
HUDSON_LOG=/var/log/hudson
 
usage ()
{
    echo ""
    echo "Usage: $0 <command> "
    echo ""
    echo "where <command> is one of the following:"
    echo "    start - start $APP if not running"
    echo "    stop - stop $APP if running"
    echo "    status - report whether $APP is running"
    echo "    restart - stop and restart $APP"
    echo "    usage, help - print this message"
}
 
get_pid_for_cmd ()
{
    for pid in `ls /proc`; do
        if [ -d /proc/$pid ] && [ -f /proc/$pid/cmdline ]; then
            if [ "$1" = "$(</proc/$pid/cmdline)" ]; then
		# return with current pid 
                return
            fi
        fi
    done
    pid=0
}
 
# checkport <port>
# returns port_used
check_port()
{
	port=$1
	if netstat -nlept 2>/dev/null | grep --regexp=":$port " &>/dev/null ; then
        	port_used=1
    	else
        	port_used=0
    	fi
}
 
# checks hudson, sets/returns status - 
# 0 if pidfile same as pid, running
# 3 no pidfile, not running (what with running?)
# 2 pidfile, but no process (remove file ...), not running
# 1 pidfile != pid, but running
# 4 no pidfile, but running
# 
# @todo: check port usage
check_hudson() 
{
	get_pid_for_cmd `eval echo ${HUDSON_CMD} | sed -e 's/ //g'`
 
	if [ -f $HUDSON_PIDFILE ]; then
		pidfile_pid=`cat $HUDSON_PIDFILE`
		if [ 0 = $pid ]; then
			status=2
		elif [ "$pid" = "$pidfile_pid" ]; then
			status=0;
		else 
			status=1;
		fi		
	else 
		if [ 0 = $pid ]; then
			status=3;
		else 
			status=4;
		fi
	fi
 
}
 
 
test_for_app ${JAVA} x
test_for_app ${HUDSON_WAR} f
 
case "$1" in
    start)
        check_hudson
        case "$status" in
            0)
                echo "${APP} already running"
		rc_failed 0
		rc_status -v1
           	rc_exit
                ;;
	    1|4)
		echo "${APP} seems running but pidfile is not up to date or doesn't exist, please check!"
		rc_failed
                rc_status -v1
                rc_exit
		;;
            2)
                echo "Found stale pidfile for ${APP} - unclean shutdown?"
                rm ${HUDSON_PIDFILE}
                ;;
            3)
                # not running - ok
        	check_port $CI_HTTP_PORT
		if [ $port_used = 1 ]; then
			echo "Port $CI_HTTP_PORT already in use, pleas check!"
                	rc_failed
                	rc_status -v1
                	rc_exit
		fi 
                ;;
            *)
                echo "Check for ${APP} failed"
                rc_failed
                rc_status -v1
                rc_exit
        esac
 
        echo -n "Starting ${APP}"
        startproc -f -u $HUDSON_USER -g $HUDSON_GROUP -p ${HUDSON_PIDFILE} ${HUDSON_CMD} >> ${HUDSON_LOG} 2>&1
        if ! [ 0 = $? ]; then
            echo -n "(Error - "
            case $? in
                2)
                    echo -n "invalid arguments"
                    ;;
                4)
                    echo -n "insufficient permission"
                    ;;
                5)
                    echo -n "no such program"
                    ;;
                7)
                    echo -n "launch failure"
                    ;;
                *)
                    echo -n "unspecified error"
                    ;;
            esac
            echo -n ")"
            rc_failed
            rc_status -v1
            rc_exit
        fi
	# removing spaces from commandline to be able to compare with /proc/<pid>/cmdline
        get_pid_for_cmd `eval echo ${HUDSON_CMD} | sed -e 's/ //g'`
        if [ 0 = $pid ]; then
            echo "Warning - Couldn't obtain PID for ${APP} "
        else
            echo $pid > ${HUDSON_PIDFILE}
        fi
        rc_status -v
        ;;
    stop)
        if [ $1 = "stop" ]; then
            check_hudson
	    if ! [ $status = 0 ]; then
		echo "Warning - not running!"
	    fi
            killproc -p ${HUDSON_PIDFILE} -t 10 ${JAVA}
            rc_status -v
        fi
        ;;
    status)
        check_hudson
	case "$status" in
        0)
            echo -n "(running)"
            rc_failed 0
            ;;
	1|4)  
	    echo -n "(running)"
	    echo -n  "Warning - PID corrupted"
            rc_failed 0
            ;;
        2)
            echo "(not running)"
            echo -n "Warning - PID file found"
            rc_failed 3
            ;;
        3)
            echo -n "(not running)"
            rc_failed 1
            ;;
        *)
            echo "(unknown)"
            echo "Warning - Couldn't get status"
            rc_failed 1
            rc_status -v1
            rc_exit
    esac
    rc_status -v
        ;;
    restart)
        $0 stop &>/dev/null
        $0 start &>/dev/null
        rc_status -v
        ;;
    usage|help)
        usage
        rc_exit
        ;;
    *)
        usage
        rc_failed 1
        rc_status -v
        rc_exit
esac

ls -d --sort=time */ | head -n 1

To exclude certain files and directories from subversion’s version control you can set the ‘svn:ignore’ property as described in Ignoring Unversioned Items.
That works fine but nevertheless you might run into problems if you are using
svn add *
or similiar on linux systems. This is due to the shell expansion of wildcards. The above command is expanded to explicit files names svn add filename and seems to have the same effect as svn add --no-ignore filename.
Tip: Use svn add --force . instead!

Have you ever been surprised by obvious, even trivial facts which you have been overlooking for years?

Today it hit me:
I used shell metacharacters for expansion and even wrote quite complex bash scripts and used tree (not installed by default in ubuntu) to look into a given display depth of the directory tree. But a combination of ls and shell expansion can do quite the same:

ls */*
ls */*/*

Another interesting example from the same category is
echo */
to list all directories in working directory.

One more example – List only the directories :
ls -d */
works similiar to
find . -type d -maxdepth 1 -mindepth 1

Say you want to record a video live stream from the internet for example from the Streaming CCC Congress in Berlin 2007.

This can be accomplished using the glorious mplayer HOWTO Rip Streams With MPlayer.

To stop the recording after a certain amount of time use this simple script (see also my post Finding Child Process Pids … ):

#!/bin/bash
URL=$1
FILE=$2
# duration in minutes
DURATION=$3
mplayer -framedrop -autosync 30 -cache 10000 -dumpstream $URL -dumpfile $FILE &amp;amp;
sleep $DURATIONm
kill %+

Now you need to start this script at a certain time. For that you could use the linux “at” command (see at). To run it you have to export the DISPLAY variable on ubuntu (see Bug #94933):
export DISPLAY=$DISPLAY ; export TERM=$TERM; echo "echo 'Hallo'" | at  now

But I prefer KAlarm from KDE to schedule tasks which should run only once.
Run KAlarm, create New Alarm, choose Action “Command”, enter the path to your script with appropriate arguments for stream url, dumpfile and duration in minutes:
/home/myhome/scripts/stream_ripper.sh mms://streaming-internet.fem.tu-ilmenau.de/saal3 test.asf 60
, adjust all other settings which are quite self explaining. Use the template feature of KAlarm for similiar tasks.

To control background child processes from a shell script you want to know the PID or job-id of the child. Here are some ways to do this:

First starting a background process echoes the job-id and pid of the process:

$ emacs &amp;
[1] 15393

The PID of the last command set to run in the background by the current shell or script is stored in $! variable:
$ echo $!
15393

This you could find out by using the jobs command:
$ PID=`jobs -l | sed -n 's/^\[[0-9]*\] *+  *\([0-9]*\) .*$/\1/p'`; echo $PID
using the fact that the last current (last started) job will be marked by a + in
jobs -l output.
To terminate the current job you don’t need the explicit job-id:
$ kill %+
The exit status will be stored in the $? variable.
Read more in the Advanced Bash-Scripting Guide:Job Control Commands

In linux you can use some shell scripting to find a running server process by port number. I came up with this example using awk (watch out for backticks `):

ps aux | grep --regexp="`netstat -nlept | awk '/:8079 / || /:8080 / {split($9,t,"/"); print t[1]}'`"

where you suspect the server ports to be 8079 or 8080. Problem here – if nothing is found then all processes will be listed. For automation you might prefer:

ps u --no-heading -p  `netstat -nlept | awk '/[:]8080 / {split($9,t,"/"); print t[1]}'` 2>/dev/null

… last part will redirect error/help message in case there is no process.

Or put it into a function:

myserver_info() { ps aux | grep --regexp="`netstat -nlept | awk '/8079/ || /8080/ {split($9,t,"/"); print t[1]}'`"; }

this definition you can add to your .bashrc .

Usage:

myserver_info

or pass it within a pipe to “kill <pid>” command for example.

You can extend it further by passing the ports as function arguments … (see Advanced Bash-Scripting Guide: Functions)

Do you know a shorter way to do the same?