3

I am trying to run a Bash command from within my Perl program. However Perl seems to be confusing my Bash $PWD environment variable as a Perl variable.

How can I make it just read it all as a string?

This is what I'm trying to run

my $path = /first/path;
`ln -s $path $PWD/second/path`

Those backticks runs the second line in Bash. Using System() produces the same problem.

Any ideas?

Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
Ryan
  • 948
  • 6
  • 20
  • 2
    `PWD` *is exported* by bash (and by other shells like `zsh`); use `/usr/bin/printenv` to verify that ; it is a *magic* environment variable – Basile Starynkevitch May 25 '17 at 16:48
  • @BasileStarynkevitch, I stand corrected. – Charles Duffy May 25 '17 at 16:49
  • 2
    Use `\$` to escape the `$` –  May 25 '17 at 16:57
  • Why use PWD followed by a path? Why not a relative path? –  May 25 '17 at 16:59
  • 2
    @RyanSaffer, ...btw, in general, this kind of code is outright dangerous. You're *much* better using native Perl operations than `system()` or any equivalent -- if your path can contain `$(rm -rf ~)` (and yes, it's possible to create an actual file or directory with that string in its name), you don't want that to be passed to a shell without being quoted or escaped first (and if it were `$(rm -rf ~)'$(rm -rf ~)'` as a string literal in a name, that would serve to escape its quoting and wreak havoc even if you tried to make it safe by injecting literal single-quotes surrounding). – Charles Duffy May 25 '17 at 16:59
  • 1
    I'm sure this question is a duplicate, but the selected duplicate question is a terrible choice. – mob May 25 '17 at 17:28
  • @mob, the list can be edited (by anyone with a badge in a pertinent tag). Care to provide suggestions? – Charles Duffy May 25 '17 at 18:14
  • @CharlesDuffy I've tried and had trouble finding an honest duplicate, as this questions mixes things (running external commands and using Bash variables) for which there are many good posts. I linked some directly relevant ones in my answer but can't tell whether any is good for marking this as a "duplicate" of it. – zdim May 25 '17 at 19:18
  • @zdim, hmm. Arguably that's an argument for "too broad", *or* for flagging as a duplicate of multiple other links (where the conjunction of the full set adds up to cover the entire question space) -- that said, under the circumstances, I've just reopened the question. – Charles Duffy May 25 '17 at 19:41
  • @CharlesDuffy Yes, I would say that it is a dupe of multiple other posts. (I did find it most appropriate to give only an overview, and a number of links.) I'll review my post tonight anyway, and if it becomes clear what small set of links nicely covers it I'll label it as a duplicate of those (since this has clearly been covered, and everybody agrees on that). – zdim May 25 '17 at 21:38
  • @CharlesDuffy I give up; I can't decide on a reasonable set of posts for this (Bash env + external comms -- and to make a link). Instead I added a specific example. But please go ahead and mark it if you wish; all this has been covered. Sorry for noise. – zdim May 26 '17 at 05:49

1 Answers1

6

There are two queries here, on use of Bash variables and on running external commands.

There is the %ENV hash in Perl, with environment variables

perl -wE'say $ENV{PWD}'

However, you are often better off getting the equivalent within the script, as things may have a subtly different meaning for the script or change as the script runs.

More importantly, using shell commands exposes you to all kinds of potential problems with quoting, shell injection, and interpretation. For instance, the command you show is dangerous, as outlined in Charles Duffy comment. It is in principle better to use Perl's rich functionality. See for example

for a sober, and detailed, account of advantages.


In case you do need to run external commands, it is best to avoid the shell altogether, for example by using the multi-argument form of system. If you need the output of the command as well there are various modules in Perl that provide that. See links below.

If you also need to use the shell's capabilities, instead of quoting everything just right in order for the shell to receive what it needs better use a ready tool like String::ShellQuote.

Some examples:

Note that qx operator (backticks) uses /bin/sh, which may or may not get relegated to Bash. So if you want Bash you need system('/bin/bash', '-c', $cmd), where $cmd need be built carefully to avoid problems. See the links with examples.


Here is a full example related to the objective behind the question.

Your program's working directory may be other than expected depending on how it's started. For one, it changes after chdir. I don't know your exact intent with PWD, but in Perl there are core Cwd::cwd and FindBin with $RealBin, for the current working directory and for the directory where the script resides (generally different things).

To create a symbolic link to $path, with the relative path following the current working directory

use warnings;
use strict;
use Cwd qw(cwd);

my $cwd = cwd;

my $path = '/first/path';

symlink($path, "$cwd/second/path") or die "Can't make a symlink: $!";

If the path is meant to be the script's location use $RealBin from FindBin instead of cwd.

Note that with symlink you cannot pass a directory instead of a link name. See this page.

zdim
  • 64,580
  • 5
  • 52
  • 81