Passing command line argument to an application - perl

I would like to execute an application from a Perl script.
The Perl script calls the application with a variable as a parameter, the value of which is a long string with lots of spaces within.
The application interprets these as separate strings but I want all of it as one string.
Here's the code in the Perl script:
$command = "Hello world here i come. Hope this works"
when the Perl script tries to call the application
./a.out $command
and within the applicaion I try to access argv[1], I only get the string Hello. argv[2] contains world but I want argv[1] to contain the complete string contained in $command. How do I do it?

Use a multiple argument form of exec
exec "./a.out", $command
In this way the shell doesn't get involved

Related

Run ps1 file in foreground

How to run a ps1 file in foreground?
I noticed when I execute my ps1 file, instead of view the log of the ps1 file execution, a Background job is started.
Is there anyway to run a ps1 file and get the same behavior we have when executing a sh or batch file?
Updates:
My ps1 file:
$scratchOrgName=$args[0]
Write-Host "Hello " & $scratchOrgName
ps1 file execution:
The & starts a new process. (It's called the background operator)
Change the code into something like
Write-Host "Hello" $scratchOrgName
or
Write-Host "Hello $scratchOrgName"
tl;dr
Unless you explicitly request that commands be run in the background (as you accidentally did, see next section), PowerShell commands do run in the foreground.
To achieve what you were (presumably) trying to do:
$scratchOrgName=$args[0]
"Hello $scratchOrgName"
Michaël Hompus' helpful answer provides the crucial pointers, but let me attempt a systematic overview:
Write-Host "Hello " & $scratchOrgName is composed of two statements:
Write-Host "Hello " & submits command Write-Host "Hello " as a background job, in PowerShell (Core) v6+ (in Windows PowerShell (v5.1-), you'd get an error, saying that & is reserved for future use). An object representing the newly created job is output and prints to the screen, as shown in your screenshot.
The post-positional use of & - i.e. placed after a command - is indeed the background operator, and is therefore equivalent to Start-Job { Write-Host "Hello " }
This contrasts with pre-positional use of &, which then acts as the call operator, for invoking command names or paths that are potentially quoted or contain / are variable values (e.g. & 'C:\Program Files\Node.js\node.exe')
$scratchOrgName - by virtue of PowerShell's implicit output behavior - outputs the value of that variable, which prints to the screen by default.
As for what you intended:
& is VBScript's string-concatenation operator; its PowerShell equivalent is +
A string-concatenation operation is an expression, and as such it must be enclosed in (...) in order to be passed as an argument to a command such as Write-Host.
Therefore, the direct PowerShell expression of your intent would be:
Write-Host ("Hello " + $scratchOrgName)
But, as also shown in Michaël's answer, this is more easily expressed via an expandable (double-quoted) string ("..."):
Write-Host "Hello $scratchOrgName"
Taking a step back: Write-Host is typically the wrong tool to use, unless the intent is to write to the display only, bypassing the success output stream and with it the ability to send output to other commands, capture it in a variable, or redirect it to a file.
To output a value, use it by itself, taking advantage of the aforementioned implicit output behavior (or use Write-Output, though that is rarely needed):
"Hello $scratchOrgName"
See this answer for more information.

Perl interface with Aspell

I am trying to identify misspelled words with Aspell via Perl. I am working on a Linux server without administrator privileges which means I have access to Perl and Aspell but not, for example, Text::Aspell which is a Perl interface for Aspell.
I want to do the very simple task of passing a list of words to Aspell and having it return the words that are misspelled. If the words I want to check are "dad word lkjlkjlkj" I can do this through the command line with the following commands:
aspell list
dad word lkjlkjlkj
Aspell requires CTRL + D at the end to submit the word list. It would then return "lkjlkjlkj", as this isn't in the dictionary.
In order to do the exact same thing, but submitted via Perl (because I need to do this for thousands of documents) I have tried:
my $list = q(dad word lkjlkjlkj):
my #arguments = ("aspell list", $list, "^D");
my $aspell_out=`#arguments`;
print "Aspell output = $aspell_out\n";
The expected output is "Aspell output = lkjlkjlkj" because this is the output that Aspell gives when you submit these commands via the command line. However, the actual output is just "Aspell output = ". That is, Perl does not capture any output from Aspell. No errors are thrown.
I am not an expert programmer, but I thought this would be a fairly simple task. I've tried various iterations of this code and nothing works. I did some digging and I'm concerned that perhaps because Aspell is interactive, I need to use something like Expect, but I cannot figure out how to use it. Nor am I sure that it is actually the solution to my problem. I also think ^D should be an appropriate replacement for CTRL+D at the end of the commands, but all I know is it doesn't throw an error. I also tried \cd instead. Whatever it is, there is obviously an issue in either submitting the command or capturing the output.
The complication with using aspell out of a program is that it is an interactive and command-line driver tool, as you suspect. However, there is a simple way to do what you need.
In order to use aspell's command list one needs to pass it words via STDIN, as its man page says. While I find the GNU Aspell manual a little difficult to get going with, passing input to a program via its STDIN is easy enough and we can rewrite the invocation as
echo dad word lkj | aspell list
We get lkj printed back, as due. Now this can run out of a program just as it stands
my $word_list = q(word lkj good asdf);
my $cmd = qq(echo $word_list | aspell list);
my #aspell_out = qx($cmd);
print for #aspell_out;
This prints lines lkj and asdf.
I assemble the command in a string (as opposed to an array) for specific reasons, explained below. The qx is the operator form of backticks, which I prefer for its far superior readability.
Note that qx can return all output in a string, if in scalar context (assigned to a scalar for example), or in a list when in list context. Here I assign to an array so you get each word as an element (alas, each also comes with a newline, so may want to do chomp #aspell_out;).
Comment on a list vs string form of a command
I think that it's safe to recommend to use a list-form for a command, in general. So we'd say
my #cmd = ('ls', '-l', $dir); # to be run as an external command
instead of
my $cmd = "ls -l $dir"; # to be run as an external command
The list form generally makes it easier to manage the command, and it avoids the shell altogether.
However, this case is a little different
The qx operator doesn't really behave differently -- the array gets concatenated into a string, and that runs. The very fact that we can pass it an array is incidental, and not even documented
We need to pipe input to aspell's STDIN, and shell does that for us simply. We can use a shell with command's LIST form as well, but then we'd need to invoke it explicitly. We can also go for aspell's STDIN by means other than the shell but that's more complex
With a command in a list the command name must be the first word, so that "aspell list" from the question is wrong and it should fail (there is no command named that) ... except that in this case it wouldn't (if the rest were correct), since for qx the array gets collapsed into a string
Finally, apsell nicely exposes its API in a C library and that's been utilized for the module you mention. I'd suggest to install it as a user (no privileges needed) and use that.
You should take a step back and investigate if you can install Text::Aspell without administrator privilige. In most cases that's perfectly possible.
You can install modules into your home directory. If there is no C-compiler available on the server you can install the module on a compatible machine, compile and copy the files.

Input argument is a file or an either content to Perl

I wrote a Perl script to convert from TEX format to JSON format.
Calling in the batch file:
perl -w C:\test\support.pl TestingSample.tex
This is working fine now.
Perl script having two types of input from another program (might be any platform/technology) one is file (*TEX) or else content (*TEX file) either this or that option.
How can I receive the full content as the input to the Perl script?
Now my Perl script is:
my $texfile = $ARGV[0]; my $texcnt = "";
readFileinString($texfile, \$texcnt);
I am trying to update:
perl -w C:/test/support.pl --input $texcnt" #Content is Input
I am receiving error message:
The command line is too long.
Could someone please advice?
First of all regarding the error you're getting:
Perl (or your shell) is complaining that your input argument is too long.
Parsing entire files as arguments to scripts is generally a bad idea anyway, for example quotation mark escaping etc. might not be handled and thus leave a wide open vulnarbility to your entire system!
So the solution to this is to modify your script so that it can take the file as an argument (if that isn't already the case) and if you really need to have an entire file's content parsed as an argument I'd really advise you to create a temporary file in /tmp/ (if on Linux) or in your %TEMP% directory on Windows and parse the file the content into the file and after that give your support.pl script the new temp file as an argument.

Calling a shell command with multiple arguments

I'm trying to automate creating certificates via a Perl script.
The command I want to run is:
easyrsa build-client-full $clientname nopass
The way I thought it should be done in Perl is:
my $arguments = ("build-client-full $clientname nopass");
my $cmd = "$easyrsa_path/easyrsa"." "."$arguments";
system("bash", $cmd);
However, this yields
"file not found"
on execution. I triple checked that the path is correct.
If I try it like this:
my #arguments = ("bash", $easyrsa_path,"build-client-full $clientname nopass");
system(#arguments);
Bash returns
"Unknown command 'build-client-full test nopass'. Run without commands
for usage help."
Background
When you use system(LIST) where LIST has more than one element, Perl will not call the shell, and instead directly invoke the program given by the first element in the LIST, and use the rest of the list as command line arguments to be passed verbatim, with no interpolation by the shell, including no splitting arguments on whitespace.
So in your first example, Perl is running the command bash and passing the string "$easyrsa_path/easyrsa build-client-full $clientname nopass", literally as one big long argument, and in your second example, it's running the command bash and passing the two arguments $easyrsa_path and "build-client-full $clientname nopass". However, I assume that easyrsa needs the three arguments as separate strings in its argument list, which the shell would normally split, but since both of your calls to system aren't using the shell, it's not working.
system (and exec) have four ways of interpreting their arguments, as per the documentation:
If you pass a single string (including a LIST with only one element) that does not contain any shell metacharacters, it is split into words and passed directly to execvp(3) (meaning it bypasses the shell).
Warning: This invocation is easily confused with the following - a single metacharacter will cause the shell to be invoked, which can be dangerous especially when unchecked variables are interpolated into the command string.
If you pass a single string (including a LIST with only one element) that does contain shell metacharacters, the entire argument is passed to the system's command shell for parsing. Normally, that's /bin/sh -c on Unix platforms, but the idea of the "default shell" is problematic, and there is certainly no guarantee that it'll be bash (though it could be).
Warning: In this invocation of system, you have the full power of the shell, which also means you're responsible for correctly quoting and escaping any shell metacharacters and/or whitespace. I recommend you only use this form if you explicitly want the power of the shell, and otherwise, it's usually best to use one of the following two.
If there is more than one argument in LIST, this calls execvp(3) with the arguments in LIST, meaning the shell is avoided.
(See below for caveats on Windows.)
The form system {EXPR} LIST always runs the program named by EXPR and avoids the shell, no matter what's in LIST.
(See below for caveats on Windows.)
The latter two are desirable if you want to pass special characters that the shell would normally interpret, and I'd actually always recommend doing this, since blindly passing user input into system can open up a security hole - I wrote a longer article about that over on PerlMonks.
Solutions
#Borodin and #AnFi have already pointed out: If you simply split up the elements of the LIST properly, it should work - it doesn't look like you need any features of bash or any shell here. And don't forget to check for errors!
system("$easyrsa_path/easyrsa","build-client-full",$clientname,"nopass") == 0
or warn "system failed: \$? = $?";
Note that there are good modules that provide alternatives to system and qx, my go-to module is usually IPC::Run3. These modules are very helpful if you want to capture output from the external command. In this case, IPC::System::Simple might be easier since it provides a drop-in replacement for system with better error handling, as well as systemx which always avoids the shell. (That module is what autodie uses when you say use autodie ':all';.)
use IPC::System::Simple qw/systemx/;
systemx("$easyrsa_path/easyrsa","build-client-full",$clientname,"nopass");
Note that if you really wanted to call bash, you'd need to add the -c option and say system("bash","-c","--","$easyrsa_path/easyrsa build-client-full $clientname nopass"). But as I a said above, I strongly recommend against this, since if $easyrsa_path or $clientname contain any shell metacharacters or malicious content, you may end up having a huge problem.
Windows
Windows is more complicated than the above. The documentation says that the only "reliable" way to avoid calling the shell there is the system PROGRAM LIST form, but on Windows, command line arguments are not passed as a list, but a single big string, and it's up to the called command, not the shell, to interpret that string, and different commands may do that differently - see also. (I have heard good things about Win32::ShellQuote, though.)
Plus, there's the special system(1, #args) form documented in perlport.
If you pass multiple parameters to system then each one forms a separate parameter to the command line. So it is as though you had entered
easyrsa "build-client-full test nopass"
and you correctly get the error
Unknown command 'build-client-full test nopass'
You also don't need to add bash: perl will run the shell for you if necessary
You can either pass the whole command to system
system($cmd)
and perl will pass it to the shell to be processed as if you'd entered it at the command prompt. Or you can split the parameters properly
system("$easyrsa_path/easyrsa", "build-client-full", $clientname, "nopass")
which will make perl call easyrsa directly unless the command contains things that need the shell to process, like output redirection

Passing a variable to a command in a script

I've been searching all over the place and since I'm taking my first steps in PERL this might be one of he dumbest questions but here it goes.
So I'm creating a script to manage my windows and later bind it to keyboard shortcuts, so I I'm trying to run a command and passing some variables:
my $command = `wmctrl -r :ACTIVE: -e 0,0,0,$monitors->{1}->{'width'}/2,$monitors->{1}->{'height'}`;
But I get an error saying I'm not passing the right parameters to the command, but if I do this, everything works great:
my $test = $monitors->{1}->{'width'}/2;
my $command = `wmctrl -r :ACTIVE: -e 0,0,0,$test,$monitors->{1}->{'height'}`;
So do I really have to do this? assign it first to a variable and then pass it, or there's a more elegant way of doing it?
The backticks operator (or the qx{}) accepts A string which is (possibly) interpolated. So accepts string and not expression like $var/2.
Thats mean than the $variables ($var->{1}->{some} too) are expanded but not the arithmetic expressions.
Therefore your 2 step variant works, but not the first.
If you want evaluate an expression inside the string you can use the next:
my $ans=42;
print "The #{[ $ans/2 ]} is only the half of answer\n";
prints
The 21 is only the half of answer
but it is not very readable, so better and elegant is what you're already doing - calculate the command argument in andvace, and to the qx{} or backticks only pass the calculated $variables.