Scripting Language C ——How a text file without a shebang is executed

Is it possible to create a C language source file that can be executed as it is?

There are many definitions of what a scripting language is, but the convenience of being able to "run" the source file is important. For example, suppose you have a C source file like the one below.

$ file hello.c 
hello.c: C source, ASCII text
$ gcc -Wall ./hello.c 
$ ./a.out
Hello, script language C.

Correct code for C that compiles properly without warning. Assuming that this source file is executed with the execution attribute given

$ #Make the source file an executable file like a scripting language
$ chmod +x hello.c
$ ./hello.c
Hello, script language C.

If it is executed without any problem like, C language may be said to be a scripting language. Consider whether you can create such a file.

Proposal 1: Use TCC with shebang

In a scripting language, the first line of a source file specifies the command to execute that source file, such as #! / Bin / sh, #! / Usr / bin / python. It is "shebang". Let's use this in C as well.

The compiler called TCC has an option called -run, which automatically compiles and runs, but you can also specify this on the shebang line.

hello_tcc.c


#!/usr/bin/tcc -run
#include <stdio.h>
int main (int argc, char* argv[]) {
	printf("Hello, script language C.\n");
	return 0;
}

Now you can run the tcc command directly in your environment.

$ chmod +x hello_tcc.c
$ ./hello_tcc.c 
Hello, script language C.

But is this correct for C?

$ gcc -Wall ./hello_tcc.c 
./hello_tcc.c:1:2: error: invalid preprocessing directive #!
 #!/usr/bin/tcc -run
  ^

After all, the shebang line will result in an error. It can only be compiled with TCC.

Proposal 2: Just add the execution attribute

If there is a shebang line, it will not be in C language, so delete it.

hello_x.c(Add execution attribute)


#include <stdio.h>
int main (int argc, char* argv[]) {
	printf("Hello, script language C.\n");
	return 0;
}

It's just a text file anymore, but it's not that it's prepaid.

When run from a shell

$ chmod +x hello_x.c
$ ./hello_x.c
./hello_x.c: 2: ./hello_x.c: Syntax error: "(" unexpected

You're getting a syntax error. Check the behavior of the shell to see what is giving this error.

$ #(Because my environment is Linux`strace`By command)
$ strace -f sh -c './hello_x.c'

Excerpt from strace output


[pid 20349] execve("./hello_x.c", ["./hello_x.c"], [/* 80 vars */]) = -1 ENOEXEC (Exec format error)
[pid 20349] execve("/bin/sh", ["/bin/sh", "./hello_x.c"], [/* 80 vars */]) = 0

