I want to create a for loop that runs from 1 to a number stored in a variable. But it produces this error:

Syntax error: Bad for loop variable

This is my script:


count_lines () 
  local f=$1  
  l=`wc -l $f | sed 's/^\([0-9]*\).*$/\1/'`
echo "$0 counts the lines of code" 
while [ "$*" != ""  ]
        count_lines $1
        echo "$1: $l"
for(( i= 0 ; i <= ${l} ; i++))
   title=$(grep -oPm1 "(?<=<title>)[^<]+" <<< "$0")
   echo "$title"
  • Small trick: wc -l <filename (note the left-angle) outputs just the count and you don't need to fiddle with sed or even something cheaper like ${var% *}. That said, if you really want to look for XML construct(s?) in the file(s?) they are unlikely to be related to the number of lines; one of the main features of XML is that it is stream-oriented, not line-oriented. Commented Dec 6, 2015 at 2:20
  • for (( i= 0 ; i <= ${l} ; i++)) space missing here. And in (( )) use i <= l -- ${} is unnecessary and a missing l actually messes up with the syntax. Commented Dec 9, 2015 at 3:18

2 Answers 2


Make sure that the script is executable, and run the script by just typing the command, including its path. For example, if the script is called foo and is in the current directory, run


Given the error message, you are doing something like sh foo. This runs the script under sh, not bash. sh on your machine is a different shell, probably dash, which doesn't support the for loop syntax you used. By running ./foo, your script will be executed by the shell mentioned in the first line, which is bash.

Your script is weird in several places:

  • Always put double quotes around variable substitutions: "$1", "$f", etc.
  • That while [ "$*" != "" ] … loop is a very roundabout way of iterating over the arguments of the script. The simple, idiomatic way is

    for x; do
      count_lines "$x"


    for x in "$@"; do
      count_lines "$x"
  • I'm not sure what you're trying to do with title=$(grep -oPm1 "(?<=<title>)[^<]+" <<< "$0"); "$0" is the path to the script, so you're searching the regexp (?<=<title>)[^<]+ in the path to the script, which doesn't make much sense.
  • I'm trying to extract a value from an xml document that has been read into my script as a variable with title=$(grep -oPm1 "(?<=<title>)[^<]+" <<< "$0")
    – Yuces
    Commented Dec 5, 2015 at 23:21
  • and I wish to extract the all values between titles with a loop
    – Yuces
    Commented Dec 5, 2015 at 23:22
  • @engineer Fix the parts you understand, then post a new question with your updated script, some sample input, and the desired output. Commented Dec 5, 2015 at 23:30
  • I test my machine's shell with ps -p $$ and it is bash @Gilles
    – Yuces
    Commented Dec 5, 2015 at 23:30
  • @engineer That shows you which shell you're running interactively. But given the error message you got, you are not running the script with bash. Once again, run /path/to/script instead of sh /path/to/script or whatever it is you're doing. Commented Dec 5, 2015 at 23:33

The main problem I believe is with not testing if the file actually exists and getting a count l that is invalid for the for loop.

Take a look at this (very modified) similar script to give you some ideas:


count_lines() {
if [[ -r $1 ]]; then     # if the file exist.
  l="$(wc -l "$1")"      # count the number of lines.
  l="${l%%[^0-9]*}"      # remove anything that is not a digit from the end.
echo "$0 counts the lines of code"

for file; do               # while there is an argument.
  count_lines "$file"    # execute the function above
  echo "$file: $l"       # print the number of lines.

echo "$(grep -oPm1 "(?<=<title>)[^<]+" "$0")"

I can not understand why you try to run l times a command (grep) to find title inside a file. One time is enough.

Also, the grep command is reading the script (not an external file).

As Gilles said: heavily correct your code, post a new question.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .