How can I call a C program from a shell script? [duplicate] - c

I have a simple question. I want to execute a C program in a shell script. How do I do that? Thanks for your help in advance.

Assuming this is linux/unix we're talking about:
#!/bin/sh
/path/to/executable arg1 arg2

cc hello_world.c #produces a.out
./a.out #run your program
IMHO, your problem is the $PATH. Your current directory is not in PATH, so when you enter
a.out
your shell respond:
-bash: a.out: command not found
you should execute it as
./a.out
(or add "." to your PATH, but this is not recommended.)

Almost every program that you execute in a shell script is a C program (but some, often many, of the commands you execute may be built into the shell). You execute a C program in the same way as any other program:
By basename: command [arg1 ...]
The command must be in a directory searched by the shell - on your PATH, in other words.
By relative name: ./command [arg1 ...] or ../../bin/command [arg1 ...]
The program must exist and be executable (by you)
By absolute name: /some/directory/bin/command [arg1 ...]
The program must exist and be executable (by you)
One of the beauties of Unix is that programs you create, whether in C or any other language, attain the same status as the system-provided commands. The only difference is that the system-provided commands are in a different place (such as /bin or /usr/bin) from commands you create (such as usr/local/bin or $HOME/bin).

Related

how does shell run sh scripts?

We know that each time a user runs a program by typing the name of an executable object file to the shell, the shell creates(use fork) a new process and then loads(use execve) and runs the executable object file in the context of this new process.
Below is my understanding of how a shell works internally, please correct me if I was wrong:
Commands such as ls, cat etc are executable objects (source file written by C) are in /bin/ directory. for example, when a user type in bash shell ls to list files and directories, the bash shell inteprets ls command and fork a child process to run ls
Q1-Is my understanding correct?
Q2-if my understanding is correct, then when a shell run .sh script file which is:
#!/bin/sh
echo "what is your name?"
read name
so the shell forks two child processes for echo and read, then how does these two processes communicate with each other? I mean how does the return output of echo process get passed to read process?
Is my understanding correct?
Generally, yes. But the executable file not necessarily is in /bin/. A file named ls is searched in paths specified inside PATH environment variable and the match is used. The file can be in /usr/bin /usr/sbin /usr/local/bin etc.
And ls may be a builtin. Or a function. Or an alias.
so the shell forks two child processes for echo and read
And this is where a "built-in" comes in. A built-in is an internal part of the shell handled internally by the shell. There is no fork, just some internal code is run and that way it can modify the environment variables. echo not necessarily is a builtin, it only outputs data. But read has to be handled specially and most probably is a builtin for it to modify name variable (there is no requirement for read to be builtin, it may not be, but usually shell writers solve this problem by just making read a builtin).
On bash you can check the type of command with type. Ex. type echo shows echo is a shell builtin.
I mean how does the return output of echo process get passed to read process?
It doesn't.
You may want to read posix Command Search and Execution.

How to tell if a shell command is written in Bash or C?

I'm using macOS and I noticed (via a separate article) that the cat command is written in C. But I'm sure I've read elsewhere that some shell commands (builtins?) are written in Bash.
How can you tell the difference?
UPDATE: seems I was misinformed and that no builtin commands are written in bash. What I must have read was something related to an external executable.
Use the 'file' command to determine the type of file.
Built-ins are not written in bash. The are intrinsically part of the command interpreter (which is often bash). Example: 'cd'. The 'file' command will not be able to find a built-in and will give an error.
the difference between a bash builtin and an executable is that when calling from a bash process a builtin is a function call whereas an external command forks a new process (if not in background waits for termination).
note the overhead of calling a new process
for((i=0;i<1000;i++)); do /bin/echo -n ; done
to know if a command is a builtin or an executable you can use type
type cat
type -a echo
to explicitly call echo builtin
builtin echo
to explicitly call echo command
command echo
note commands that changes process environment like cd can't be an executable because calling a sub process can't change caller's environment.

Need to get C program name inside shell script

