How to pass a string constant as it is in powershell - java

I have the following simple java program:
import java.util.Arrays;
public class Arguments
{
public static void main(String [] args)
{
System.out.println("args: "+Arrays.toString(args));
}
}
When I execute this in powershell using the following command: java Arguments "*.java" the string received in the program is not "*.java" but a comma-separated list of all java files in the current directory. And if there are no java files in the current directory the string received is "*.java".
I want to know why this is happening and how to pass a string as it is without converting it.
Update: java Arguments '"*".java' and java Arguments `"*.java`" did the work but this creates the same problem when executed in cmd. Can anyone explain why this is happening? Is there any common solution for both PowerShell and cmd?

It is not PowerShell (nor cmd.exe) that interprets "*.java" as a filename pattern and expands (resolves) it to the matching files, known as globbing in the Unix world.
(You would only get that behavior if you used *.java - i.e., no quoting - in PowerShell Core on Unix-like platforms, but never with a quoted string such as "*.java" or '*.java', and even without quoting never on Windows).
Apparently, it is legacy versions of java.exe on Windows that automatically perform the globbing on unquoted arguments, in an apparent attempt to emulate the behavior of POSIX-like shells such as Bash on Unix.
As of (at least) JDK 12, this behavior no longer seems to be effect, at least not by default.
The linked answer suggests that in earlier versions there may be a system property that controls the behavior, but it's unclear what its name is.
Generally, the syntax java -D<systemPropName>=<value> ... can be used to set a system property on startup.
Therefore, you have the following options:
Upgrade to a Java version that no longer exhibits this behavior (by default).
In legacy versions, find the relevant system property name that disables the behavior and use the syntax shown above.
Use shell-specific quoting, as shown below.
Using quoting to prevent globbing:
To prevent java.exe from performing globbing, the invocation command line must ultimately contain "*.java", i.e., the argument must be enclosed in double quotes.
Unfortunately, there is no common syntax that works in both PowerShell and cmd.exe:
cmd.exe:
cmd.exe passes double-quoted arguments through as-is, so the following is sufficient:
java Arguments "*.java"
PowerShell:
PowerShell, by contrast, performs re-quoting as needed behind the scenes (see this answer for more information).
Since an argument with content *.java normally does not require quoting when you pass it to an external program, PowerShell translates both "*.java" and '*.java' to unquoted *.java in the command line that is ultimately used behind the scenes - which is what you experienced.
There are two ways around that:
Use java Arguments '"*.java"', i.e., embed the " chars. in the argument, inside a literal string ('...').
Use java Arguments --% "*.java"; --% is the stop-parsing symbol (PSv3+), which instructs PowerShell to pass the remainder of the command line through as-is (except for expanding cmd.exe-style environment-variable references).

Related

How to fix Java args not getting Japanese characters properly in string from Windows Explorer?

On Windows 10, I have a shortcut file in the "SendTo" directory. It is a shortcut to a .bat file.
Inside the .bat file can have just the command "python <filepath> %*" or "java -jar <filepath> %*".
When I select and right click file(s) from Windows Explorer and have it sent to this shortcut file, it will run the program from <filepath> with the selected file(s) as arguments.
I am trying to send files with filenames containing Japanese characters as arguments. The filenames are passed to python programs just fine, but for Java programs, the args for the filenames are messed up and the Java program cannot find the file.
For example, in Java and with locale of Japan, a filename of Filename ファイル名.txt becomes Filename 繝輔ぃ繧、繝ォ蜷�.txt in the args. Other locales also do not work. The result is the same if I send the args to python and then from python to Java.
How to make it so Java gets the proper filename or can find the file properly?
You are encountering an unresolved issue with Java. See open bug JDK-8124977 cmdline encoding challenges on Windows which consolidates several problems related to passing Unicode arguments to a Java application from the command line.
Java 18 (to be released next month) resolves some UTF-8 issues with the implementation of JEP 400: UTF-8 by Default, but specifically not your problem unfortunately. From the "Goals" for JEP400:
Standardize on UTF-8 throughout the standard Java APIs, except for console I/O. [Emphasis mine]
However, there is a workaround. See Netbeans Chinese characters in java project properties run arguments, and in particular this answer which successfully processes Chinese characters passed as command line arguments using JNA (Java Native Access). From that answer:
JNA allows you to invoke Windows API methods from Java, without using
native code. So in your Java application you can call Win API methods
such as GetCommandLineW() and CommandLineToArgvW() directly, to access
details about the command line used to invoke your program, including
any arguments passed. Both of those methods support Unicode.
So the code in that answer does not read the arguments passed to main() directly. Instead it uses JNA to invoke the Win API methods to access them.
While that code was processing Chinese characters passed as arguments from the command line, it would work just as well for Japanese characters, including your Japanese filenames.

unable to execute "normalize-audio out/*.wav" from java

I am trying to execute "normalize-audio out/*.wav" command from java. for single file as follows,
Runtime.getRuntime().exec("normalize-audio 0002.wav");
it works fine. but, when i use,
Runtime.getRuntime().exec("normalize-audio out/*.wav");
it says:
file *.wav: No such file or directory
any idea, how i can achieve this?
The expansion of the * glob is, when you call your script "normally", carried out by the shell. That is, bash or cmd.exe or whatever you're using, sees the * as a special character and carries out some logic to expand it into the set of actual filenames that match. Then, the script is actually executed passing in these explicit filenames as the argument(s).
So, you have two options:
Do the same yourself. Query the files in the appropriate directory, find all of them that match the *.wav pattern, then build these up into the argument string. So instead of invoking exec passing *.wav as an argument to your script, you'd end up passing something like 0001.wav 0002.wav 0007.wav.
Get bash to do it. If you invoke bash as the program, and then pass in your command as a string, then bash will do its glob expansion as normal. This would look something like Runtime.getRuntime().exec("bash -c normalize-audio out/*.wav");. The downsides to this are firstly that you're now much less portable (you have requirements about third-party software existing on the PATH), and also that you can get into all sorts of tangles with escaping quotes in your invocation.

