tags:

views:

94

answers:

5

I have a simple bash script, called *print_code.sh*, that takes a file name, and prints out the file with line numbers.

Here it is:

FILE=$1
line=0;
numLines=`wc -l $FILE | sed 's/ .*$//'`
digits=`echo "l($numLines)/l(10)+1" | bc -l`
digits=`echo "0$digits" | sed 's/\..*$//'`
for i in `cat $FILE | sed 's/ /_=SPACE=_/g'`; do
  line=`echo $line + 1 | bc`;
  i=`echo $i | sed 's/_=SPACE=_/ /g'`;
  printf "%${digits}d | %s\n" $line "$i"
done

Here is a silly source file in an imaginary language:

var x = 1
var y = 2
var z = 3
func dostuff {
  var a
  var b
  x = x + 1
  y = y + 1
  z = z + 1
  a = x + y + z
  b = a
}

Here is the output with the line numbers:

01 | var x = 1
02 | var y = 2
03 | var z = 3
04 | func dostuff {
05 |   var a
06 |   var b
07 |   x = x + 1
08 |   y = y + 1
09 |   z = z + 1
10 |   a = x + y + z
11 |   b = a
12 | }

There are (at least) two functional problems with *print_code.sh*:

  • Any blank lines will be ignored - which will break the numbering
  • If the source file contains tabs, the tab character will be treated like a newline character

Also, it is hackish in places, because I don't know bash that well - particularly substituting the space character with a placeholder =SPACE=, so we can iterate over each line using for.

What is the best way this code can be cleaned up such that is not as hackish, and such that the above functional problems go away?

Are there any other functional problems that I have missed?

+8  A: 
cat -n filename
SilentGhost
As always, best to focus on the problem space instead of the solution space, as was done here! :-)
talkaboutquality
Best solution to the specific problem, so I've up-voted. I accepted another answer because it gave insight into problems with the script that will useful in the future.
Joel
A: 

Try this:

egrep -n . /dev/null file | sed -e 's/:/ | /'
Aaron Digulla
+1  A: 

Don't write a script to add line numbers, use cat -n or grep -n as others have suggested.

In general though, if you want to process a file line-by-line in bash, use "while read", eg:

 cnt=0
 cat "$file" | while read line; do
     cnt=$(($cnt+1))
     printf "%02d | %s\n" "$cnt" "$line"
 done

(You don't need to use cat and a pipe here, redirection works fine too)

A different way to work out the number of digits necessary is:

 digits=$(wc -l <"$file" | tr -d '\n' | wc -c)

(wc -l outputs the number of lines and a newline, tr removes the newline, wc -c counts how many characters that needed)

Anthony Towns
You can do `(( cnt++ ))` instead of `cnt=$(($cnt+1))`
Dennis Williamson
+2  A: 

several ways, using tools such as nl, or awk

# nl file

# awk '{print NR,$0}' file

or just with bash

while read line; do echo "$i: $line"; i=$((i+1)); done < file
ghostdog74
Ah, "read line" - That's a whole lot better than using for! Thanks
Joel
A: 

sed = filename.rb | sed 'N;s/\n/ \| /' | sed 's/(^[0-9] )/0\1/'

Jonas Elfström