1#!/usr/local/bin/bash 2 3# Copyright (c) 2015-2020 Oliver Mahmoudi 4# All rights reserved. 5# 6# Redistribution and use in source and binary forms, with or without 7# modification, are permitted providing that the following conditions 8# are met: 9# 1. Redistributions of source code must retain the above copyright 10# notice, this list of conditions and the following disclaimer. 11# 2. Redistributions in binary form must reproduce the above copyright 12# notice, this list of conditions and the following disclaimer in the 13# documentation and/or other materials provided with the distribution. 14# 15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 17# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 19# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 23# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 24# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25# POSSIBILITY OF SUCH DAMAGE. 26 27# mapdir - A utility to map files and directory hierarchies 28 29# Functions: 30# readfile() 31# check_excludes() 32# process_new_dir() 33# log_entry() 34# get_file_stats() 35# get_filename() 36# pretty_output() 37# check_savefile_existence() 38# usage() 39 40# Global variables: 41READLINK= 42CHECKSUM= 43DATE=$(date +%m%d%Y) 44DEPTH=0 45SAVEFILE= 46STRLEN= 47EXCLUDES= 48 49# Global variables that are used for the statistics at the end of the program. 50DIRS=0 51UNREADABLE_DIRS=0 52FILES=0 53UNREADABLE_FILES=0 54BLOCK_SPECIALS=0 55CHARACTER_SPECIALS=0 56PIPES=0 57SOCKETS=0 58SYMBOLIC_LINKS=0 59UNKNOWN=0 60TOTAL_FILES=0 61SAVEIFS=$IFS # save the current Internal Field Seperator 62IFS=$(echo -en "\n\b") # and set a new one 63export LC_COLLATE=C 64 65# Global flags for getopts: 66d_flag=0 # dotglob flag 67e_flag=0 # exclude files flag 68e_list= # list of files to be excluded 69f_flag=0 # omit startdir flag 70h_flag=0 # use sha256 flag 71n_flag=0 # no savefile flag 72p_flag=0 # display output as directory tree 73s_flag=0 # alternate savefile flag 74s_file= # the alternate savefile itself 75t_flag=0 # print statistics flag 76x_flag=0 # excludes from file flag 77x_file= # the excludes file itself 78 79# 80# This is the main function. 81# 82readfile() 83{ 84 local _file _depth _psymbol _pnoes _pbinstring 85 86 _file=$1 87 _depth=$3 88 _psymbol=$4 89 _pnoes=$5 90 _pbinstring=$6 91 92 # Continue? 93 $(check_excludes $_file) 94 if [ $? -eq 1 ] ; then 95 return 96 fi 97 98 if [ -d $_file ]; then 99 if [ -r $_file ] && [ -x $_file ]; then 100 DIRS=$((DIRS+1)) 101 TOTAL_FILES=$((TOTAL_FILES+1)) 102 if [ $f_flag -eq 0 ]; then 103 log_entry $_file directory fullpath 104 process_new_dir $_file 105 else 106 if [ $p_flag -eq 1 ]; then 107 pretty_output $_file $_psymbol $_pbinstring 108 process_new_dir $_file $_depth 109 elif [ $2 -eq 0 ]; then 110 log_entry $_file directory filename_only 111 process_new_dir $_file 112 elif [ $2 -eq 1 ]; then 113 process_new_dir $_file 114 fi 115 fi 116 else 117 UNREADABLE_DIRS=$((UNREADABLE_DIRS+1)) 118 TOTAL_FILES=$((TOTAL_FILES+1)) 119 if [ $f_flag -eq 0 ]; then 120 log_entry $_file "directory is not readable" fullpath 121 else 122 if [ $p_flag -eq 1 ]; then 123 pretty_output $_file $_psymbol $_pbinstring 124 elif [ $2 -eq 0 ]; then 125 log_entry $_file "directory is not readable" filename_only 126 fi 127 fi 128 fi 129 elif [ -b $_file ]; then 130 BLOCK_SPECIALS=$((BLOCK_SPECIALS+1)) 131 TOTAL_FILES=$((TOTAL_FILES+1)) 132 if [ $f_flag -eq 0 ]; then 133 log_entry $_file "block special file" fullpath 134 else 135 if [ $p_flag -eq 0 ]; then 136 log_entry $_file "block special file" filename_only 137 else 138 pretty_output $_file $_psymbol $_pbinstring 139 fi 140 fi 141 elif [ -c $_file ]; then 142 CHARACTER_SPECIALS=$((CHARACTER_SPECIALS+1)) 143 TOTAL_FILES=$((TOTAL_FILES+1)) 144 if [ $f_flag -eq 0 ]; then 145 log_entry $_file "character special file" fullpath 146 else 147 if [ $p_flag -eq 0 ]; then 148 log_entry $_file "character special file" filename_only 149 else 150 pretty_output $_file $_psymbol $_pbinstring 151 fi 152 fi 153 elif [ -L $_file ]; then 154 SYMBOLIC_LINKS=$((SYMBOLIC_LINKS+1)) 155 TOTAL_FILES=$((TOTAL_FILES+1)) 156 if [ $f_flag -eq 0 ]; then 157 log_entry $_file "symbolic link" fullpath 158 else 159 if [ $p_flag -eq 0 ]; then 160 log_entry $_file "symbolic link" filename_only 161 else 162 pretty_output $_file $_psymbol $_pbinstring 163 fi 164 fi 165 elif [ -f $_file ]; then 166 if [ -r $_file ]; then # it's a readable file 167 FILES=$((FILES+1)) 168 TOTAL_FILES=$((TOTAL_FILES+1)) 169 if [ $f_flag -eq 0 ]; then 170 get_file_stats $_file readable fullpath 171 else 172 if [ $p_flag -eq 0 ]; then 173 get_file_stats $_file readable filename_only 174 else 175 pretty_output $_file $_psymbol $_pbinstring 176 fi 177 fi 178 else # unreadable file 179 UNREADABLE_FILES=$((UNREADABLE_FILES+1)) 180 TOTAL_FILES=$((TOTAL_FILES+1)) 181 if [ $f_flag -eq 0 ]; then 182 get_file_stats $_file unreadable fullpath 183 else 184 if [ $p_flag -eq 0 ]; then 185 get_file_stats $_file unreadable filename_only 186 else 187 pretty_output $_file $_psymbol $_pbinstring 188 fi 189 fi 190 fi 191 elif [ -p $_file ]; then 192 PIPES=$((PIPES+1)) 193 TOTAL_FILES=$((TOTAL_FILES+1)) 194 if [ $f_flag -eq 0 ]; then 195 log_entry $_file pipe fullpath 196 else 197 if [ $p_flag -eq 0 ]; then 198 log_entry $_file pipe filename_only 199 else 200 pretty_output $_file $_psymbol $_pbinstring 201 fi 202 fi 203 elif [ -S $_file ]; then 204 SOCKETS=$((SOCKETS+1)) 205 TOTAL_FILES=$((TOTAL_FILES+1)) 206 if [ $f_flag -eq 0 ]; then 207 log_entry $_file socket fullpath 208 else 209 if [ $p_flag -eq 0 ]; then 210 log_entry $_file socket filename_only 211 else 212 pretty_output $_file $_psymbol $_pbinstring 213 fi 214 fi 215 else 216 UNKNOWN=$((UNKNOWN+1)) 217 TOTAL_FILES=$((TOTAL_FILES+1)) 218 if [ $f_flag -eq 0 ]; then 219 log_entry $_file "unknown file type" fullpath 220 else 221 if [ $p_flag -eq 0 ]; then 222 log_entry $_file "unknown file type" filename_only 223 else 224 pretty_output $_file $_psymbol $_pbinstring 225 fi 226 fi 227 fi 228} 229 230# 231# Check for files that are to be excluded from the search process. Passed via -e and/or -x. 232# 233check_excludes() 234{ 235 local _file_to_check 236 237 _file_to_check=$1 238 239 # We need the "standard" IFS to parse the array. Otherwise it won't work... 240 IFS=$SAVEIFS 241 for i in $EXCLUDES ; do 242 if [ "$_file_to_check" = "$i" ] ; then 243 IFS=$(echo -en "\n\b") 244 return 1 245 fi 246 done 247 248 IFS=$(echo -en "\n\b") 249 return 0 250} 251 252# 253# In case we encounter a new directory, this function gets called. 254# It checks whether or nor the new directory has contents and calls 255# readline again. 256# 257process_new_dir() 258{ 259 local _dir _contents _depth _entry _newpath _noes _pcontents _pdir _pnoes _psstring 260 261 # For the pretty_output function, first examine all the parent directories, 262 # to find out what type of entries we have and set binary flags accordingly. 263 _depth=$2 264 if [ $p_flag -eq 1 ] && [ $_depth -ge 1 ]; then 265 _pdir=$1 266 while [ $_depth -ge 0 ]; do 267 cd $_pdir 268 cd .. 269 _pcontents=* 270 _pcontents=($_pcontents) 271 _pnoes=${#_pcontents[@]} 272 _pdir=$(get_filename $_pdir) 273 274 if [ "${_pcontents[$_pnoes-1]}" = "$_pdir" ]; then 275 _psstring="0$_psstring" 276 else 277 _psstring="1$_psstring" 278 fi 279 280 _pdir=$(pwd) 281 _depth=$(($_depth-1)) 282 done 283 fi 284 285 # Now process the directory that actually got passed to the function. 286 _dir=$1 287 _depth=$2 288 cd $_dir 289 _contents=* 290 _noes=($_contents) 291 _noes=${#_noes[@]} 292 293 if [ $_noes -ne 0 ]; then 294 _depth=$(($_depth+1)) 295 296 for _entry in $_contents ; do 297 _newpath="$_dir/$_entry" 298 if [ $_noes -eq 1 ]; then 299 readfile $_newpath 0 $_depth lastentry $_pnoes $_psstring 300 else 301 readfile $_newpath 0 $_depth middleentry $_pnoes $_psstring 302 fi 303 _noes=$(($_noes-1)) 304 done 305 fi 306} 307 308# 309# Log filetype to stdout and to SAVEFILE if desired. This function logs all 310# filetypes except for regular files, which need a little more handling based 311# on OS and accessability. 312# 313log_entry() 314{ 315 local _entry _filetype _logtype 316 317 _filetype=$2 318 _logtype=$3 319 320 if [ "$_logtype" = "fullpath" ]; then 321 _entry=$1 322 elif [ "$_logtype" = "filename_only" ]; then 323 _entry=$(get_filename $1) 324 fi 325 326 echo $_entry - $_filetype 327 if [ $n_flag -eq 0 ]; then 328 echo $_entry - $_filetype >> $SAVEFILE 329 fi 330} 331 332# 333# This function can be considered to be the log_entry function for regular files. 334# 335get_file_stats() 336{ 337 local _file 338 339 _file=$1 340 341 if [ "$2" = "readable" ] && [ "$3" = "fullpath" ]; then 342 echo $_file - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \ 343 ${CHECKSUM}: `${CHECKSUM} $_file | awk '{ print $4 }'` 344 if [ $n_flag -eq 0 ]; then 345 echo $_file - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \ 346 ${CHECKSUM}: `${CHECKSUM} $_file | awk '{ print $4 }'` >> $SAVEFILE 347 fi 348 elif [ "$2" = "readable" ] && [ "$3" = "filename_only" ]; then 349 echo $(get_filename $_file) - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \ 350 ${CHECKSUM}: `${CHECKSUM} $_file | awk '{ print $4 }'` 351 if [ $n_flag -eq 0 ]; then 352 echo $(get_filename $_file) - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \ 353 ${CHECKSUM}: `${CHECKSUM} $_file | awk '{ print $4 }'` >> $SAVEFILE 354 fi 355 elif ["$2" = "unreadable" ] && [ "$3" = "fullpath" ]; then 356 echo $_file - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \ 357 ${CHECKSUM}: not readable 358 if [ $n_flag -eq 0 ]; then 359 echo $_file - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \ 360 ${CHECKSUM}: not readable >> $SAVEFILE 361 fi 362 elif ["$2" = "unreadable" ] && [ "$3" = "filename_only" ]; then 363 echo $(get_filename $_file) - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \ 364 ${CHECKSUM}: not readable 365 if [ $n_flag -eq 0 ]; then 366 echo $(get_filename $_file) - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \ 367 ${CHECKSUM}: not readable >> $SAVEFILE 368 fi 369 fi 370} 371 372# 373# When invoking mapdir with the -f switch, we only log the filename. 374# 375get_filename() 376{ 377 local _filename 378 379 # Use awk to get the last "/" character and extract the part to the right of it. 380 _filename=$(awk -v filename=$1 'BEGIN { 381 n = split(filename, a, "/"); 382 print a[n]; 383 }') 384 385 echo $_filename 386} 387 388# 389# Print the structure of the argument passed to mapdir to stdout as a pretty output tree. 390# Invoked with the -p switch. Needs -f to work. 391# 392pretty_output() 393{ 394 local _entry _space _symboltype _binstring _str _box_rh _box_hl _box_vl _box_mh 395 396 # http://jrgraphix.net/r/Unicode/2500-257F 397 _box_rh=$(echo -e "\u2514") # └ 398 _box_hl=$(echo -e "\u2500") # ─ 399 _box_vl=$(echo -e "\u2502") # │ 400 _box_mh=$(echo -e "\u251C") # ├ 401 402 _entry=$(get_filename $1) 403 _symboltype=$2 404 _binstring=$3 405 _space=" " 406 _str="" 407 408 # Examine the binstring and prepare the middle part of the entry. 409 if [ ! -z $_binstring ]; then 410 for i in $(seq 0 1 $((${#_binstring}-1))) ; do 411 if [ $i -eq 0 ]; then 412 continue 413 elif [ "${_binstring:i:1}" = "0" ]; then 414 _str="$_str " 415 elif [ "${_binstring:i:1}" = "1" ]; then 416 _str="$_str$_box_vl" 417 fi 418 done 419 fi 420 421 # Now prettyprint the entry. 422 if [ $TOTAL_FILES -eq 1 ]; then 423 echo "$_box_hl$_box_hl$_entry" 424 if [ $n_flag -eq 0 ]; then 425 echo "$_box_hl$_box_hl$_entry" >> $SAVEFILE 426 fi 427 elif [ "$_symboltype" = "middleentry" ]; then 428 echo "$_space$_str$_box_mh$_entry" 429 if [ $n_flag -eq 0 ]; then 430 echo "$_space$_str$_box_mh$_entry" >> $SAVEFILE 431 fi 432 elif [ "$_symboltype" = "lastentry" ]; then 433 echo "$_space$_str$_box_rh$_entry" 434 if [ $n_flag -eq 0 ]; then 435 echo "$_space$_str$_box_rh$_entry" >> $SAVEFILE 436 fi 437 fi 438} 439 440check_savefile_existence() 441{ 442 local choice 443 444 if [ -e $1 ] ; then # Confirm 445 echo -n "The savefile: $1 already exists. Do you want to overwrite it Yes/No? " 446 read -t 30 choice # We got 30 seconds to make a choice 447 448 case $choice in 449 450 [Yy][Ee][Ss] | [Yy] ) 451 return 452 ;; 453 [Nn][Oo] | [Nn] ) 454 echo 'Aborted!' 455 exit 1 456 ;; 457 *) 458 echo "No input received. Terminating." 459 exit 1 460 ;; 461 esac 462 fi 463} 464 465usage() 466{ 467 echo "usage: mapdir [-dfhnpt] [-e excludes] [-s savefile] \ 468[-x excludes_file] [file]||[directory]" 469 exit 1 470} 471 472### Point of entry ### 473while getopts ":de:fhnps:tx:" opt ; do 474 case $opt in 475 d) 476 d_flag=1 477 ;; 478 e) 479 e_flag=1 480 e_list=$OPTARG 481 ;; 482 f) 483 f_flag=1 484 ;; 485 h) 486 h_flag=1 487 ;; 488 n) 489 n_flag=1 490 ;; 491 p) 492 p_flag=1 493 ;; 494 s) 495 s_flag=1 496 s_file=$OPTARG 497 ;; 498 t) 499 t_flag=1 500 ;; 501 x) 502 x_flag=1 503 x_file=$OPTARG 504 ;; 505 \?) 506 echo "unkown flag: -$OPTARG." 507 usage 508 exit 509 ;; 510 esac 511done 512 513shift $((OPTIND-1)) 514 515# If -p == 1 => -f == 1 516if [ $p_flag -eq 1 ] && [ $f_flag -eq 0 ] ; then 517 echo "The -p option can only be used with the -f option." 518 exit 519fi 520 521# Allowing (-p && -e) || (-p && -x) would break the formatting of the output tree. 522if ([ $p_flag -eq 1 ] && [ $e_flag -eq 1 ]) || ([ $p_flag -eq 1 ] && [ $x_flag -eq 1 ]) ; then 523 echo "The -p option cannot be used with the -e or -x options." 524 exit 525fi 526 527# Process the other options 528if [ $d_flag -eq 0 ]; then 529 shopt -s dotglob nullglob 530else 531 shopt -s nullglob 532fi 533 534if [ $h_flag -eq 1 ]; then # Change CHECKSUM to sha256 535 CHECKSUM=sha256 536else 537 CHECKSUM=md5 538fi 539 540if [ $e_flag -eq 1 ]; then 541 for i in $e_list ; do 542 EXCLUDES="$i $EXCLUDES" 543 done 544fi 545 546if [ $x_flag -eq 1 ]; then 547 while read line 548 do 549 EXCLUDES="$line $EXCLUDES" 550 done < "$x_file" 551fi 552 553# 554# If an argument is given, take it, otherwise process the current directory. 555# 556if [ $# -eq 1 ]; then 557 READLINK=$(readlink -f $1) 558 if [ ! -e $READLINK ]; then 559 echo "The file: $READLINK doesn\'t exist." 560 usage 561 fi 562 if [ $n_flag -eq 0 ]; then 563 if [ $s_flag -eq 0 ]; then 564 SAVEFILE=~/mapdir$(readlink -f $1 | sed s#/#_#g)_$DATE.txt 565 check_savefile_existence $SAVEFILE 566 : > $SAVEFILE 567 else 568 SAVEFILE=~/${s_file} 569 check_savefile_existence $SAVEFILE 570 : > $SAVEFILE 571 fi 572 fi 573else 574 READLINK=$(readlink -f ./) 575 if [ $n_flag -eq 0 ]; then 576 if [ $s_flag -eq 0 ]; then 577 SAVEFILE=~/mapdir$(pwd | sed s#/#_#g)_$DATE.txt 578 check_savefile_existence $SAVEFILE 579 :> $SAVEFILE 580 else 581 SAVEFILE=~/${s_file} 582 check_savefile_existence $SAVEFILE 583 : > $SAVEFILE 584 fi 585 fi 586fi 587 588# When calling the readline function for the first time, we pass a second argument 589# of "1" to it. This serves the purpose of pleasing the diff utility when invoking 590# mapdir with the -f switch and having a directory as the first argument. If we would 591# map the starting directory to the $SAVEFILE and would later on compare it with 592# another the $SAVEFILE, the diff utility would obviously exit with a return value 593# other than 0, even though the contents of the directories may be truly equivalent. 594# Consider for example the folders: 595# 596# /media/filesystem_a and /media/filesystem_b that both have the same content. 597# 598# The logic is as follows: if the file is a folder, then the readlink function detects 599# this in the "is directory" part and skips mapping its occurence to the $SAVEFILE. 600# For subsequent calls to readlink we will pass a second argument of "0" to the 601# function, which this time maps it. 602 603# Start processing the file/folder... 604readfile $READLINK 1 $DEPTH 605 606# 607# At this point, we are done parsing. Now print statistics if desired as per the -t flag. 608# 609if [ $t_flag -eq 1 ]; then 610 echo 611 if [ $n_flag -eq 0 ]; then 612 echo >> $SAVEFILE 613 fi 614 615 STRLEN="########## Statistics for $READLINK ##########" 616 echo $STRLEN 617 if [ $n_flag -eq 0 ]; then 618 if [ $f_flag -eq 0 ]; then 619 echo $STRLEN >> $SAVEFILE 620 else 621 echo "########## Statistics ##########" >> $SAVEFILE 622 fi 623 fi 624 625 if [ $DIRS -ne 0 ]; then 626 echo Number of directories: $DIRS 627 if [ $n_flag -eq 0 ]; then 628 echo Number of directories: $DIRS >> $SAVEFILE 629 fi 630 fi 631 if [ $UNREADABLE_DIRS -ne 0 ]; then 632 echo Number of unreadable directories: $UNREADABLE_DIRS 633 if [ $n_flag -eq 0 ]; then 634 echo Number of unreadable directories: $UNREADABLE_DIRS >> $SAVEFILE 635 fi 636 fi 637 if [ $FILES -ne 0 ]; then 638 echo Number of regular files: $FILES 639 if [ $n_flag -eq 0 ]; then 640 echo Number of regular files: $FILES >> $SAVEFILE 641 fi 642 fi 643 if [ $UNREADABLE_FILES -ne 0 ]; then 644 echo Number of unreadble files: $UNREADABLE_FILES 645 if [ $n_flag -eq 0 ]; then 646 echo Number of unreadble files: $UNREADABLE_FILES >> $SAVEFILE 647 fi 648 fi 649 if [ $BLOCK_SPECIALS -ne 0 ]; then 650 echo Number of block special files: $BLOCK_SPECIALS 651 if [ $n_flag -eq 0 ]; then 652 echo Number of block special files: $BLOCK_SPECIALS >> $SAVEFILE 653 fi 654 fi 655 if [ $CHARACTER_SPECIALS -ne 0 ]; then 656 echo Number of character speial files: $CHARACTER_SPECIAL 657 if [ $n_flag -eq 0 ]; then 658 echo Number of character speial files: $CHARACTER_SPECIAL >> $SAVEFILE 659 fi 660 fi 661 if [ $PIPES -ne 0 ]; then 662 echo Number of pipes: $PIPE 663 if [ $n_flag -eq 0 ]; then 664 echo Number of pipes: $PIPE >> $SAVEFILE 665 fi 666 fi 667 if [ $SOCKETS -ne 0 ]; then 668 echo Number of sockets: $SOCKET 669 if [ $n_flag -eq 0 ]; then 670 echo Number of sockets: $SOCKET >> $SAVEFILE 671 fi 672 fi 673 if [ $SYMBOLIC_LINKS -ne 0 ]; then 674 echo Number of symbolic links: $SYMBOLIC_LINKS 675 if [ $n_flag -eq 0 ]; then 676 echo Number of symbolic links: $SYMBOLIC_LINKS >> $SAVEFILE 677 fi 678 fi 679 if [ $UNKNOWN -ne 0 ]; then 680 echo Number of symbolic links: $UNKNOWN 681 if [ $n_flag -eq 0 ]; then 682 echo Number of symbolic links: $UNKNOWN >> $SAVEFILE 683 fi 684 fi 685 if [ $TOTAL_FILES -ne 0 ]; then 686 echo Total number of files: $TOTAL_FILES 687 if [ $n_flag -eq 0 ]; then 688 echo Total number of files: $TOTAL_FILES >> $SAVEFILE 689 fi 690 fi 691 692 # Formatted output 693 STRLEN=${#STRLEN} 694 while [ $STRLEN -gt 0 ] 695 do 696 echo -n "#" 697 if [ $n_flag -eq 0 ]; then 698 if [ $f_flag -eq 0 ]; then 699 echo -n "#" >> $SAVEFILE 700 fi 701 fi 702 STRLEN=$((STRLEN-1)) 703 done 704 echo 705 if [ $n_flag -eq 0 ]; then 706 if [ $f_flag -eq 0 ]; then 707 echo >> $SAVEFILE 708 else 709 echo "################################" >> $SAVEFILE 710 fi 711 fi 712fi # t_flag 713 714IFS=$SAVEIFS # reset the old IFS 715exit 0 716