I extracted two lines of related parts. [ʻExecve](https://linuxjm.osdn.jp/html/LDP_man-pages/man2/execve.2.html) provides the most basic functions of the Unix exec functions. In Linux etc. this is a system call as it is, so it appears in strace`.

Shell behavior 1: Execute ./hello_x.c → Failure

First, I run ./hello_x.c on ʻexecve` and get an error.

  • ENOEXEC:
    The executable could not be executed due to an incomprehensible format, a different architecture, or some other format error.

Text files without shebang cannot be executed in ʻexecve`.

Shell behavior 2: Run the shell itself

Immediately after failing to execute ./hello_x.c, it is executing itself (sh) with ./hello_x.c as an argument (how to achieve this behavior is slightly different depending on the shell). You may not use ʻexecve` in some situations). This is the behavior specified in the shell specifications.

If the execve() function fails due to an error equivalent to the [ENOEXEC] error, the shell shall execute a command equivalent to having a shell invoked with the command name as its first operand, with any remaining arguments passed to the new shell —— Shell Command Language

Translation: If the ʻexecve () `function fails with an error equivalent to the [ENOEXEC] error, the shell executes the same command that calls the shell with the command name as the first operand, with the remaining arguments. It will be passed to the new shell.

However, since ./hello_x.c is not a shell script, it causes a syntax error, which means that the above error is displayed.

When executed from a program other than the shell

I found that when I run a file without a shebang from a shell, it is run as a shell script by the shell itself. But what if it was run from another program?

$ chmod +x ./hello_x.c
$ env ./hello_x.c 
./hello_x.c: 2: ./hello_x.c: Syntax error: "(" unexpected

The above is an example of the ʻenv` command, but with the same results as running it in a shell. This is because some of the exec functions do the same thing as the shell.

If the header of the file is not recognized as executable (the execve (2) you try to call then fails with the error ENOEXEC), these functions shell the file as the first argument (/ bin / sh )

Programs that look for commands in the environment variable PATH, such as the shell, often use these functions or throw them into the shell from the beginning. Therefore, ** "Files with execution attributes but no shebang are not certain, but are usually passed to the shell and executed as a shell script" ** It looks good to say.

Proposal 3: Source file that is both C language and shell script

In the previous plan, it turned out that C language was executed as a shell script as it was and an error occurred. All you have to do is think of a code that meets the following conditions.

C language preprocessor directives begin with #. We take advantage of this being a comment in shell scripts.

hello_c_and_sh.c


#undef SHELL_SCRIPT_CODE
#ifdef SHELL_SCRIPT_CODE
exec tcc -run "$0" "$@"
#endif

#include <stdio.h>
int main (int argc, char* argv[]) {
	printf("Hello, script language C.\n");
	return 0;
}

When executed as a shell script, ʻexecwill execute TCC, so the subsequent C language code will not be executed. When it is interpreted as C language,SHELL_SCRIPT_CODE is ʻundef, so the shell script part is always skipped. Can be run as a script

$ chmod +x hello_c_and_sh.c
$ ./hello_c_and_sh.c 
Hello, script language C.

It is also a C language source file that can be compiled normally.

$ gcc -Wall ./hello_c_and_sh.c 
$ ./a.out 
Hello, script language C.

I used TCC because it can be written short, but I also tried to support a compiler that seems to be a little more common.

#undef SHELL_SCRIPT_CODE
#ifdef SHELL_SCRIPT_CODE
command -v tcc >/dev/null 2>/dev/null && exec tcc -run "$0" "$@"

tmp_execute="$(mktemp /tmp/tmp_execute.XXXXXX)"
c99 -o "$tmp_execute" "$0"
"$tmp_execute" "$@"
exit_code="$?"
rm "$tmp_execute"
exit "$exit_code"
#endif

#include <stdio.h>
int main (int argc, char* argv[]) {
	printf("Hello, script language C.\n");
	return 0;
}

If there is no TCC, compile, execute, delete the executable file, etc. with c99. If you want to make sure to delete the executable file created in / tmp /, you may need to devise a little more, but for the time being, it will be completed.

License for this article

[Creative Commons License](http://creativecommons.org/licenses/by-sa/4.0/ deed.ja) This article is published under CC BY-SA 4.0 (Creative Commons Attribution 4.0 Inheritance International License).

Recommended Posts

Scripting Language C ——How a text file without a shebang is executed
Use a scripting language for a comfortable C ++ life
Create an executable file in a scripting language
How to display the modification date of a file in C language up to nanoseconds
How to temporarily implement a progress bar in a scripting language
Use a scripting language for a comfortable C ++ life-OpenCV-Port Python to C ++-
Export a gzip-compressed text file
[C language] How to create, avoid, and make a zombie process
I did a lot of research on how Python is executed
How to create a config file
Let's use a scripting language for a comfortable C ++ life 2 Automatically generate C ++ source
Use a scripting language for a comfortable C ++ life 4-Use your own C ++ library from a scripting language-
Use a scripting language for a comfortable C ++ life 3-Leave graphing to matplotlib-
What is a C language library? What is the information that is open to the public?
[C language] My locomotive is too slow ~ I made a sl command ~