ddwrapper

Wrapper Script for dd.

This is a simple wrapper script for dd. It runs dd and displays the progress (total transferred and average speed). The status update rate is configurable with the -d flag. It accepts all parameters dd would accept too. Reading from or writing to stdout is not possible with this version.

#!/bin/bash
#
# $Id: dd.sh,v 1.6 2007/03/01 16:11:37 flip Exp $
#
# Copyright (c) 2006, 2007 Philippe Kehl <phkehl at gmx dot net>
#
# dd.sh -- dd wrapper script to display progress and speed
#
# This is Free Software blabla etc.
#

DD=${DD:-dd}
DEBUG=${DEBUG:-}
delay=1

function usage()
{
    echo "Usage: [DEBUG=1] [DD=/path/to/dd] ${0##*/} [-d <delay>] \"
    echo "       if=.. of=.. [ibs=.. obs=.. |bs=..] [skip=..] [count=..] [..]"
    exit 1
}


# get command line parameters

[ $# -lt 2 ] && usage

while [ "${1:0:1}" = "-" ]; do
    echo "loop1"
    case $1 in
	-d)
	    delay=$2;
	    shift
	    ;;
	*)
	    echo "Error: Unknown flag $1."
	    exit 1
	    ;;
    esac
    shift
done
    
[ $# -lt 2 ] && usage

while [ ! -z $1 ]; do
    k=${1%%=*}
    v=${1##*=}
    case $k in
        "if")
	    p_if=$v
	    ;;
        of)
	    p_of=$v;
	    of_dir=${p_of%/*}/
	    [[ $of_dir = $p_of/ ]] && of_dir=./
	    ;;
	bs)
	    p_bs=$v
	    ;;
        obs)
	    p_obs=$v;
	    ;;
        ibs)
	    p_ibs=$v;
	    ;;
        cbs)
	    p_cbs=$v;
	    ;;
        conv)
	    p_conv=$v;
	    ;;
        count)
	    p_count=$v;
	    ;;
        seek)
	    p_seek=$v;
	    ;;
	skip)
	    p_skip=$v;
	    ;;
    esac
    shift
done


# check parameters

[[ -z $p_if || -z $p_of ]] && 
    { echo "Error: Both if and of are mandatory."; exit 1; }

[[ ! -z $p_bs && ( ! -z $p_obs || ! -z $p_ibs ) ]] && 
    { echo "Error: Do not mix bs with ibs or obs."; exit 1; }

[[ ( -z $p_obs && ! -z $p_ibs ) || ( -z $p_ibs && ! -z $p_obs) ]] && 
    { echo "Error: Specify both ibs and obs."; exit 1; }

[[ ! -r $p_if ]] && 
    { echo "Error: Cannot read from $p_if."; exit 1; }

[[ -e $p_of && ! -w $p_of ]] && 
    { echo "Error: Cannot write to $p_of."; exit 1; }

[[ ! -e $p_of && ! -w $of_dir ]] && 
    { echo "Error: Cannot write in $of_dir (or $p_of does not exist)."; exit 1; }


# combine parameters for dd

dd_arg=
for p in ${!p_*}; do 
    eval dd_arg=""$dd_arg ${p#*_}=$$p""
done
[ ! -z $DEBUG ] && echo "dd_arg=$dd_arg"


# create a fifo to watch dd output

while true; do
    dd_out=/tmp/${0##*/}-$RANDOM.tmp
    [ ! -e $dd_out ] && { mkfifo -m 600 $dd_out; break; }
done
[ ! -z $DEBUG ] && echo "dd_out=$dd_out"


# run dd
LC_ALL=C dd $dd_arg 2> $dd_out &
dd_pid=$!
[ ! -z $DEBUG ] && echo "dd_pid=$dd_pid"


# create a trap to abort dd nicely
abort=0
function abortdd()
{
    abort=1
    echo
    echo -n "Aborting "
    while [ -d /proc/$dd_pid ]; do
	echo -n "."
	#kill -SIGINT $dd_pid # FIXME: why does this not work?
	kill $dd_pid
	sleep 1
    done
    echo " done."
    abort=2
}
trap abortdd SIGINT SIGKILL SIGHUP


# redirect errors
exec 2>/dev/null


# routine to convert bytes mb

function b2mb()
{
    local m
    m=$(($1*100/1024/1024))
    m=${m:0:$((${#m}-2))}.${m: -2}
    echo $m
}


# watch dd output

while [ $abort -eq 0 ]; do

    sleep $delay
    [ $abort -eq 1 ] && continue
    [ $abort -eq 2 ] && break
    kill -SIGUSR1 $dd_pid
    read l1
    read l2
    read l3
    #echo $l1 " / " $l2 " / " $l3
 
    # convert bytes per second to mb per second
    bps=${l3##*(}; bps=${bps%% *}
    mps=$(b2mb $bps)
    b=${l3%% bytes *}
    m=$(b2mb $b)
    t=${l3##* transferred in }; t=${t%%.*};
    echo -ne "rStatus: $l1, $l2, $m MB transferred in $t seconds ($mps MB/sec)"

     #[ $abort -eq 2 ] && break
done < $dd_out

rm -f $dd_out


#eof