I have an occasion where a C program invokes a shell script, which in-turn does some copying stuff from the CD mount location to an installation directory.
Now my question is that, is there a straightforward approach to get the absolute path of this C program inside this shell script ?.
I tried a couple of approaches that includes using "$(ps -o comm= $PPID)" from within the script, but nothing did work out till now. I know that I can create a temporary file from the C program which contains its own name (argv[0]) and then make the shell script to read that file, but I don't want to follow that approach here.
Of course, it can be passed as an argument to the script, but I was thinking why the bash built-in macros or something cannot be used here
On linux there is a /proc/self/exe path that points the absolute path of the current executed file. So you can push an environment variable that contains the path before spawning the shell. Something like:
readlink("/proc/self/exe",...,buf);
putenv("MYEXE",buf);
system("thescript");
and accessing the variable in the script:
echo $MYEXE
Before running a foo command you could use which like
fooprog=$(which foo)
to get the full path of the program (scanning your $PATH). For example which ls could give /bin/ls ....
On Linux specifically you could use proc(5).
In your shell process (running bash or some POSIX compliant shell) started by your C program, $PPID give the parent process id, hopefully the pid of the process running your C program.
Then the executable is /proc/$PPID/exe which is a symbolic link. Try for example the ls -l /proc/$PPID/exe command in some terminal.
(notice that you don't run C source files or stricto sensu C programs, you often run some ELF executable which was built by compiling C code)
You might have weird cases (you'll often ignore them, but you might decide to handle them). Someone might move or replace or remove your executable while it is running. Or the parent process (your executable) died prematurely, so the shell process becomes orphan. Or the executable removed itself.

Cannot execute binary file when calling Java from Bash

I'm using Ubuntu 14.04.
There are 4 files involved: 'compile.sh', 'execute.sh', 'work.c', 'tester.sh'.
In 'compile.sh', it compiles the 'work.c' file and outputs an executable file called 'execute.sh'. In my own testing process, I do ./compile.sh, then ./execute.sh to run my C program. This works.
Now, the 'tester.sh' is a script that calls a Java program and this Java program does the same thing. It will run my 'compile.sh' first and then excute 'execute.sh'. It checks the correctness of my program outputs.
The problem is that when I do ./tester.sh, I get the error below
Reading first line from program...
./execute.sh: ./execute.sh: cannot execute binary file
First line of execution should match: Created \d heaps of sizes .+
Failed to execute (error executing ./execute.sh)
You can ignore the third line "First line of execution...."; it tries to check whether my output matches exactly with the tester. Since the binary file cannot be executed, then the first line does not match for sure.
So why does it say "cannot execute binary file"?
Content in compile.sh
#!/bin/bash
gcc -Wall work.c -o execute.sh
Content in tester.sh
#!/bin/bash
java -cp bin/tester.jar edu.ssu.cs153.work1.Tester
(bin/tester.jar is in my local machine; we can assume there is nothing wrong with the tester script.)
Diagnosis
It is weird, but not disallowed, to name an executable with the .sh extension. Your problem is that the Java code is trying to run it as a shell script (e.g. bash ./execute.sh), and it isn't a shell script so it fails. You need to change the Java to run the .sh file as an executable instead of as a shell script. Or, better (since you probably can't fix the Java), fix the compilation so that it produces an executable with a different name (e.g. work), and have execute.sh execute ./work.
File execute.sh is just an output file from compiling the work.c file. It is just like a.out by default from gcc. I can run ./execute.sh from the terminal and see all the correct outputs.
The trouble is, when you run it, you do ./execute.sh and the shell executes directly. The Java is running it as bash ./execute.sh, and that generates the error. Try it at the command line.
Prescription
On the face of it, you need to change compile.sh, perhaps like this (generating a program work from work.c):
#!/bin/bash
gcc -o work -Wall work.c
And you write a shell script called executable.sh that reads:
#!/bin/bash
exec ./work "$#"
This script runs your program with any command line arguments it is given. The exec means the shell replaces itself with your program; there are minor advantages to doing it that way, though it'll be OK if you omit the exec from the script.

can't execute bash command via c standard system function

the code snippet I wrote is like this:
#include <stdlib.h>
int main()
{
system("/bin/bash ls");
}
when I compile and execute the binary, I got the result:
/bin/ls: /bin/ls: cannot execute binary file
so what's the thing missing here?
ls is an actual system binary. it's not a built-in shell command. All you need is system("ls"). Right now you're trying to pass the contents of the ls binary file into bash as a script.
Do not use system() from a program , because strange values for some environment variables might be used to subvert system integrity. Use the exec(3) family of functions instead, but not execlp(3) or execvp(3). system() will not, in fact, work properly from programs with set-user-ID or set-group-ID privileges on systems on which /bin/sh is bash version 2, since bash 2 drops privileges on startup. (Debian uses a modified bash which does not do this when invoked as sh.)
In your case , ls is not built in command in shell so system() is not working.
You can check using type <cmd_name> command to know that cmd_name is built-in or not.
For more man system()
If no options are specified, the argument to /bin/bash is the name of a file containing shell commands to execute.
To execute commands specified on the command line, use the -c option: /bin/bash -c ls.
As others have noted, there are security considerations when doing this, so you should seek alternatives.

Resources