Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

#module request for those typing python myprogram.pl use DidYouMeanPython; # new error message "did you mean python?"

Replies are listed 'Best First'.
Re: did you mean python
by Corion (Patriarch) on Jan 27, 2026 at 08:40 UTC

    ... as the code is run through the python3 interpreter, you will need to write a polyglot program that runs under Python as well as under Perl. Under Python, the program should compile (as not to raise a meaningless syntax error) but then output an error message, while under Perl, the program should run as-is.

    Ideally the Python implementation would restart the program under Perl.

    I can easily see a Python module import working, but I'm unsure about the compile-time / run-time semantics and implementation of Python, and whether the top unit must compile completely before imported code is run. The following code could work under Perl and Python:

    import RunPerlCodeFromPython

    This would be interpreted as valid Python code, importing the RunPerlCodeFromPython module.

    Under Perl, this would be interpreted as RunPerlCodeFromPython->import().

    The problem is that Perl and Python syntax are largely incompatible and you need to find a good escape hatch to make the Python interpreter ignore all lines following this import. Maybe a cleverly crafted here-document can help there.

      you will need to write a polyglot program that runs under Python as well as under Perl

      I would love to see something like that!!! That would be interesting! But how would one even begin something like that? I mean the first line in a perl program "#!/usr/bin/perl" prevents it from being interpreted as a python program. No?

        I mean the first line in a perl program "#!/usr/bin/perl" prevents it from being interpreted as a python program.

        See perlrun: The shebang line does not have to be the first line in the file. You can make the perl interpreter skip a lot of lines in the file until it finds a line starting with "#!" and containing "perl".

        And the shebang line is not required at all. You can start your perl script right in the first line of a file, without a shebang line. Many operating systems don't use the shebang line at all, in fact, it is specific to the Unix family of operating systems. Other systems use file name extensions, file name prefixes, or some other mechanisms. Most other scripting languages, including Python, also do not need the shebang line.

        So, you can get a very simple polyglot program just like this:

        print("Hello World")

        Not very impressive, but it is both a valid Perl script and a valid Python script. The syntax is similar enough for this to work.

        Of course, things get more complicated if you want more than just "Hello World". That's where you need to be creative. Find a way for the two (three, four) languages to make them interpret the same text in a different way. Like this:

        @rem =(' @echo off echo Hello World from a plain old DOS batch file goto end '); print "Hello World from perl\n"; __END__ :end

        This is both a DOS batch file and a Perl script. DOS batches don't print lines starting with @ and ignore everything following rem. They also do not interpret lines that aren't executed. So for DOS, this is a non-printed comment (@rem), a non-printed command to disable printing (@echo off), a print statement (echo), a jump to a label at the end of the file, three lines that are not executed, and a jump label (:end). For Perl, this is an assignment of a multi-line string to the array @rem, followed by a print, followed by an end-of-script marker, followed by data which is not processed by this script. The assignment to @rem just wastes a little bit of RAM and CPU, but does nothing except skipping the DOS batch script.

        perlrun shows how a script can be both a (Unix) shell script and a perl script, where the former starts the latter. This trick is used to work around problems in ancient operating systems. A similar trick is used in Strawberry Perl, where batch files start embedded Perl scripts.

        And another little detail may be useful on Unix: If a text file is made executable, is invoked as a program, and the kernel won't find a shebang line as line 1 or some other file identifier, the shell will execute the text file as a shell script:

        $ echo echo Hi > hi $ chmod +x hi $ ./hi Hi $ cat hi echo Hi $

        See also Re^2: Shebang behavior with perl.

        <Update>

        The perl interpreter has another little detail that might suprise you, but it is meant to be helpful:

        $ cat foo.pl #!/bin/bash echo Hello World from Bash $ perl foo.pl Hello World from Bash $

        If perl sees a shebang line for another interpreter, it happily replaces itself with the other interpreter. So even if this file is started via perl and has a .pl extension, it will be executed by /bin/bash.

        </Update>

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: did you mean python
by bliako (Abbot) on Jan 27, 2026 at 13:55 UTC

    The main problem is that pyton and perl interpreters have different command line arguments and so detecting the script name is a pain. Otherwise, once you have the code, detecting computer language is a solved problem if avoiding the edges.

    My line of attack is to pass the whole CLI (edit: CLI means command-line params really) to a Perl diy interpreter which uses builtin CLI and script parsing (perl_parse()) to assess if the CLI and the provided script/one-liner parse as valid Perl CLI/Perl code. If it does not it passes the whole CLI to pyton via exec. This check is lame because if you intended to run Perl code which has syntax errors or your Perl CLI has errors then it will assume it is not Perl.

    This is better done by whipping our own Perl interpreter via perlembed and taking advantage of its builtin CLI and script parsing. Doing it via a Perl script is also possible following a similar approach using eval().

    Well, hopefully this can ascend you a step or two in the PyAno process, leading to Liberation soon,

    bw bliako

    EDIT: usage:

    after compiling it:

    gcc python.c -o python `perl -MExtUtils::Embed -e ccopts -e ldopts`

    run it with a perl or python script or one-liner, using any valid switches for any of the two interpreters:

    python myperl.pl python -e 'print reverse reverse split//,"Just another Perl Hacker"' python mypy.py python -c 'import sys'

    Note, the below program has been modified a couple of times already

    /* 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 ldopt +s` Tested only on Linux but it should be portable as it uses only POSIX and perlembed functions. */ #include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #include <errno.h> #include <string.h> #include <EXTERN.h> /* from the Perl distribution */ #include <perl.h> /* 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 i +s 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 &*^#$* pyto +n /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_TRU +NC|O_WRONLY, 0600)) ){ fprintf(stderr, "std_mute(): on open logger, f +ilename '%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 }
      python.c:37:3: error: call to undeclared function 'execvpe'; ISO C99 and later do not support implicit function declarations -Wimplicit-function-declaration
                      execvpe("/usr/bin/python", argv, env);
                      ^
      1 error generated.
      

        Yes, execvpe() is a GNU extension. Replace said function and its params with this: execv("/usr/bin/python", argv); or add #define _GNU_SOURCE at the beginning if your compiler supports GNU extensions, e.g. gcc and I think clang. For M$ alternatives, good luck.

        P.S. You also need to adjust the (real) python path "/usr/bin/python" with whatever you have. And make sure that the real python executable does not shadow your newly created one. The new one must have precedence. That depends on your PATH env var and OS specific rules.

        P.S.2 The side-effect of this change would be that your current environment is not passed to the (real) python executable. I am not sure what happens if your python script expects env vars. Perhaps it will read the system-wide ones and ignore those session/terminal-specific ones you may have set earlier.

Re: did you mean python
by Jenda (Abbot) on Jan 30, 2026 at 01:52 UTC

    Why?

    If you give either interpreter a script in the other language you end up with a syntax error right at the start and especially in case of a Perl script fed to python, the error is RIGHT at the use strict;. Do you really spend time reviewing the script to find out why do you get

    File "d:\temp\findInFiles.pl", line 1 use strict; ^^^^^^ SyntaxError: invalid syntax

    Even if you did find a way to hide the rest of the python script from Perl and getting it to print that message AND a way to hide the rest of the Perl script from python and getting it to print the opposite message, there is no chance you'd consistently include that thing even in your own scripts.

    Besides on a well set system, you do not need to specify the interpreter. The right one is used either based on the shebang or the file extension.

    Jenda
    1984 was supposed to be a warning,
    not a manual!