I would like to create a program in a linux/unix environment that runs from command line. The desired outcome would be to have the ability to tab complete directories. Are there any libraries available to achieve this?
Synopsis: /ho
[tab] /home/
There exists a JNI wrapper for the GNU "readline" library (which is what Bash uses for tab-completion); see http://sourceforge.net/projects/java-readline/. Tab-completion is a generic feature, not specifically tied to the filesystem — for example, the PostgreSQL command-line client uses tab-completion to complete table-names — so you'll probably want to use this in concert with the PrefixFileFilter that Tomasz Nurkiewicz mentions (or another similar approach for generating the list of filenames).
PrefixFileFilter from apache-commons IO might be helpful:
File dir = new File(".");
String[] files = dir.list(new PrefixFileFilter("ho"));
This will return a list of files in current directory starting with ho. You'll get the rest.
I don't think you need a library for that, just code it your self. File class already contains everything needed like:
File[] listFiles()
boolean isDirectory()
boolean isFile()
...
Then you just need to create:
take the temporary path, eg "/home/Ja"
split it between last concrete part and part to complete, /home and Ja
list files from concrete part new File("/home").listFiles()
and select only currently correct for partial file.getName().startsWith("Ja")
I guess it will be around 50-100 LOCs including all the checks necessary to avoid weird things.
If you want a good approach, use FilenameFilter, so that you will be able to filter out files directly when invoking listFiles, eg
files = folder.listFiles(new PartialFileFilter("Ja"))
Are there any Java libraries to create command line applications with TAB completion of directories?
You may be interested in picocli. Picocli is a 1-file Java library for building command line applications with almost zero code; it can generate ANSI colored usage help messages, and picocli supports command line autocompletion on Linux (and Cygwin and babun on Windows) since version 1.0.
How does this work? The bash and zsh Unix shells provide something called "programmable completion", and picocli leverages this to generate an autocompletion script tailored to the #Option and #Command annotations in your application.
With this script installed, users can type the TAB key, and the Unix shell will show a list of the available subcommands and options. For options associated with a java.io.File or java.nio.file.Path field, the shell will show the available files and directories that match what the user typed so far.
This actually handles more than just files and directories: the picocli-generated completion script shows known hosts (from the user's /etc/hosts file) for options of type java.net.InetAddress, and shows enumvalues for options on fields of any enum type, including custom enums.
The below animation demonstrates what is possible.
You can use a recent JLine version.
The latest one is available from https://github.com/jline/jline3.
It provides built-in completers for directories and files.
JLine is probably no longer maintained, but there are still quite a few Java projects that use it:
http://jline.sourceforge.net/index.html
However, if you know what you want to use Linux/Unix, and the only completion you want is simple file/directory names, you could just shift the problem and run your Java app under rlwrap:
$ rlwrap -c java ...
Related
I'm trying to make a java program which executes java files and gives output in the text field. I've used Runtime class to compile the .java file .So how do I get the output from that newly made class file.
Runtime.getRuntime().exec("javac Y://CodeSave.java");
Runtime.getRuntime().exec("java Y://CodeSave.class>output.txt");
In the general case: exec returns a Process instance which has accessors (getOutputStream, etc.) for the I/O streams. You read from / write to those streams.
But: In your code you've used >output.txt. That's a shell feature. If you want to do it that way, you need to spawn a shell, not the java tool directly, and have the shell execute that command line. (A search for spawning/execing a shell should find you lots of examples.)
Using Runtime.exec is definitely not the right way to do it, for various reasons. Examples are that both java and javac rely on environment variables which you can't pass that way.
First of all, I'd ask myself if I really needed to do this. Compiling and executing dynamically created code is a huge security risk.
But if you're sure you need to do it, here's what I'd do.
Move your sources to a dedicated temporary folder
Use the ToolProvider api to compile your sources
Use a dynamic throwaway ClassLoader (ByteBuddy may help you there) with a SecurityManager to load and execute your code from within your application
In Unix (or Linux), if I want to run a shell script, I can start the file with #!/bin/sh. With awk, I start the executable file with #!/usr/bin/awk -f and it treats the rest of the file as the program.
How do I do that with a Java program? I tried copying the simple.class to simple, putting #!/export/appl/Mail/java/bin/java at the top and making the file executable, but I get:
69> ./simple
Error: Could not find or load main class ..simple
I know this can be done with an executable shell script, or a C program that execs the java interpreter. Every other interpreter on Unix can be called with a #! load card, surely there's a way to do it with Java.
The most usual way is to have a wrapper for the Java. A shell script that executes the "java -jar yourJar.jar" or equivalent. And then you bundle the shell script and the windows equivalent bat file with your product.
Another option is to have a native launcher. For example you can see the Eclipse project which has gone that way. You download Eclipse and you have a native executable to run. The native executable will launch your Java program.
One more option is to compile Java into native code. For example you can use this commercial tool called Excelsior JET ( http://www.excelsior-usa.com/jet.html ).
The Java class file format doesn't allow text before the header, that's why the Java runtime no longer accepts the .class file after your modification.
On Linux you can use binfmt_misc to support additional executable formats, including .class files. It's basically a way to tell the Linux kernel how to detect executable formats and how to execute them.
This Archilinux Wiki article explains in more detail how to get this effect.
You cannot do it with a Java program. Firstly, the Java program needs to be compiled before execution. Secondly, even if compilation wasn't required, the hash sign is not a comment in Java, so that would be a syntax error.
I've never heard the term "load card". What you have is an "interpreter directive" designated by a shebang. This merely designates which interpreter the shell should invoke on a given script.
As for why C programs can be run directly in the shell, executables recognized by the operating system are passed to the loader. A Java class isn't an executable, at least to the OS anyway. So the shell must know which interpreter to pass control to.
But as you've noticed, the shebang doesn't work. The reason is that the class file is in a specific binary format that the JVM expects. Editing this file will break convention and lead to an error. Therefore, there is no way to do what you've asked.
However, you can create a "shortcut" to the program you want to run by creating an alias or perhaps writing a one-line Shell script to wrap the java command you need. This is the common practice as I understand it.
The other answers explain why you can't do what you are trying to do. However, if your shell is zsh, you can create a suffix alias. For example, to execute .jar files using java:
alias -s .jar="/usr/bin/java -jar"
Then, to execute blarg.jar, you just type ./blarg.jar. Of course, you must chmod +x your .jar file first.
Apart from the wrapper script and binfmt_misc solutions suggested by others, I'd like to suggest a potential solution which doesn't directly answer your question but maybe it solves your actual problem.
Since Scala does have an interpreter that can run code without you having to compile it first, and this code can reference any Java code, if your goal can be summed up as "using Java as a shell scripting language", you could use a .scala file as your starter script (which can include the shebang to be run with scala) from which you can call all your Java classes. This isn't any simpler tha having a bash-based starter script, but it's a good starting point to gradually move to scripting in Scala instead of Java in which case you can get rid of the need to compile to .class file in the first place.
The reason this doesn't work is that Java isn't really an interpreted language, it's partially compiled, partially interpreted.
The .java source code that you'd put the hashbang directive in needs to be compiled to a .class file before the java interpreter can run it. Comments are stripped out by the compiler, so there's no way to push a comment from the .java into the .class file. The .class file is "compiled" output in a specific format, so adding a hashbang directive to the top of it would break the format.
Java isn't really meant to be a scripting language - but some JVM languages are. For example Groovy supports hashbang and so does Clojure.
I am using ANTLRWorks 1.4.3 together with ANTLR 3.4 to generate a Java-based parser and lexer from a grammar file. The generated .java-files contain strings like
C:\\Users\\[path to the eclipse project]\\src\\some\\package\\name\\MyGrammar.g
This absolute path is used as
return string e.g. in method getGrammarFileName() of lexer and parser, and
throughout the both files various times as comment.
I see following disadvantages:
If somebody else with different paths in his development environment will regenerate these files, a lot of changes will be introduced even if no changes in the grammar file were done.
Nobody, especially in an open source project, needs to know where I exactly store my grammar files. E.g., what about C:\\Users\\simon\\customerA\\crap_software\\[rest of the path to grammar file]
Is there a way to control this in ANTLRWorks or ANTLR s.th. at least only relative paths are used?
Finally I found a way to solve my own problem.
Paths seem to depend from where and how you invoke ANTLR. I was not able to achieve this with ANTLRWorks, but using command line ANTLR you are able to perform this. You can do the following (example is for Windows but should be reproducible on other OSes, too):
Download Antlr for command line and copy it to e.g.
C:\Program Files (x86)\ANTLRworks\antlr-3.4-complete.jar.
Open a Windows command line (cmd.exe) and change to the directory where your grammar file is located:
cd C:\Users[path to the eclipse project]\src\some\package\name
Invoke
java -jar "C:\Program Files (x86)\ANTLRworks\antlr-3.4-complete.jar" MyGrammar.g
from commandline.
The generated java files will only contain the name of your grammar file and no path anymore.
Is there a way to control this in ANTLRWorks or ANTLR s.th. at least only relative paths are used?
Short answer
No.
Long(er) answer (Containing highly subjective views! Proceed at own risk)
This is target-specific, but, AFAIK, no target allows you to specify the type (absolute or relative) of the path.
The "no" might be because getGrammarFileName() is only used while debugging generated lexers/parsers. And one should probably not check in generated source files into source control, so no one would ever see the path you see in your generated source file. One ought to check in the grammar, and let developers generate their own lexers/parsers from it.
Again, this is all speculation on my part.
Currently I have been deploying my application as a .jar file, because users of every OS can just double click it. However I now need to increase the max heap size, and the only way to do that is to pass a command-line argument (-Xmx1g) to the JVM. I wish it was possible to include this in the jar manifest, but it's not.
So now I am forced to include a .bat or .csh with the .jar that has the arguments. It seems like there is a better way to do this right? I don't think that Webstart is a good option because the .jar is meant to run in a user's directory where it writes out files. The application is a desktop GUI app.
Unfortunately it is not possible to specify vmargs in the manifest file inside your jar, so you need a workaround like :
Create a script that launch the jar file with specified vm args
Wrap your jar inside an executable that will work as a launcher
Compile your Java code into native binary
The first solution can be easily implement using a batch or an shell script for example, while for the second solution there are several alternatives that can be useful as for example the aforementioned and native Java Web Start and launch4j that is a Cross-platform Java executable wrapper.
The third solution can be implemented in some situations and if your code is compatible with the GNU classpath library, in this case you could compile into native binary using GCJ .
Webstart seems like a good way to achieve that, and if you have the right permissions, nothing prevents you from reading from / writing to the user's file system.
It also provides several interesting "features", including:
the possibility to push upgrades to the user transparently
checking that the user is using the right version of the JRE
In your case, you would just need to use the following syntax:
<j2se version="1.7+" java-vm-args="-Xmx1g" href="http://java.sun.com/products/autodl/j2se"/>
Is there a java sdk for cygwin?
It would be nice if there were a native cygwin implementation which used the cygwin file system and X-windows for display, unfortunately I am not aware of such a release. I would assume it is quite an effort to port OpenJDK as well, but I haven't tried.
Although there is no java sdk for cygwin, you can get the Windows jdk to work if you are willing to accommodate workarounds for various problems:
some cygwin paths are not handled as expected by java programs
the file path separator is backslash rather than slash
the PATH entry separator is semicolon instead of colon
In my experience, the first bullet is by far biggest problem, although the three are somewhat inter-related. The separators tend to take care of themselves as a side-effect of solving the first problem.
All three problems are largely resolved by setting up a development environment in which all file paths of interest (as viewed by java.io.File and java.nio.Path, etc.) can be represented without specifying a drive letter.
As it turns out, it's rarely necessary to use backslashes in a file path string under windows. The only exceptions to this rule that I have encountered are when passing file path strings as parameters when spawning a command line for a program that require backslashes (e.g., CMD.EXE). The java.io and java.nio packages all accept forward slashes, and so, for that matter, do the Microsoft development libraries. Most programs that reject a path with forward slashes are (IMHO) likely to be doing so gratuitously.
So, the real problem is that "/cygdrive/c" is not recognized by java.io.File as referring to "C:\".
In other words, the following file test will return false:
new java.io.File("/cygdrive/c").exists()
whereas this works as expected:
new java.io.File("c:/").exists()
Recent versions of Windows now support general symlinks, providing a way to setup a cygwin development environment in which drive letters are not used. With a unified view of the entire filesystem (with all files appearing below "/", the default drive letter being invariant, e.g., C:) , the drive letter can be discarded from file path strings. In other words, you want to be able to refer to "c:/" as "/". This can be accomplished in various ways, one being to symlink other drives below c:/, for example:
$ ln -sFT d:/ c:/d
If env variable CYGWIN contains "winsymlinks:native", this will create a Windows symlink, with the result that (assuming c: is the default drive) your java program will correctly recognize the string "/d" as referring to "D:\", so you can do this:
new java.io.File("/d").isDirectory // returns true, as expected
If you are unable or reluctant to modify you cygwin environment, there is another approach, which is more universal. You can extend java.io.File and override constructors and various methods to (in effect) to translate cygwin paths to their windows equivalent (like cygpath -m or -w), and to translate windows path strings to a more POSIX-like format. I have such a library (written in scala, but usable from java) and intend to make it available, hopefully sometime soon.