Bash script - printing range of lines from text file

I'm working on a new exercise that calls for a script that will take in two arguments on the command line (representing the range of line numbers) and will subsequently print those lines from a a specified file. Command line would look like this: ./lines_script.bash 5 15 <file.txt. The script would then spit out lines 5-15 from the text file (including the line number). The only caveat - I'm not supposed to use sed, awk, grep, head or tail.

Here's what I have so far:

COUNT=($1-1)
while read -r line; do
COUNT=$(( $COUNT +1))
if [$COUNT >= $2]; then
echo $COUNT
fi
done

When I run this on the command line- ./lines_script.bash 5 15 <file.txt it spits this garbage back at me:

./lines_script.bash: line 11: [20: command not found
./lines_script.bash: line 11: [21: command not found
./lines_script.bash: line 11: [22: command not found
./lines_script.bash: line 11: [23: command not found
./lines_script.bash: line 11: [24: command not found
./lines_script.bash: line 11: [25: command not found
./lines_script.bash: line 11: [26: command not found
./lines_script.bash: line 11: [27: command not found
./lines_script.bash: line 11: [28: command not found
./lines_script.bash: line 11: [29: command not found
./lines_script.bash: line 11: [30: command not found

[ and ] must have a spaces before and after them

1 Like

Chubler! Thank you, I got that patched up, though I'm still getting garbage back.

COUNT=($1-1)
while read -r line; do
COUNT=$(( $COUNT +1))
if [ $COUNT >= $2 ]
then
echo "$COUNT"
fi
done

Working fine for me - what garbage do you get? And what would you expect, respectively?

Try this code (note it needs to be "hardened", e.g. checking if $1 and $2 were supplied, is $1 less than $2, etc.):

#!/bin/bash

ARGSTART=$1
ARGSTOP=$2

COUNT=0

while IFS= read -r line; do
 COUNT=$((COUNT+1))

  if [ $COUNT -ge $ARGSTART ] && [ $COUNT -le $ARGSTOP ]; then
   echo "$COUNT: $line"
  fi

done

Demo:

$ man bash | head -15 >bash.txt
$ ./script.sh 5 12 <bash.txt
5: NAME
6:        bash - GNU Bourne-Again SHell
7: 
8: SYNOPSIS
9:        bash [options] [file]
10: 
11: COPYRIGHT
12:        Bash is Copyright (C) 1989-2011 by the Free Software Foundation, Inc.
$ 

---------- Post updated at 06:24 PM ---------- Previous update was at 06:18 PM ----------

Don't know what OP gets, but I got some unary operator expected

1 Like

Thank you, Junior. I'm trying that out now. And yes - I'm getting unary operator expected errors with my script.

---------- Post updated at 11:33 AM ---------- Previous update was at 11:18 AM ----------

That seems to have done the trick, junior-helper! Thank you.

The one thing I don't understand - why did you use "while IFS= read -r line; do" instead of "while read -r line; do"? I'm trying to make sure I understand what I'm doing and why it works. Thanks!

The IFS= sets IFS empty for the following read command, so the read does no word splitting. This ensures that leading space characters are not suppressed.

You're welcome, ksmarine1980.

MadeInGermany has already explained why IFS= was used.

The original description from man bash is as follows:

       IFS    The Internal Field Separator that is  used  for  word  splitting
              after  expansion  and  to  split  lines into words with the read
              builtin  command.   The  default  value  is  ``<space><tab><new
              line>''.

Due to the default value of IFS, lines containing preceding whitespaces won't be printed one-to-one.
One good example are various man pages (Note I used the bash man page in the demo ;)).

If your file has no leading whitespaces, you can remove IFS= from the code.

Note: in addition it is better to use printf rather than echo, because some implementations of echo interpret special character escapes by default, which may play tricks on your output.

So instead of

echo "$COUNT: $line"

it is better to use:

printf "%d: %s\n" "$COUNT" "$line"

--
@ksmarine1980: in your script

COUNT=($1-1)

is wrong. That should be:

COUNT=$(($1-1))