Rust std::process with special characters - windows

I've got a very simple rust program but its not doing quite what I'd expect. Running on Windows, using a powershell prompt, I can do the following to display the path:
echo "%PATH%"
and I have a simple Rust program with:
Command::new("echo")
.arg("%PATH%")
.spawn()
.expect("ls command failed to start");
The command will launch and run, but it outputs:
%PATH%
instead of the path contents, like I'd expect. Other commands which don't use special characters seem to work as expected, so I suspect its related to handling them but I don't see a way in Rust to make the command any more primitive than it already is.
I've tried various formatting but it either fails to run the command or does the same.
I also tried using $env:path, but this always fails to run from Rust with a cannot find the path specified error.
Are there any suggestions for handling this? I could write the contents to a file and run the file instead, but running these types of commands from other languages directly works fine so I think it should work from Rust as well.
Thanks!
Update:
Managed to get the expected results from by using:
let out = Command::new("cmd")
.arg("/C")
.arg("echo %PATH%")
.spawn()
.expect("ls command failed to start");
}
I think the question got interpreted a bit differently, as getting the path was just an example of a larger issue I was seeing. Updating with the above solved my larger issue as well.

As the comment by French says: Spawning the process does not include the Powershell-environment, which would expand %PATH% to it's actual content before launching the process.
You need to get the content of PATH via std::env yourself or lookup the Powershell documentation on how to launch a subprocess inside a powershell-session.

As others have mentioned, it's not the special characters, it's the fact that those special characters are interpreted by powershell before the "echo" program runs at all.
Using https://doc.rust-lang.org/cargo/reference/environment-variables.html as a reference for how to look up environment variables, try something like this:
use std::env;
fn main() {
let cur_path = env::var("PATH").unwrap();
println!("Environment is: {}", cur_path);
}
You can try this here: https://play.rust-lang.org/
You can then feed cur_path into your "Command::new" if you wish. The trick is that powershell substitutes that argument BEFORE launching echo, which you may not have known, whereas when you execute the echo program directly, you have to do that substitution yourself.

Related

Where does MinGW/Cygwin convert environment variables to Windows style?

If you are in a Cygwin or MinGW bash shell, environment variables like $PATH are in "UNIX" format - using forward slashes as dir separators and using the colon to separate multiple paths. But if, inside this shell, you run something like cmd.exe /c 'echo %PATH%' the resulting output is in "Windows" format, using backslashes and semicolons respectively.
Is this magical conversion documented somewhere? Or better yet, can somebody point to the code that makes this happen?
(The reason I ask is because it seems the conversion doesn't always happen and I'm trying to understand the exact conditions needed for it to occur.)
The internal conversions between Unix and Windows path format are
performed by the funtions in path.cc
https://cygwin.com/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=winsup/cygwin/path.cc;h=3cb46c9c812e17460d56def2f915b21c7227f3bf;hb=HEAD
When a Cygwin program executes a Windows program the spawn process is
performed by functions in spawn.cc
https://cygwin.com/git/gitweb.cgi?p=newlib-cygwin.git;a=blob;f=winsup/cygwin/spawn.cc;h=37db52608e24e866e80401668ef13562f0cb67ea;hb=HEAD
If you need more details or ask clarification use the cygwin mailing list.

lua io.popen run program with space in path

I'm trying to get this program to start but I keep getting an error.
I've already tried to make the blockquotes [==[]==] around the string and "" around the program path but it still doesn't work.
local test = string.format([==["C:\Program Files (x86)\Audacity\audacity.exe" "F:\Aufnahme %s.%s.%s\ZOOM0001.WAV"]==], tag, monat, jahr)
print(test)
io.popen(test)
error when running the lua file
If I copy the command from the print(test) and use that in cmd.exe it works.
Thanks for the help :)
On Windows, you must enclose your command line (program + arguments) in additional outer-level quotes.
local test = string.format([==["C:\Program Files (x86)\Audacity\audacity.exe" "F:\Aufnahme %s.%s.%s\ZOOM0001.WAV"]==], tag, monat, jahr)
test = '"'..test..'"'
print(test)
io.popen(test)
When you are typing the command from keyboard in CMD.EXE window, then these additional quotes are added automatically by the code that is processing your keyboard input.
When you're using C function system or Lua functions os.execute, io.popen then you must add additional quotes manually.
This is how CMD.EXE works (and its design is full of not-very-logical decisions).

CMake convert unix to windows path

