So, can C be used as a scripting language?
The short answer is "no".
The longer answer is "not really, but..."
Contents
What is a scripting language?
According to Wikipedia, a scripting language is a language used to customise or automate the utilities of the existing system. It is usually interpreted at runtime, rather than compiled, and it usually can interact with other programs.
Some widely used scripting languages are Bash, Perl, Python, and even JavaScript (though JavaScript is mostly limited to running within a browser).
Bash in particular is ubiquitous in Linux systems and the default command language for many Linux distributions. It interacts with standard Linux tools to perform a variety of tasks quickly.
It is enough to write in the terminal echo "Hello world"
to have the string “Hello world” returned, no intermediate steps required.
Perl can also be used to print strings in the terminal with the -e
command line option: perl -e 'print("Hello world\n")'
will print "Hello world". In Python, the equivalent command line option would be -c
, as in: python -c 'print("Hello world")'
However, Perl and Python scripts are not usually called with the program passed as a string. They usually live in executable source code files that begin with a shebang: #!/usr/bin/env python
for Python, or #!/usr/bin/env perl
for Perl. The script can be executed either by calling the interpreter to read it (for example, python myscript.py
), or by directly executing the source code file (./myscript.pl
). In the latter case, the kernel will spawn a new process with the interpreter referenced by the shebang, and the file content will be passed to standard input.
A shebang for C
Can we use a shebang for a C source code file? The answer to this is a "yes, but...".
A C source file must be compiled to machine code in order to run it. However, the Tiny C compiler, also known as tcc, can be invoked from scripts. If we include a shebang of the form #!/usr/bin/tcc -run
in our source code and execute the file without compiling it first, the tcc compiler will compile the source on the fly and directly execute the program:
#!/usr/bin/tcc -run
#include <stdio.h>
int main(void) {
printf("Hello world\n");
return 0;
}
If we make the file executable and run it, it will print "Hello world" in the terminal without producing an executable file: tcc
with the -run
option compiles to memory and immediately executes the instruction. It's worth noting here that if we try to compile the file with another compiler, such as gcc
, the preprocessor will complain for the shebang.
tcc
with the -run
option can be used without even a source file:
echo 'main(){printf("Hello world\n");}' | tcc -run -
# outputs a warning for not including the
#stdio library and then prints
# Hello world
The C interpreter
There are quite a few C interpreters, as I found out in these StackOverflow and StackExchange questions: Is there an interpreter for C? and Is there a scripting language with C-like syntax?.
The most comprehensive one appears to be Cling, a part of CERN's root project. As far as I can tell, they used to use CINT, also available as a standalone package, and they switched at some point in 2013, and the original developer stopped updating it soon after.
The one I actually tried though is bic.
bic
Bic is a C interpreter and API explorer, according to its author, using a read-eval-print loop.
The installation is fairly straight-forward, clone the repo and then:
autoreconf --force --install
./configure --enable-debug --prefix=/home/USER/mylocaldir
make
make install
Or see if it offers binaries for your distro.
Then, time for some interactive fun:
./bin/bic
BIC> #include <stdio.h>
BIC> char a;
a
BIC> scanf("%c", &a);
H
1
BIC> printf("%X\n", a);
48
3
BIC>
In the above example we run, line by line the following program, that takes a char as an input and returns the hexadecimal representation of this char:
#include <stdio.h>
int main(void) {
char a;
scanf("%c", &a);
printf("%X\n", a);
return 0;
}
In the bic
prompt, there is no need for a main function; actually if we try to type int main(void) {
and press <ENTER>
it will throw an error. In that sense, it doesn't work like the familiar Python interpreter, or even the Bash shell, which allow us to write functions line by line.