I am new to Java, and installed the following JDK on Windows 10:
C:\Program Files\AdoptOpenJDK\jdk-11.0.8.10-openj9
I can invoke the compiler javac
from Cygwin's Bash command line
(in an x-terminal), but it creates many errors that seem to go to
neither stdout
or stderr
. I need to send them to a file
that I can peruse with Vim.
Here is my invocation command
# mk.bash
# -------
javac \
-classpath "/c/Program Files/.../cplex.jar" \
TestSetup.java
The classpath
argument is irrelevant here, as I
only want to focus on capturing javac
's output in a Vim-perusable
way.
Issuing ./mk.bash >| mk.out
generates an empty mk.out
, as does
./mk.bash 2>&1 >| mk.out
. I've used the latter pattern for decades
to redirect stderr to stdout and overwrite the destination file.
I can use the script
command to send the javac
output to mk.out
:
script mk.out
./mk.bash
exit
I can then browse the error messages using Vim
. However, the
contents are obfuscated by many binary characters (image & link to file below). Normally, I can
clean up messy files with dos2unix
, but on this output, it quits due
to binary characters.
As another way to clean up the non-text content, Vim has a
fileformat=dos
option which can be entered using :e ++ff=dos %
.
The e
and %
says to edit the current file, while ++ff=dos
says
to interpret the file as dos
format (ff
is fileformat
). All
this does is clean up visual artifacts due to the different line
endings in Unix and DOS. All the error messages are still interspersed
with what seem like Escape characters ^[
.
Is there any way to get javac
to generate only plain text
output or to clean up the output?
Here is an image of the non-plain-text file in Vim:
I doubt it is all that relevant, but I'm following this webpage to
compile a simple Java app TestSetup.java
that invokes a 3rd party
tool: https://kunlei.github.io/cplex/cplex-java-setup.
Bottom line up front
The following Bash command sends both stdout
and stderr
from
javac
to both the screen and the file mk.out
:
javac -classpath /Some/NonExistent/Jar/File.jar TestSetup.java \
2>&1 | tee mk.out
How it was found
I found that the problem was primarily the use of Bash piping,
redirection, and the script
command. The latter is responsible for
the ANSI codes. I could not determine this previously because I could
not capture the javac
error messages in a file without script
. So
I first solved that problem, then compared the use of script
with
the absence of script
. Here is what I found.
The following bash command creates a DOS format file without the ANSI codes:
javac -classpath '/Some/NonExistent/File.jar' TestSetup.java \
>| mk.out 2>&1
Before, I had: (1) 2>&1 >| mk.out
instead of (2) >| mk.out 2>&1
. For option (1), I thought stderr
became directed to
stdout
, so I simply needed to direct stdout
to mk.out
.
But that's not how it works. In option (1), the specification 2>&1
directs stderr
to what stdout
is currently directed at, i.e., the
screen. The subsequent >| mk.out
then directs stdout
(and only
std.out
) to mk.out
. Unfortunately, this meant that stderr
was
still going to the screen. Presto, mk.out
is left empty (bad).
In option (2), stdout
is first directed to mk.out
. The subsequent
2>&1
then directs stderr
to what stdout
is directed to, i.e.,
mk.out
. Hence, the javac
error messages get sent to mk.out
.
It did not contain ANSI codes, which is good.
While option (2) works, it is not ideal; the error messages are not
shown on the screen. This is where I fell back to script
and found
that it introduced the ANSI codes:
script \
-c "javac -classpath '/Some/NonExistent/File.jar' TestSetup.java" \
mk.out
Since the above solves the problem of redirecting stderr
, I looked
for bash options that avoid script
. The following sends stderr
to
the screen and to mk.out
:
javac -classpath /Some/NonExistent/Jar/File.jar TestSetup.java \
2>&1 | tee mk.out
According to the explanation of option (1) above, it looks like it
shouldn't work, but Bash makes an exception when it is used in a
pipeline. Specifically, stderr
is redirected to what stdout
is
directed to, but only after all redirections. In a pipeline, the
above means that javac
's stdout
is directed to tee
's stdin
,
and only then is javac
's stderr
directed to the target of
javac
's stdout
, i.e., tee
's stdin
.
For reference: Initial solution (javac
's Xstdout option)
I found that my javac
has a command line option -Xstdout OutputFileName.txt
specifically for this purpose.
Unfortunately, not all javac
documentation that I found online shows availability of this option. In that case, the most direct solution I found was :term cat The/Output/File.Path
in Vim.
Many vim alternatives are also shown on that page, as well as here
For future reference, if needed, 3 fulsome pages that I found to be relevant are:
Mention is made of ansi2txt
, but you need Ubuntu for that.