I found Perl installed on my PC:
C:\Users\matth>perl -v
This is perl 5, version 38, subversion 2 (v5.38.2) built for x86_64-msys-thread-multi
[...]
It came in with installing Git. I never did Perl on Windows by now, only on Linux, but now I could use it to simplify things a bit. I used backticks a lot on Linux to run commands.
On Windows, I try to create a directory using Windows’ MD
("make directory") command in back-ticks. I get an error. And I get a different error if there is a backslash in the path.
C:\Users\matth>perl -e 'use warnings; `md $ARGV[0]`;' test12
Can't exec "md": No such file or directory at -e line 1.
C:\Users\matth>perl -e 'use warnings; `md $ARGV[0]`;' test12\b4
sh: line 1: md: command not found
What is happening here? Can I call Windows commands in backticks, as if I was working on the CMD.exe command line manually? Or am I in a bash context? If so, always or just sometimes? When?
First of all, you talk of Windows, but you're using an MSYS build of Perl ("built for x86_64-msys-thread-multi"). MSYS provides a unix-ish environment.
Why they fail
md test12\b4
When this string is provided to backticks, Perl attempts to run the shell command md test12\b4
.
As this is a unix environment, the shell in question is sh
. There is no executable or shell built-in named md
, so the shell emits an error message. (There is an executable named mkdir
.)
md test12
When this string is provided to backticks, Perl attempts to run the shell command md test124
.
However, since there are no shell metacharacters other than whitespace, Perl attempts to run the command directly instead of invoking a shell as an optimization. There is no executable named md
, so Perl emits an error message. (There is an executable named mkdir
.)
Correct approaches
From most complicated (what you were attempting) to least.
Note that I'm using system
instead of backticks because we're not trying to capture the output.
Using cmd
's md
via cmd
via sh
:
use String::ShellQuote qw( shell_quote );
use Win32::ShellQuote qw( quote_cmd );
my $dir = 'test\b4';
my $cmd_cmd = quote_cmd( "md", $dir );
my $sh_cmd = shell_quote( "cmd", "/c", $cmd_cmd );
system( $sh_cmd );
die( "Can't execute `sh`: $!" ) if $? == -1;
die( "`sh` killed by signal ".( $? & 0x7F ) ) if $? & 0x7F;
die( "`sh` exited with error ".( $? >> 8 ) ) if $? >> 8;
Using cmd
's md
via cmd
:
use Win32::ShellQuote qw( quote_cmd );
my $dir = 'test\b4';
my $cmd_cmd = quote_cmd( "md", $dir );
system( "cmd", "/c", $cmd_cmd );
die( "Can't execute `cmd`: $!" ) if $? == -1;
die( "`cmd` killed by signal ".( $? & 0x7F ) ) if $? & 0x7F;
die( "`cmd` exited with error ".( $? >> 8 ) ) if $? >> 8;
Using MSYS's mkdir
via sh
:
use String::ShellQuote qw( shell_quote );
my $dir = 'test/b4';
my $sh_cmd = shell_quote( "mkdir", $dir );
system( $sh_cmd );
die( "Can't execute `sh`: $!" ) if $? == -1;
die( "`sh` killed by signal ".( $? & 0x7F ) ) if $? & 0x7F;
die( "`sh` exited with error ".( $? >> 8 ) ) if $? >> 8;
Using MSYS's mkdir
(directly):
my $dir = 'test/b4';
system( "mkdir", $dir );
die( "Can't execute `mkdir`: $!" ) if $? == -1;
die( "`mkdir` killed by signal ".( $? & 0x7F ) ) if $? & 0x7F;
die( "`mkdir` exited with error ".( $? >> 8 ) ) if $? >> 8;
Using Perl's mkdir
:
my $dir = 'test/b4';
mkdir( $dir )
or die( "Can't make directory `$dir`: $!" );