# This bash script must be used with 'source' or '.' # and so it does not need to be made executable. # . scriptname Display last 35 history lines (default) # . scriptname n Display last n history lines # The lines are displayed with their line numbers. # Enter a list of the lines to be deleted # An example of a line list is 90 45-56 78- # 90 Delete line 90 # 45-56 Delete lines 45 to 56 (can also use 56-45) # 78- Delete line 78 to the last line # Number ranges can be in any order in the list and can overlap. # The lines are deleted in memory, not from the history file. displaylines=35 # default #displaylines= # if unset all of the history will be displayed if [ "$1" ];then displaylines=$1;fi # or use $1 instead of default lastline=$(echo $(history 1)) # Last line, with leading spaces removed lastlinenum=${lastline%%[* ]*} # Extract line number from the last line # Note: '*' after a line number in bash history indicates a modified line # history -d$lastlinenum # Delete the command which ran this script (optional) history $displaylines # Display history while true;do if [ "${displaylines//[0-9]/}" ];then break;fi # If it contains non-integer characters. read -ep "Lines to delete (e.g. 67-89 45 103- ) or enter nothing to quit: " numbers if ! [ "$numbers" ];then break;fi # exit if nothing entered numbers="$numbers " # Append a space to the line number list numbers=${numbers//- /-$lastlinenum } # e.g change '23- ' to '23-lastlinenumber ' numbers=$(echo $numbers) # Remove unnecessary spaces # Check the entered number list is valid: errors=$(echo "${numbers// / }"|while read line;do # [ "not just digits and -" OR "more than one -" OR "starts with -" ] && echo 'error' [ "${line//[0-9-]/}" -o "${line%%*-*-*}" = '' -o "${line:0:1}" = '-' ] && echo 'error' done) if [ "$errors" ];then echo "Bad line number list, try again:";continue;fi # Expand line ranges n-m to a list of individual line numbers numbers=$(echo "${numbers// / }" | while read line;do if [ "${line#*-}" != "$line" ];then # Found a line containing '-' (a range 'n-m') n=${line%-*} m=${line#*-} if [ $n -lt $m ];then line="$m-$n";fi # Descending order for faster reverse sorting line="{${line/-/..}}" # Change 'n-m' to '{n..m}' fi eval echo $line # '{n..m}' will be expanded to a list of numbers (brace expansion) done) a=( $numbers ) # Put line numbers into an array for reverse unique sorting sorted="no" while [ "$sorted" = "no" ];do unset sorted for ((i=0;i<${#a[@]}-1;i++));do if (( ${a[i]} < ${a[i+1]} ));then # Sort by comparing/swapping adjacent elements tmp=${a[i]} a[i]=${a[i+1]} a[i+1]=$tmp sorted="no" fi done done for ((i=0;i<${#a[@]}-1;i++));do # Remove duplicate line numbers from the sorted array if (( ${a[i]} == ${a[i+1]} ));then a[i]= fi done a=( ${a[@]} ) # Remove empty array elements created by removing duplicates i=${#a[@]} # Number of array elements # Check that the reverse sorted list is in the range $lastlinenum to 1 if [ ${a[0]} -gt $lastlinenum -o ${a[i-1]} -lt 1 ];then echo "Line number list is out of range, try again:" continue fi echo "Deleting these lines from the above history: ${a[@]}" echo "***********************************************************************" for ((i=0;i<${#a[@]};i++));do a[i]="history -d${a[i]};" # Create the delete commands e.g. history -d67; .... done eval ${a[@]} # Execute all the 'history -d' commands lastline=$(echo $(history 1)) # New last line, with leading spaces removed lastlinenum=${lastline%%[* ]*} # Extract the line number from the new last line history $displaylines # Refresh the displayed history done unset a errors i lastline lastlinenum line numbers sorted tmp # Finished; tidy up