Converting Perl to Java

I have a Perl file which is actually calling some jar files and I'm trying to make it an all Java program and remove the Perl. So I came up through this lines which is:
$blammercommand="$javapath $javaparams -jar $blammerpath -conf $blammerconf $blammerparams -cpu $cpu -i \"".$tmpdir."blastresults/*.bls\" -db \"$infilename $blastdb\"";
(system($blammercommand)==0) or die "unable to do $blammercommand\n";
I've already decompressed the jar files and added the source codes to my Eclipse project and have access to the main function of the related jar file. I'm just trying to pass the arguments as inputs.
My problem is exactly here that I don't know what "\"".$tmpdir."blastresults/*.bls\"" and "\"$infilename $blastdb\"" mean. I know what exactly each one of this variables are but I don't know how those \, / and * are working and how should I convert them to Java.
Those are just shell escaping and shell globbing. Literally written, it would look like this:
${javapath} ${javaparams} -jar ${blammerpath} -conf ${blammerconf} ${blammerparams} -cpu ${cpu} -i "${tmpdir}blastresults/*.bls" -db "${infilename} ${blastdb}
Here, the syntax ${name} is meant to indicated an inserted value of the variable. The system command in Perl runs a system command through the default system shell, usually something like bash. The quotes are used to make several space-separated strings "stick together" as one argument. The * is a wildcard, which is replaced by all filenames in a given directory.

Java path problems on Cygwin

I'm trying to compile a Java project under Cygwin using a native Win32 Java.
The Java binaries are correctly found under /cygdrive/c/jdk/bin on my machine.
The following command works fine:
javac -d . ./gnu/kawa/util/PreProcess.java
The PreProcess.class file is generated in ./gnu/kawa/util/. Trying to invoke Java on this fails however:
CLASSPATH=.:$CLASSPATH java gnu.kawa.util.PreProcess \
%java6 +use:com.sun.net.httpserver +enable:XML \
`sed -e 's|\([^ ]*\)|./\1|' < ./patch-source-list`
Error: Could not find or load main class gnu.kawa.util.PreProcess
...
This command was invoked by make, that's where the $CLASSPATH variable is set dynamically. patch-source-list is just a list of class names. The : in the classpath looks suspicious, but I'm not sure how to test ; while not annoying sh.
My only other suspicion is that the native Java is trying gnu\kawa\util\PreProcess, but I think cygwin can transparently handle that.
Any ideas? Thanks for your time.
Another option would be to build your path using the ':' and then fix the results using cygpath. This is probably overkill in your specific situation, but in a general case where you may have references to multiple directories, some of which may be referenced as absolute rather than relative paths, or if you are working with cygwin symlinks, it is much more useful.
$ ln -s /cygdrive/c/development/libraries/ ../libs
$ cygpath -pw /cygdrive/c/development/:.:../libs
C:\development\;.;C:\development\libraries\
so then you'd build your CLASSPATH variable as before, and in the final stage run
CLASSPATH="`cygpath -pw "$CLASSPATH"`" java (whatever)
Remember, the JVM has no idea that you are using the cygwin bash shell.
Two things:
for the classpath locations, use the windows path names. Thus, no "/cygdrive/c/somepath", but "c:\somepath\" ("/" and "\" can be used interchangeably however)
use ';' instead of ':' in the classpath list
This should work:
export CLASSPATH="./gnu/kawa/util/PreProcess.class"
CLASSPATH=".;$CLASSPATH" java gnu.kawa.util.PreProcess
The : in the classpath looks suspicious, but I'm not sure how to test ; while not annoying sh.
You're exactly right: you need to use ; instead of :. As for how to use it — as Mat alludes to above, you need to "quote" the semicolon. Any of these will work:
CLASSPATH=.\;$CLASSPATH java Foo
CLASSPATH=.';'$CLASSPATH java Foo
CLASSPATH='.;'$CLASSPATH java Foo
CLASSPATH=".;$CLASSPATH" java Foo
You can use whichever one you like best. (The first uses a backslash, which quotes a single following character. The second and third use single-quotes, which quote a sequence of zero or more characters. The fourth uses double-quotes, which are like single-quotes except that they still allow the variable $CLASSPATH to be expanded. For that matter, you could also write something like
CLASSPATH=".;"$CLASSPATH java Foo
if you want. See the above link for lots more information about quoting in Bash.)

What does "java: No match." mean?

When running java from the command line:
java -classpath bin:pellet-2.0.0/lib/* com.stuff.MyClass argumentTextStuff
I'm getting the following reply:
java: No match.
What's this mean? which java points to the expected file. And if I take the asterisk out, then I get the expected class not found error. Google searches aren't fruitful because I keep getting stuff about matching regexp patterns.
Ah... already figured it out. It worked when I was using a bash shell, however the * is treated differently in the tcsh shell. So we switched to bash and it works. The reason is described here (per polygenelubricants's suggestion in the comments below).
That is a bash (or whatever shell are you using) error message not a java one.
It means that "bin:pellet-2.0.0/lib/*" doesn't match any file.
Do not use wildcards in classpath.
If the * makes the difference, then the issue is probably related to how it's interpreted and by who. Try escaping it so that it's passed as is to java.
See also
Java/Tutorials/Path and Class Path
Java/Technical Article/Setting the class path (on Windows) (on setting up classpath wildcards)
With a unix Shell you often need to put such things in single or double quoted to ahold the Shell expanding the asterisk.

Categories