-1

I'm practically done with my project, all I need now is to write a bash script that should compile and execute the program. My instructions are that we should provide a script that should compile the program and the instructor should be able to run the program by typing in Project1 Value1 Value2. I'm kinda lost on this one, so any help would be appreciated.

I have tried to compile the program by running this script

#!/bin/bash
echo $1
echo $2
g++ -std=c++11 Project2.cpp -lpthread -o project1
./project1 $1 $2

Edit

#!/bin/bash
g++ -std=c++11 Project2.cpp -lpthread -o project1

I thought about it a bit more, and I think my second version is closer to what I would like my script to do. Except I was hoping to be able to execute the program without using ./ if possible.

random_coder
  • 23
  • 1
  • 3
  • What problems are you having exactly? For robustness you might want to fix the things that http://shellcheck.net/ reports. – tripleee Nov 14 '18 at 06:13
  • I think my main problem at the moment is my lack of experience with writing a script to compile a program. Now that I think about it, I think I'm closer to my solution. I was wondering if it would be possible to compile a program and run it without using ./ – random_coder Nov 14 '18 at 06:15
  • Not really no. That would require you to have the current directory in the `PATH`, which is generally considered a very bad idea – tripleee Nov 14 '18 at 06:19
  • For your own convenience you could declare a function like `p1 () { ./Project1 "$@"; }` in your interactive shell and then use the shorthand `p1` instead of typing out the full path. – tripleee Nov 14 '18 at 06:20
  • So my best option would be to use my second version of the script and simply run it by using ./Project1 value1 value2, correct? – random_coder Nov 14 '18 at 06:21
  • The second version doesn't seem to take any parameters. It's still not clear what your actual question here really is. – tripleee Nov 14 '18 at 06:22
  • Possible duplicate of [Why do you need ./ (dot-slash) before executable or script name to run it in bash?](/q/6331075) – tripleee Nov 14 '18 at 06:23
  • All I'm really trying to do is use this script to compile and execute a program. Not sure if it would be possible for the user to enter some values at the time of using the script. So for example, the user could maybe do something like ./compileProject.sh 10 5 – random_coder Nov 14 '18 at 06:42
  • Just FYI, normally you would use the `make` program with a `Makefile` that describes how to compile and link your program and it would then do the minimum necessary to build a new version of your program depending on what you have changed - it is more efficient and more reliable than a script... http://www.cs.colby.edu/maxwell/courses/tutorials/maketutor/ – Mark Setchell Nov 14 '18 at 07:26

2 Answers2

0

I ended up figuring it out after a while.

#!/bin/bash
echo "Enter TP"
read TP
echo "Enter TC"
read TC
g++ -std=c++11 Project_1.cpp -lpthread -o Project_1
./Project_1 $TP $TC
random_coder
  • 23
  • 1
  • 3
  • 1
    A read is fine, but your original thought of using command line arguments was much better. You can **validate** you receive the correct input with `if [ -z "$1" -o -z "$2" ]; then printf "error: insufficient input\nusage: %s filename.[c,ccp]\n" "${0##*/}"; exit 1; fi` If the one argument is not given, the program will then warn and exits displaying the *usage: ...* information. – David C. Rankin Nov 14 '18 at 07:22
  • You still need to quote your arguments. Again, feed your scripts to http://shellcheck.net/ – tripleee Nov 14 '18 at 08:35
  • I appreciate all the feedback and I will definitely work on my script. This was my first time putting together a script so I wasn't sure on the best way to approach the problem. On top of this, I had to submit before midnight, so I put something together that should hopefully work with no problems until I can work on a better solution. – random_coder Nov 14 '18 at 09:10
0

You have struggled for a few hours and put something together, but what happens if the compilation fails? What happens if there are no arguments? What happens if there are 4 arguments for the program?

When writing a script, or a program, the most important thing you can do is validate every necessary step! If you are expecting input, validate you received it. Once you have the input, validate it is what your expected. Is it a value within the usable range? Is it a filename that meets my requirements? Does it exist? Did my compile succeed or fail? Do I try and execute the program?

All of these need to be addressed. Otherwise, your script (or program) will stray off on some undefined path.

Bash provides a wealth of conditional expressions that can be used with test (or [...]) or using the bash [[...]] operator. See Bash Manual - 6.4 Bash Conditional Expressions. Make use of the language features to make your code robust.

Bash provides a wealth of string handling features through parameter expansions Bash Manual - 3.5.3 Shell Parameter Expansion, use them to check the extension to make sure it is c, cpp or c++

Putting those features together with a few if...then...fi statements will make your script much more reliable -- and actually do what it is you are trying to do.

