#!/bin/bash # search disk for a string and output commands to dump matching sections # License: LGPLv2 # Author: # http://www.pixelbeat.org/ # Notes: # On my Fedora 8 system members of the "disk" group are # allowed to read the disk devices, so to do that: # sudo /usr/sbin/usermod -a -G "disk" $USER # sudo su - $USER # Changes: # V0.1, 20 Apr 2008, Initial release # V0.3, 29 Sep 2010 # http://github.com/pixelb/scripts/commits/master/scripts/disk_grep proc usage { echo "Usage: $[basename $0] file|device string" > !2 exit 1 } setglobal COLUMNS = $[tput cols] #pass % complete proc progress { setglobal DONE = $shExpr(' (($COLUMNS-2)*$1)/100 ') setglobal TODO = $shExpr('$COLUMNS-2-$DONE') setglobal DONE = $[printf "%$(DONE)s" | tr ' ' =] setglobal TODO = $[printf "%$(TODO)s" | tr ' ' -] printf "[$DONE$TODO]\r" > /dev/tty } #pass strings to stdout proc print { setglobal CLEAR = $[printf "%$(COLUMNS)s] printf "$CLEAR\r" echo @Argv } test $Argc -ne 2 && usage setglobal DISK = $1 setglobal STRING = $2 export LANG=C # Note we must manage these limits ourselves # because dd currently does not fail if you seek # past the end of a file or device. In fact for devices # it reads the whole device when you do this! # # This also allows us to show completion progress setglobal CHUNK_SIZE = $shExpr('8*1024*1024') if test -b $DISK { setglobal SIZE = $[/sbin/blockdev --getsize64 $DISK] || exit $? #reads from disk? } else { setglobal SIZE = $[stat --format %s $DISK] || exit $? } test $SIZE -eq 0 && exit setglobal CHUNKS = $shExpr('($SIZE+$CHUNK_SIZE-1)/$CHUNK_SIZE') setglobal i = '0' while true { progress $shExpr('$i*100/$CHUNKS') test $i -ge $CHUNKS && break # use "direct" flag so cache not polluted with read data dd if=$DISK iflag=direct conv=noerror bs=$CHUNK_SIZE count=1 skip=$i !2 >/dev/null | #we ensure grep reads all data, otherwise dd will get SIGPIPE and exit with 141 grep --binary-files=text -U -F $STRING >/dev/null setglobal status = $[echo $(PIPESTATUS[@])] setglobal dd_status = $[echo $status | cut -f1 -d ' ] setglobal re_status = $[echo $status | cut -f2 -d ' ] test $dd_status -ne 0 && do { echo "dd error" > !2; exit $dd_status; } test $re_status -eq 0 && do { print "dd if=$DISK iflag=direct bs=$CHUNK_SIZE count=1 skip=$i > disk_grep.$i"; } setglobal i = $shExpr('$i+1') } echo