4

I have a makefile which I use to run simple bash scripts inside of a repo I am working on. I am attempting to make a new make call which will log me into my mySQL database automatically. I have my username and password stored in a .zinfo file at a known location (such as "/u/r/z/myid/.zinfo"). I am attempting to read the lines of this file to get my password, which is of the format:

user = csj483
database = cs495z_csj483
password = fjlID9dD923

Here is the code I am trying to run from the makefile, but it is not working. If I run the same code directly in the shell, it seems to work ok. Note that I had to use double $$s because make doesn't like single ones.

login:
    for line in $$(cat /u/z/users/cs327e/$$USER/.zinfo); \
    do \
        PASSWORD=$$line; \
        echo $$PASSWORD; \
    done 
    echo $$PASSWORD

At this point, I am just trying to get the password, which should be the last value that PASSWORD is set to in the loop. If anyone can suggest an easier way to retrieve the password, or point out my error in my code, I would appreciate it. Eventually I will also want to retrieve the database name as well, and any help with that too would be appreciated as well. I am new to bash, but experienced in numerous other languages.

1 Answer 1

14

You didn't specify what you meant by "not working"; when asking questions please always be very clear about the command you typed, the result you got (cut and paste is best), and why that's not what you expected.

Anyway, most likely the behavior you're seeing is that the first echo shows the output you expect, but the second doesn't. That's because make will invoke each logical line in the recipe in a separate shell. So, the for loop is run in one shell and it sets the environment variable PASSWORD, then that shell exits and the last echo is run in a new shell... where PASSWORD is no longer set.

You need to put the entirety of the command line in a single logical line in the recipe:

login:
         for line in $$(cat /u/z/users/cs327e/$$USER/.zinfo); do \
             PASSWORD=$$line; \
             echo $$PASSWORD; \
         done \
         && echo $$PASSWORD

One last thing to remember: you say you're running bash scripts, but make does not run bash. It runs /bin/sh, regardless of what shell you personally use (imagine the havoc if makefiles used whatever shell the user happened to be using!). Your best option is to write recipes in portable shell syntax. If you really can't do that, be sure to set SHELL := /bin/bash in your Makefile to force make to use bash.

ETA:

Regarding your larger question, you have a lot of options. If you have control over the format of the zinfo file at all, then I urge you to define it to use the same syntax as the shell for defining variables. In the example above if you removed the whitespace around the = sign, like this:

user=csj483
database=cs495z_csj483
password=fjlID9dD923

Then you have a valid shell script with variable assignments. Now you can source this script in your makefile and your life is VERY easy:

login:
        . /u/z/users/cs327e/$$USER/.zinfo \
            && echo user is $$user \
            && echo database is $$database \
            && echo password is $$password

If you don't have control over the syntax of the zinfo file then life is harder. You could use eval, something like this:

login:
        while read var eq value; do \
            eval $$var="$$value"; \
        done < /u/z/users/cs327e/$$USER/.zinfo \
            && echo user is $$user \
            && echo database is $$database \
            && echo password is $$password

This version will only work if there ARE spaces around the "=". If you want to support both that's do-able as well.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks so much! Like I said, I am pretty unfamiliar with bash (or anything in the shell really) and make; I am primarily a python programmer.Unfortunately, I don't have control over the .zinfo file; it is maintained by the professor of my databases class.
I got the login to work properly using the first suggestion of simply adding the && to my last line, but I tried the last block and upon running, it simply says "nothing to be done for "login". I added a statement at the end that is a call to mysql just like I did for the first example. Any idea what's going on there?
It's hard to follow your description: if you updated your question with the actual makefile rule it would be simpler. However, a message of the form nothing to be done for XXX means that make thinks that the target XXX is up to date. Since your login target has no prerequisites, it will be considered to be up to date if that target exists. So, I would say you have a file in your directory named login. Either delete that file or, better, declare the login target phony by adding .PHONY: login to your makefile.
What's so good with having the script in POSIX shell? There's bash on most systems at this age, and using bash features could make code clearer.
"Most systems" is relative. It's often not available on any of the *BSD systems, not on Solaris, not on other non-GNU/Linux systems. It's often not available on smaller systems (although admittedly most won't run make). Also, what version of bash? Not all versions support all features, and older but still supported GNU/Linux systems come with older versions of bash. It's simpler to just use POSIX sh. If you're writing a makefile recipe complicated enough that could really take advantage of bash features, you should probably reconsider your implementation anyway.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.