python myperl.pl python -e 'print reverse reverse split//,"Just another Perl Hacker"' python mypy.py python -c 'import sys' #### /* python.c - 27/01/2026 by bliako (bliako at cpan org) for https://perlmonks.org/?node_id=11167241 Run perl or python scripts without caring for what is which. It runs the whole command-line first through the Perl interpreter and if it encounters syntax errors or params errors, it runs the whole command-line with all switches passed, through the python interpreter. Unless evnironment variable PERL_PYTHON_EXECUTOR_DEBUG is set, the output from the Perl interpreter will be redirected to ./logger.stderr Run it either with a Perl command-line params and a Perl script, like you would normally do with perl script.pl, e.g. perl -e ... etc. or with python CL params and a python script. If the script does not parse as valid Perl code, it will pass the CLI and the script to a python interpreter. CAVEAT: it will fallback to python if your script is Perl but has syntax errors. Note the distinction between syntax errors and runtime errors. compile it with: gcc python.c -o python `perl -MExtUtils::Embed -e ccopts -e ldopts` Tested only on Linux but it should be portable as it uses only POSIX and perlembed functions. */ #include #include #include #include #include #include #include /* from the Perl distribution */ #include /* from the Perl distribution */ static PerlInterpreter *my_perl; /*** The Perl interpreter ***/ /* You gotta love Perl */ /* You gotta love C too! */ /* these are used for muting and redirecting stderr/stdout to a file and restoring it back */ typedef struct _STD_MUTE_DATA { int old[2]; int new[2]; char *logger_filename[2]; } std_mute_data_t; int std_mute(unsigned char mute, int fd, std_mute_data_t *smd); int main(int argc, char **argv, char **env) { // export PERL_PYTHON_EXECUTOR_DEBUG=1 // or PERL_PYTHON_EXECUTOR_DEBUG=1 python ... // will enable debug mode // debug or not error messages from failed execution // of input script will persist. char *debug = getenv("PERL_PYTHON_EXECUTOR_DEBUG"); /* if( debug && env ){ // this prints the current ENV if PERL_PYTHON_EXECUTOR_DEBUG is set (to something) printf("CURRENT ENVIRPONMENT:\n"); char **p = env; while( *p ){ printf("%s\n", *p); p++; } } */ std_mute_data_t mydata; PERL_SYS_INIT3(&argc,&argv,&env); my_perl = perl_alloc(); perl_construct(my_perl); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; // with last param being NULL we don't pass any env // instead the current env will be used, which means is the same // as passing env from main() if( ! debug ){ // redirect stderr only so we don't see syntax errors // errors go to file 'logger.stderr' mydata.logger_filename[0] = "logger.stdout"; mydata.logger_filename[1] = "logger.stderr"; if( -1 == std_mute(1, 2, &mydata) ){ fprintf(stderr, "%s : std_mute(ON) has failed.\n"); exit(1); } } int ret = perl_parse(my_perl, NULL, argc, argv, (char **)NULL); if( ! debug ){ // unmute stderr if( -1 == std_mute(0, 2, &mydata) ){ fprintf(stderr, "%s : std_mute(ON) has failed.\n"); exit(1); } } if( ret ){ perl_destruct(my_perl); perl_free(my_perl); // add here language detection logic, // various options available open source if( debug != NULL ) fprintf(stderr, "I guess it is &*^#$* pyton /sic/ ...\n"); // this needs #define _GNU_SOURCE and a compiler // which understands it, e.g. gcc: //execvpe("/usr/bin/python", argv, env); // else use this: execv("/usr/bin/python", argv); // this program terminates here because of exec*() // it does not leave this branch and moves below until exit() // btw we have already cleanup perl above } if( debug != NULL ) printf("It parses as Perl so I am running it ...\n"); perl_run(my_perl); perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); exit(EXIT_SUCCESS); } /* Mute or restore stdout (fd=1) or stderr (fd=2). Muting redirects std* to a file named in smd->logger_filename[0] and smd->logger_filename[1] mute will mute if set to 1 and unmute if set to 0. There may be bugs in the following code. Since it uses POSIX functions I assume it runs on OS other than Linux which was tested on. */ int std_mute(unsigned char mute, int fd, std_mute_data_t *smd){ int i = fd - 1; if( (fd < 0) || (fd > 2 ) ){ fprintf(stderr, "std_mute(): fd must be 1 or 2 and not %d.\n", fd); return(-1); } if( mute > 0 ){ if( -1 == (smd->old[i]=dup(fd)) ){ fprintf(stderr, "std_mute(): on dup(fd=%d) : %s\n", fd, strerror(errno)); return(-1); } if( -1 == (smd->new[i]=open(smd->logger_filename[i], O_CREAT|O_TRUNC|O_WRONLY, 0600)) ){ fprintf(stderr, "std_mute(): on open logger, filename '%s': %s\n", smd->logger_filename[i], strerror(errno)); exit(1); } if( close(fd) == -1 ){ fprintf(stderr, "std_mute(): on close(fd=%d) : %s\n", fd, strerror(errno)); return(-1); } dup2(smd->new[i], fd); // now all STD* send to the loggers } else { // reopen stdout if( smd->new[i] < 0 ){ fprintf(stderr, "std_mute(): attempting to run std_mute(OFF) before std_mute(ON), ignoring.\n"); return(-1); } if( dup2(smd->new[i], fd) == -1 ){ fprintf(stderr, "std_mute(): on dup2(fd=%d) : %s\n", fd, strerror(errno)); exit(1); } close(smd->new[i]); smd->new[i] = -1; if( fd == 1 ){ stdout =fdopen(smd->old[i++], "w"); } else if( fd == 2 ){ stderr =fdopen(smd->old[i++], "w"); } } return(0); // success }