The following short script takes arguments (bash Positional Parameters) requiring that at least the filename to compile be provided, and passing any additional arguments to the compiled program as command line arguments with ${@:2} (all bash positional parameters beginning with the 2nd one)

#!/bin/bash

if [ -z "$1" ]      ## validate at least one argument given
then
    printf "error: insufficient arguments\n" >&2
    printf "usage: %s file.cpp [args]\n" "${0##*/}" >&2
    exit 1
fi

if [ ! -r "$1" ]    ## validate file is readable
then
    printf "error: file not found '%s'\n" "$1" >&2
    exit 1
fi

src="$1"    ## give source file a handy name

## check the source file ends in '.c', '.cpp' or '.c++'
if [ "${src##*.}" != 'c' -a "${src##*.}" != 'cpp' -a "${src##*.}" != 'c++' ]
then
    printf "error: first argument not a c/c++ file\n" >&2
    exit 1
fi

ext="${src##*.}"        ## save the extension

if [ "${#ext}" = 'c' ]  ## check if ext is 'c' use gcc else use g++
then
    ## always enable compiler warnings, -Wall -Wextra -pedantic, minimum
    #  -Wshadow to catch shadowed variables, -Werror treat warnings as error
    gcc -Wall -Wextra -pedantic -Wshadow -Werror \
        -std=c11 -O3 -o "${src%.*}" "$src"
else
    g++ -Wall -Wextra -pedantic -Wshadow -Werror \
        -std=c++11 -O3 -o "${src%.*}" "$src"
fi

if [ $? -eq '0' ]       ## check the compiler return, run only on success
then
    ./"${src%.*}" ${@:2}
else
    printf "\nAn error occurred, executable not called\n\n" >&2
fi

Now a couple of short examples to make sure it works:

#include <stdio.h>

int main (int argc, char **argv) {

    const char *s = "hello c file.";

    printf ("%s\n", s);

    for (int i = 1; i < argc; i++)
        printf ("arg[%d]: %s\n", i, argv[i]);
}

Example Use/Output

$ bash compilewargs.sh cfile.c foo bar baz
hello c file.
arg[1]: foo
arg[2]: bar
arg[3]: baz

For C++

#include <iostream>
#include <string>

int main (int argc, char **argv) {

    std::string s = "hello cpp file.";

    std::cout << s << '\n';

    for (int i = 1; i < argc; i++)
        std::cout << "arg[" << i << "]: " << argv[i] << '\n';
}

Example Use/Output

$ bash compilewargs.sh cppfile.cpp foo bar baz
hello cpp file.
arg[1]: foo
arg[2]: bar
arg[3]: baz

Does the error-handling work?

#include <stdio.h>

int main (void) {

    const char *s = "hello c file.";

    printf ("%s\n", unknown);
}

Example Use/Output

$ bash compilewargs.sh cerrfile.c foo bar baz
cerrfile.c: In function ‘int main()’:
cerrfile.c:7:21: error: ‘unknown’ was not declared in this scope
     printf ("%s\n", unknown);
                     ^
cerrfile.c:5:17: error: unused variable ‘s’ [-Werror=unused-variable]
     const char *s = "hello c file.";
                 ^
cc1plus: all warnings being treated as errors

An error occurred, executable not called

What about a file that doesn't exist?

$ bash compilewargs.sh myfile.c foo bar baz
error: file not found 'myfile.c'

What about a text (or any other non-c, cpp or c++) file?

$ bash compilewargs.sh compilewargs.sh foo bar baz
error: first argument not a c/c++ file

By taking the time to think though what the dumbest user you know could ever try and do with your script -- and protecting against it, you can write reasonably robust scripts that will save you grief. Look things over and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • I appreciate your detailed response. I had to turn in the first half of my project tonight, so I put something together that should hopefully work with no problems. I have very little experience when it comes to writing scripts so I was a little lost, but I plan on reading through all the resources provided to me to come up with a better script when the final project is due. – random_coder Nov 14 '18 at 09:13
  • Just like any language, learn the basics first, and then slowly digest to remaining language features as you go. Be curious. Don't be afraid of `man bash` (it's a bit cryptic at first, but it is all there). Also, bookmark [ShellCheck](https://www.shellcheck.net/), [Bash Guide](http://mywiki.wooledge.org/BashGuide), and [Bash FAQ](http://mywiki.wooledge.org/BashFAQ) and [Bash Pitfalls](http://mywiki.wooledge.org/BashPitfalls). Bash is an incredible shell - if you can think it -- you can probably do it in bash... – David C. Rankin Nov 14 '18 at 09:19
  • Thank you for all the information. I can't believe I have not looked into writing bash scripts sooner. They look like fun, and I hope I can free up some time to practice in my spare time. – random_coder Nov 14 '18 at 09:27