Question

Hi I am trying to print/echo line numbers that are multiple of 5. I am doing this in shell script. I am getting errors and unable to proceed. below is the script

#!/bin/bash
x=0
y=$wc -l $1
while [ $x -le $y ]
do
  sed -n `$x`p $1
  x=$(( $x + 5 ))
done

When executing above script i get below errors

#./echo5.sh sample.h
./echo5.sh: line 3: -l: command not found
./echo5.sh: line 4: [: 0: unary operator expected

Please help me with this issue.

Was it helpful?

Solution 4

There are cleaner ways to do it, but what you're looking for is this.

#!/bin/bash
x=5
y=`wc -l $1`
y=`echo $y | cut -f1 -d\ `
while [ "$y" -gt "$x" ] 
do
  sed -n "${x}p" "$1"
  x=$(( $x + 5 ))
done

Initialize x to 5, since there is no "line zero" in your file $1.

Also, wc -l $1 will display the number of line counts, followed by the name of the file. Use cut to strip the file name out and keep just the first word.

In conditionals, a value of zero can be interpreted as "true" in Bash.

You should not have space between your $x and your p in your sed command. You can put them right next to each other using curly braces.

OTHER TIPS

For efficiency, you don't want to be invoking sed multiple times on your file just to select a particular line. You want to read through the file once, filtering out the lines you don't want.

#!/bin/bash
i=0
while IFS= read -r line; do
    (( ++i % 5 == 0 )) && echo "$line"
done < "$1"

Demo:

$ i=0; while read line; do (( ++i % 5 == 0 )) && echo "$line"; done < <(seq 42)
5
10
15
20
25
30
35
40

A funny pure Bash possibility:

#!/bin/bash

mapfile ary < "$1"
printf "%.0s%.0s%.0s%.0s%s" "${ary[@]}"

This slurps the file into an array ary, which each line of the file in a field of the array. Then printf takes care of printing one every 5 lines: %.0s takes a field, but does nothing, and %s prints the field. Since mapfile is used without the -t option, the newlines are included in the array. Of course this really slurps the file into memory, so it might not be good for huge files. For large files you can use a callback with mapfile:

#!/bin/bash

callback() {
    printf '%s' "$2"
    ary=()
}
mapfile -c 5 -C callback ary < "$1"

We're removing all the elements of the array during the callback, so that the array doesn't grow too large, and the printing is done on the fly, as the file is read.


Another funny possibility, in the spirit of glenn jackmann's solution, yet without a counter (and still pure Bash):

#!/bin/bash

while read && read && read && read && IFS= read -r line; do
    printf '%s\n' "$line"
done < "$1"

Use sed.

sed -n '0~5p' $1

This prints every fifth line in the file starting from 0

Also

y=$wc -l $1

wont work

y=$(wc -l < $1)

You need to create a subshell as bash will see the spaces as the end of the assignment, also if you just want the number its best to redirect the file into wc.

Dont know what you were trying to do with this ?

x=$(( $x + 5 ))

Guessing you were trying to use let, so id suggest looking up the syntax for that command. It would look more like

(( x = x + 5 ))

Hope this helps

You can do this quite succinctly using awk:

awk 'NR % 5 == 0' "$1"

NR is the record number (line number in this case). Whenever it is a multiple of 5, the expression is true, so the line is printed.

You might also like the even shorter but slightly less readable:

awk '!(NR%5)' "$1"

which does the same thing.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top