Image of the glider from the Game of Life by John Conway
Skip to content

Bash Loops and File Handling

So, working on one of the many Bash scripts at work, I needed one that handled file manipulation. Not only did it need to manipulate a file, I needed it to handle the manipulation in a loop, as the lines are all the same except for a number (like a primary key) on each line.

Initially, I tried binding vim and emacs keyboard commands to edit the file in the Bash script, then I came to the realization that it would be easier to delete the already existing file, and create a new one from scratch. Here is an example of the file that I would like to create:

marketloc=51.3
maxmarkets=99
market 001,1,7
market 002,2,7
market 003,3,7
market 004,4,7
market 005,5,7
market 006,6,7
market 007,7,7
market 008,8,7
market 009,9,7
market 010,10,7
go
p9999
sample1.lod
quit
quit

The "markets" could go anywhere from 1 to 99 in this case, or whatever "maxmarkets" is set to (above 100, if needed). The first couple lines of the files I need to put in the file before the loop, and the last five lines after the loop. Everything else in the file should be handled in the loop.

First, the scripts needs to be dynamic. From project to project, the number of markets could change, so I need to get this number from the user. Also, the project number, denoted by "p9999", will need to be from the user as well. Both of these arguments I can pass through the command line. We'll call the script "newsamp.sh". Here would be the code, with testing the number of command line arguments (both are required):

1
2
3
4
5
6
7
8
#!/usr/bin/bash
if [[ $# < 2 ]]; then  # check the number of command line arguments
    echo "ERROR: Please supply project number and number of markets."
    echo "Required usage: newsamp.sh 0536 33"
else
    project="$1"  # first command line argument
    mkt_numb="$2"  # second command line argument
fi

Fairly straight forward. Nothing fancy here. We're just making sure that the first argument is the project number, and the second argument is the number of markets. We can do a test to make sure that the first first argument is a valid project number, and the second is a valid range for a market number, but that goes beyond the point of this post.

Now that we have our assignments, lets create the file, and generate the first couple of lines in the file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/bin/bash
# check the number of command line arguments
if [[ $# < 2 ]]; then
    echo "ERROR: Please supply project number and number of markets."
    echo "Required usage: newsamp.sh 0536 33"
else
    project="$1"  # first command line argument
    mkt_numb="$2"  # second command line argument
    # test for the existence of the file
    if [[ -f market.spx ]]; then  
        rm -f market.spx  # if the file exists, remove it
    fi
    # create the text file
    touch market.spx
    # add the first two lines to the file
    echo marketloc=51.3 >> market.spx  
    echo maxmarkets=99 >> market.spx
fi

Notice I am using 'echo' to add the text to the file. I could use 'cat' or 'sed' or a number of other text options. Here, 'echo' will fulfill our purposes. Notice also that I am doing some I/O redirection using '>>'. I want to append the text to the last line of the file, rather than overwrite the file itself with every 'echo' instance. Now the loop:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#!/usr/bin/bash
# check the number of command line arguments
if [[ $# < 2 ]]; then
    echo "ERROR: Please supply project number and number of markets."
    echo "Required usage: newsamp.sh 0536 33"
else
    project="$1"  # first command line argument
    mkt_numb="$2"  # second command line argument
    # test for the existence of the file
    if [[ -f market.spx ]]; then  
        rm -f market.spx  # if the file exists, remove it
    fi
    # create the text file
    touch market.spx
    # add the first two lines to the file
    echo marketloc=51.3 >> market.spx  
    echo maxmarkets=99 >> market.spx
    # create a counter keeping track of the loop iteration
    counter=1
    # start the loop
    while [[ $counter -le $mkt_numb ]]; do
        if [[ $counter -lt 10 ]]; then
            echo market 00${counter},${counter},7 >> market.spx
        elif [[ $counter -lt 100 ]]; then
            echo market 0${counter},${counter},7 >> market.spx
        else
            echo market ${counter},${counter},7 >> market.spx
        fi
        # increment the counter by one at every pass
        let counter=counter+1
    done
fi

There is a couple of spots where we need to do some checking. The first is making sure that the counter is not more than the variable "mkt_numb". If it is, we don't need to do any looping and appending to the file. The second check is to make sure that after the word "market" in our text file, our number is precisely 3 digits, no more and no less. Again, we could do further checking that the number of markets the user enters at the command line is not larger than 999, but that goes beyond the point of this post. So, if the counter is between 1 and 9, then we want to place two zeros before the market number. If it is between 10 and 99, the one zero will suffice, otherwise, print the number. Of course, at every pass, we are using 'echo' to append the text to the file, and we are incrementing our counter by one. The only thing left is to append the last 5 lines to the text file. We want to do this after we have finished with the loop.

Here would be the completed bash script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#!/usr/bin/bash
# check the number of command line arguments
if [[ $# < 2 ]]; then
    echo "ERROR: Please supply project number and number of markets."
    echo "Required usage: newsamp.sh 0536 33"
else
    project="$1"  # first command line argument
    mkt_numb="$2"  # second command line argument
    # test for the existence of the file
    if [[ -f market.spx ]]; then  
        rm -f market.spx  # if the file exists, remove it
    fi
    # create the text file
    touch market.spx
    # add the first two lines to the file
    echo marketloc=51.3 >> market.spx  
    echo maxmarkets=99 >> market.spx
    # create a counter keeping track of the loop iteration
    counter=1
    # start the loop
    while [[ $counter -le $mkt_numb ]]; do
        if [[ $counter -lt 10 ]]; then
            echo market 00${counter},${counter},7 >> market.spx
        elif [[ $counter -lt 100 ]]; then
            echo market 0${counter},${counter},7 >> market.spx
        else
            echo market ${counter},${counter},7 >> market.spx
        fi
        # increment the counter by one at every pass
        let counter=counter+1
    done
    # Append the last 5 lines to the text file
    echo go >> market.spx
    echo p${project} >> market.spx
    echo sample1.lod
    quit
    quit
fi

That's it! Really pretty easy when you think about it, and accomplished with only a little amount of code. Make newsamp.sh executable, and run the script.

Any questions? 🙂

Post a Comment

Your email is never published nor shared.