I'm trying to convert a unix style MSYS path such as
/c/my/path/to/a/folder
to a Windows path, or something that CMake would understand,
e.g C:/my/path/to/a/folder.
I'd like it to work on a path that is already correct.
Is there any proper way to do it ?
Note : Please do not mention cygwin's cygpath.
Edit: file(TO_CMAKE_PATH mypath result) is not working
There's no built-in CMake functionality for this, but you can write a function/macro to do it:
macro(msys_to_cmake_path MsysPath ResultingPath)
string(REGEX REPLACE "^/([a-zA-Z])/" "\\1:/" ${ResultingPath} "${MsysPath}")
endmacro()
set(mypath "/c/my/path/to/a/folder")
msys_to_cmake_path(${mypath} result)
message("Converted \"${mypath}\" to \"${result}\".")
Having said that, I agree with Antonio's comment in that it seems unusual to need this in the first place.
As an alternative to the accepted answer, you may wish to consider that MSYS itself will perform the conversion at any boundary between MSYS parent and native child process; thus, in an MSYS shell console:
cmd //c echo /c/my/path/to/a/folder
would display the appropriately converted path c:/my/path/to/a/folder. Additionally, this technique offers the possible advantage that it will emit the fully converted native form of a path, such as:
cmd //c echo /home/my/path/to/a/folder
to yield its native equivalent C:/MinGW/msys/1.0/home/my/path/to/a/folder, (assuming your MSYS installation is in the recommended default location, at C:/MinGW/msys/1.0).
With the caveat that running MSYS shell without proper initialization, as performed by msys.bat, may not work, (especially when running on 64-bit Windows), you may be able to run an equivalent command from within a native process, (such as within CMake), as:
C:/MinGW/msys/1.0/bin/sh -c 'cmd //c echo /home/my/path/to/a/folder'
Note that, if you invoke this from a native process which is itself running within an MSYS console, the initialization will have been correctly performed for the console's own shell process, and should thus propagate through the native process; the issues are more likely to arise if you attempt to invoke MSYS processes directly from a cmd.exe process, in a native Windows console, (or other native container).
Also note that, if the path name in question contains spaces, (never a good idea), you may need to enclose it within double quotes:
cmd //c echo "/home/my/path with spaces"
In this case, some experimentation indicates that the double quotes remain within the cmd output. I'm not entirely certain if this is necessary; you should use your discretion in your own particular usage case.

Problem with input filter using doxygen 1.6.3 on windows XP

I am trying to use doxygen to generate documentation for some matlab classes I have written. I am using the doxygen-matlab package, which includes a perl script to kludge matlab .m files into c++ style commented files, so that doxygen can read them.
In my doxyfile, I have set (according to the instructions)
FILTER_PATTERNS = *m=C:/doxygenMatlab/m2cpp.pl
However, when the code runs, rather than running the script on the input files, it appears to just open the script using whatever the default windows setting for .pl is.
IE, if I associate .pl with notepad, the script is opened by notepad once for each input file doxygen is trying to parse. If I associate .pl with perl.exe, the script runs and throws the no argument error
Argument must contain filename -1 at C:\doxygenMatlab\m2cpp.pl line 4.
The doxygen documentation says
Doxygen will invoke the filter program by executing (via popen()) the command <filter> <input-file>
So I am wondering if there is some problem with popen() and windows that I could fix.
Could you try the workarounds I posted on the Matlab File Exchange regarding the doxygen package ?
Set the following variables in the Doxyfile :
INPUT_FILTER=perl m2cpp.pl
FILE_PATTERNS=*.m
If it doesn't work you should try to install ActivePerl : with this version of perl, everything is working fine.
I tried to reproduce the error using the Windows command prompt ("cmd") and noticed the following:
If you call "perl m2cpp.pl" you get error -1 because you did not specify a m-file to be translated into a cpp-file.
If you call "perl m2cpp.pl mfile" and the path of mfile contains spaces, you get error 1.
After I moved the mfile into a location which does not contain spaces, I got the desired output.
Now back to Doxygen. I tried what you suggested, Fabrice, without any luck. I read the doxygen help and found out that the INPUT_FILTER variable is only read and used if FILTER_PATTERNS is empty.
Therefore, I now use INPUT_FILTER = "C:\Programme\MATLAB\R2009a\sys\perl\win32\bin\perl U:\doxygen_matlab\m2cpp.pl" and an empty FILTER_PATTERNS variable. With this configuration, you can even leave the PERL_PATH variable empty. Moreover, there seems to be no issues with file names that contain spaces.
Unfortunately, all files are parsed with the above configuration, not only m-files. However, setting FILTER_PATTERNS to something like *.m=C:\Programme\MATLAB\R2009a\sys\perl\win32\bin\perl U:\doxygen_matlab\m2cpp.pl does not work because doxygen automatically adds the name of the filtered mfile and interprets the command as perl "m2cpp.pl mfile". Of course, the file "m2cpp.pl mfile" does not exist, because these are two files.
Maybe you can find a solution to this problem. In the meantime, I suggest the workaround above and that you keep your C-files away from the folder that contains the m-files.
write a simple batch file, e.g. mfilter.bat, which takes one argument from command line:
C:\Programme\MATLAB\R2009a\sys\perl\win32\bin\perl U:\doxygen_matlab\m2cpp.pl %1
Change setting in Doxyfile:
FILTER_PATTERNS = *.m=mfile.bat
This did it for me (on a Windows platform)
I think I solved this problem : it came from a bad association between .pl and the program to execute (maybe due to a bad installation of the perl shipped whith Matlab ?).
To correct this, you should change the association for the .pl files : in a Windows command prompt ("cmd"), just type de 2 following lines :
assoc .pl=PerlScript
ftype PerlScript=C:\Program Files\MATLAB\R20xx\sys\perl\win32\bin\perl.exe %1 %*
(the old installation forgot the %* at the end, the arguments were not passed to the m2cpp.pl script).
And then everything should be fine with the FILTER_PATTERNS set the usual way, for example FILTER_PATTERN=*m=C:\DoxygenMatlbab\m2cpp.pl
Could you tell me if this fixed your problem ?
According to the Doxygen forums, there is a difference in behavior between using INPUT_FILTER and FILTER_PATTERNS.
I found that if I do some extra (escaped) quoting, I can get FILTER_PATTERNS to work. For example, instead of:
FILTER_PATTERNS = "*.m=sed -e 's|%%|//!|'"
Try:
FILTER_PATTERNS = "*.m=\"sed -e 's|%%|//!|'\""
(All of my experimentation was done with doxygen version 1.8.6)

