dideod.yang has asked for the wisdom of the Perl Monks concerning the following question:

Hi monks I have a question. As I know, system in perl can command based on shell. but My script can not work system using source. I think source is shell base command. So I try to use exec. exec can operate all language I think.. But also exec can not work in script. What happend? Can you tell me?
system "source ~~script.sh"; exec "source ~~script.sh";

Replies are listed 'Best First'.
Re: system & exec
by haj (Vicar) on Jul 27, 2018 at 05:48 UTC

    source is not an executable file, but a builtin command of the shell. Therefore you need to invoke it through the shell:

    system 'bash -c "source yourscript.sh"'

    Anyway, using source doesn't make sense in that context. You probably should just invoke your shell script directly.

      No reason to invoke two shells. Fixed:

      system 'bash', '-c', 'source yourscript.sh'
Re: system & exec
by Corion (Patriarch) on Jul 27, 2018 at 06:12 UTC

    If the shell command you want to source sets some environment variables and you want to use them in your Perl script, see Get default login environment.

      If the shell command you want to source sets some environment variables and you want to use them in your Perl script

      I've been waiting for an excuse to post this little hack I used recently ;-) A Perl script that needs some environment variables set by a shell script can write another shell script that sets the variables and then re-runs the Perl script... (currently written to rely on *NIX and bash).

      use warnings; use strict; use File::Temp qw/tempfile/; use Cwd qw/getcwd abs_path/; use String::ShellQuote qw/shell_quote/; my $SOURCE_FILE = '/tmp/test.sh'; if (!$ENV{PERL_REEXECUTED}) { my ($fh,$fn) = tempfile( DIR=>getcwd, SUFFIX=>'.sh' ); print $fh ". ", shell_quote($SOURCE_FILE), "\n"; print $fh "PERL_REEXECUTED=1 ", shell_quote($^X), " ", shell_quote(abs_path($0)), " \"\$\@\"\n"; print $fh "rm -f ",shell_quote($fn),"\n"; close $fh; exec('/bin/bash','--',$fn,@ARGV)==0 or die $?; }
Re: system & exec
by hippo (Archbishop) on Jul 27, 2018 at 08:06 UTC
    My script can not work system using source.

    That's because "source" isn't a valid command in your perl's default shell. Use "." instead and it should work fine, as demonstrated below. Although, since you are shelling out there is not really any practical difference between sourcing the script with "." and just executing it (but that's a different question and is likely the X in your XY Problem)

    $ cat script.sh echo Woo-hoo! $ cat yang.pl #!/usr/bin/env perl use strict; use warnings; system '. ./script.sh'; $ ./yang.pl Woo-hoo! $

      I find it hard to believe their /bin/sh understands . but not source.

        I find it hard to believe their /bin/sh understands . but not source.

        source is a bashism. Having a /bin/sh that behaves like a bash is pure luck. Linux had bash as default shell for a long time, but even on Linux, you can't rely on that any more. The *BSDs and commercial Unixes have changed their default shells several times. See https://www.in-ulm.de/~mascheck/various/shells/ for details.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        $ cat yangsource.pl #!/usr/bin/env perl use strict; use warnings; system 'source ./script.sh'; $ ./yangsource.pl Can't exec "source": No such file or directory at ./yangsource.pl line + 5. $

        I find your lack of faith disturbing

Re: system & exec
by bliako (Abbot) on Jul 27, 2018 at 11:03 UTC

    Shell scripts are interpreted as opposed to executed. A C program when compiled produces a (binary) file which contains instructions to be fed to the CPU. But a shell script, or a Perl script do not contain instructions for the CPU. They are written in a higher level language which someone = the interpreter must interpret to the language the CPU understands.

    If your higher level language is Perl then this interpreter must be /usr/bin/perl. This is why when you have a Perl program a.pl you execute it in one of two ways:

    1. /usr/bin/perl a.pl or /usr/bin/perl < a.pl or cat a.pl | /usr/bin/perl
    2. Insert the folloing directive (also known as the shebang) as the first line of your file: #!/usr/bin/perl . Then raise the executable bit for that file: chmod 755 a.pl and finally "execute" it directly from the shell (the command line): a.pl . You will notice of course that the first line, the shebang is NOT PERL code.

    In (2), I say "execute" in quotes because the shell you are running in will understand that this file a.pl contains no CPU language and will try to find an interpreter for it. The first thing to try will be to read the first line and check if it starts with #!. If it does then it will use the rest of the line as the actual interpreter of the rest of the file contents. In the case of a.pl, yes it starts with #! and so the interpreter will be what follows, i.e. /usr/bin/perl. And will run it as case #1.

    However what happens if no #! is found in the first line?

    In this case there are other obscure and arcane heuristics probably different to different shells (bash, sh, csh, ksh, zsh, gosh they are a lot!) to find an interpreter somewhere. YOU DO NOT WANT THAT if you are serious. It is best to be explicit about who your interpreter must be and tell that to the shell either via feeding your program directly into the interpreter (case 1) or via the shebang (case 2).

    source is a special shell built-in directive, at least for bash, sh, csh. i.e. there is no executable file associated with it. You will never find a file called /bin/source. Try it: find / -type f -executable -name 'source' So, source is a shell construct, a directive like a for, if etc. What does it do? It interprets the contents of the file fed with and places the results in the current shell without spawning a new shell. This does not affect your program output like your echo statements but it affects setting environment variables and changing directories, among other things.

    For example spot the different between "executing" the following program either with source (source a.bash) or with a shebang (./a.bash):

    #!/bin/bash # file a.bash export A=12 cd / echo my PWD=$PWD

    In the first case echo $A will output 12 and you will find that you changed dir to the root. In the second case A will be undefined and your current dir will be the one you were in before you run it.

    All these within the current shell you are running in (e.g. bash).

    Now, you want to run shell scripts within perl. Here is a question for you: in your examples, which shell perl will spawn in order to pass it your shell script for interpretation? If you do not know the answer then my advice, for what is worth, is to proceed no further without finding it. Because you will be having your shell scripts interpreted by an interpreter you do not know. Introducing uncertainty in your programs, especially if they will be run in different environments, is not a good idea unless you want to spread chaos, unleash forces you do not understand about, have hospital equipment failing, helicopters falling down from the sky, nuclear rockets released by themselves, contraceptive devices acting like a swiss cheese, you get the idea...

    Some readers' eyes must have caught haj's and haukex's use of bash -c and /bin/bash. They are being explicit about the shell they want to use to interpret their shell scripts. This is the right way. Be explicit about your shells interpreters.