On Sunday 29th April 2012, I have described How to check dd’s progress using User-defined signal 1.
Today, I will extend this topic further using the proc filesystem.

The idea

This technique reads and parses the IO statistics file for running the dd process.

This file is described in detail in section 3.3 [/proc//io – Display the IO accounting fields] of
the /proc filesystem documentation.

$ ./dd.sh if=infile of=outfile
   1 GB

This is the simplest solution as it only prints how much data has been written in human-readable form.

#!/bin/sh
# print how much data has been written to the file or device
# dd output will overwrite printed data at the end
#
# sample usage:
# dd.sh if=infile of=outfile

# execute dd command in the background
$(which dd) $* &

# read process ID
process=$!

# execute until the process exists
while [ "$(kill -0 $process 2>/dev/null; echo $?)" -eq "0" ]; do
  if [ -e "/proc/${process}/io" ]; then
    # get number of written bytes
    written_data=$(cat /proc/${process}/io | awk '/wchar/ {print $2}')

    # convert it to human readable form
    unit="B"                                     # bytes

    if [ "$written_data" -gt "1024" ]; then
      written_data=$(expr $written_data / 1024)  # kilobytes
      unit="KB"
    fi

    if [ "$written_data" -gt "1024" ]; then
      written_data=$(expr $written_data / 1024)  # megabytes
      unit="MB"
    fi

    if [ "$written_data" -gt "1024" ]; then
      written_data=$(expr $written_data / 1024)  # gigabytes
      unit="GB"
    fi

    if [ "$written_data" -gt "1024" ]; then
      written_data=$(expr $written_data / 1024)  # terabytes
      unit="TB"
    fi

    # print output
    printf "%4s %2s\r" ${written_data} ${unit}

    # wait 1 second
    sleep 1
  else
    break
  fi
done
$ sh ./dds.sh test test2
  67 %

This shell script is different as it prints the percentage value of written data. The most interesting part is that it needs to know the file or device size in advance.

#!/bin/sh
# print how much data has been written to the file or device using percentage value
# dd output will overwrite printed data at the end
#
# sample usage:
# dds.sh infile outfile

# command arguments
file_from=$1
file_to=$2

# defaults settings
bs="20M"

if [ -e "$file_from" ]; then
  if [ -b "$file_from" ]; then
    file_from_size=$(blockdev --getsize64 $file_from)
  else
    file_from_size=$(stat --format "%s" $file_from)
  fi

  $(which dd) if=${file_from} of=${file_to} bs=${bs} &

  # read process ID
  process=$!

  # execute until the process exists
  while [ "$(kill -0 $process 2>/dev/null; echo $?)" -eq "0" ]; do
    if [ -e "/proc/${process}/io" ]; then
      # get number of written bytes
      written_data=$(cat /proc/${process}/io | awk '/wchar/ {print $2}')

      percent=$(expr 100 \* $written_data / $file_from_size)
      # convert it to human readable form

      # print output
      printf "%4s %%\r" ${percent}

      # wait 1 second
      sleep 1
    else
      break
    fi
  done
fi
$ sh ./ddb.sh test test2
  41 % [++++++++++++++++                        ]

This shell script uses the technique described in Creating simple bar charts in terminal using an awk blog post to print bar chart.

#!/bin/sh
# print how much data has been written to the file or device using percentage value
# dd output will overwrite printed data at the end
#
# sample usage:
# ddb.sh infile outfile

# command arguments
file_from=$1
file_to=$2

# defaults settings
bs="20M"

# character used to print bar chart
barchr="+"

# current min, max values
vmin=1
vmax=100

# range of the bar graph
dmin=1
dmax=40

if [ -e "$file_from" ]; then
  if [ -b "$file_from" ]; then
    file_from_size=$(blockdev --getsize64 $file_from)
  else
    file_from_size=$(stat --format "%s" $file_from)
  fi

  $(which dd) if=${file_from} of=${file_to} bs=${bs} &

  # read process ID
  process=$!

  # execute until the process exists
  while [ "$(kill -0 $process 2>/dev/null; echo $?)" -eq "0" ]; do
    if [ -e "/proc/${process}/io" ]; then
      # get number of written bytes
      written_data=$(cat /proc/${process}/io | awk '/wchar/ {print $2}')

      percent=$(expr 100 \* $written_data / $file_from_size)

      # generate output
      awk --assign dmin="$dmin" --assign dmax="$dmax" \
          --assign vmin="$vmin" --assign vmax="$vmax" \
          --assign percent="$percent" \
          --assign barchr="$barchr" \
          'BEGIN {
            x=int(dmin+(percent-vmin)*(dmax-dmin)/(vmax-vmin));
            printf "%4i %% [", percent
            for(i=1;i<=dmax;i++){if(i <= x) printf barchr; else printf " "};
            printf "]\r"
            if(x == dmax)
              printf "\033[0K"
          }'

      # wait 1 second
      sleep 1
    else
      break
    fi
  done
fi