powershell script exits in "if" clause if run from command prompt

I am feeling surprised by the difference between two seemingly identical scripts.
first.ps1:
"A"
if ($true) { "B" }
"C"
second.ps1:
"A"
if ($true) { "B"
}
"C"
Now open a CMD window, and run these scripts like this:
powershell - < first.ps1
powershell - < second.ps1
first produces:
A
B
C
while second produces just
A
This has to be a bug.
In the redirection under cmd.exe case, the function completes normally and correctly if the if and else blocks are individually all on one line:
$candidate = '.\foo.txt'
$current= '.\bar.txt'
"Comparison starts"
if ($(diff $(get-content $current) $(get-content $candidate)).length -gt 0){"There was a difference"}
else {"There was not a difference"}
"Comparison over"
But if either block is split up onto more than one line and that branch is taken, the script aborts with no warning/output.
I'd report this on the PowerShell feedback site (connect.microsoft.com).
Not sure why the redirection to input doesn't work, but if you just specify the script as an input argument to powershell, it seems to work:
C:\fa2>powershell.exe C:\fa2\tc.ps1
Comparison starts
There was a difference
Comparison over
Edit:
Yep, Jay proved it. The root problem is that Powershell does not support the '<' operator. I've been searching all day for some official documentaion on the web, but have not found it. I just occured to me to check some old notes, and I found a reference to this not being supported in v1. It only supports '>'.
I'll try to update if I find something more official than my memory. Leaving original text just for completnes.
I dont think the accepted answer is enitrely true here.
Take a look at Lee Holmes blog: link
He is one of the devs on the Powershell team, and wrote the Powershell Cookbook, just to give a little credence to his words.
I've run into this kind of problem with some complicated and archaic Bat scripts that relied on some funky fancy binary redirection. Powershell would run the Bat file, but at the point where the binary redirection took place it would just stop. Using [Process]:Start as described in the blog post worked wonderfully, and allowed me to parse the output of the Bat file like any other nicely behaved script to boot.
In your case I assume "diff" is an actuall exe and not a function, and its outputing binary and not text.
On a side note, I really don't see the need for redirecting the output of the script to Powershell like youre doing. Seems kind of counterproductive. You wrote a powershell script, seems like a waste not to use the paramter specifically provided to handle running input.
I don't think this is a bug. Try typing each of those lines in on the console and you will see what is happening. When you type and open bracket and don't close it, PowerShell goes into a multiline entering mode. To exit this mode, you need a closing bracket AND a blank line afterward. If you have a blank line before or after the "C" it should work.
Of course, maybe it is a bug and just has the same effect as multiline input. :)
I can't get this to work myself, powershell is ignoring what I send into it.

Resources