5

I have a suite of Java programs which are used as command-line tools on our Linux servers. Most of them use a class that prints a progress bar on STDERR, similar to Perl's Term::ProgressBar.

I'd like to have the progress bar shown whenever STDERR is going to the terminal and automatically disable itself when STDERR is redirected so that there aren't all sorts of progress bar pieces in the redirected data.

Checking System.console() == null was my first thought, but redirecting STDOUT is enough to make this true, even if STDERR is still going to the terminal. Is there anything I can check that is specific to STDERR? A solution that is Linux-specific or that uses native APIs would be ok for my needs.

Brad Mace
  • 27,194
  • 17
  • 102
  • 148
  • http://stackoverflow.com/questions/3643939/java-process-with-input-output-stream Enjoy – EvilKittenLord Aug 20 '13 at 15:20
  • @UberDoyle I'm not asking about launching another process using Java. I want to know whether the STDERR of the *current* process is being redirected. – Brad Mace Aug 20 '13 at 15:23

2 Answers2

1

I think what you're looking for is isatty(3), in unistd.h. There's no way to tell whether a file handle has been redirected, period, but that'll tell you whether it's still interactive. See the source for the tty command in GNU coreutils.

chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • Do you know which function in ioctl I should be looking at? Nothing stands out to me after reading the [`ioctl`](http://www.linuxmanpages.com/man2/ioctl.2.php), [`tty_ioctl`](http://www.linuxmanpages.com/man4/tty_ioctl.4.php), and [`console_ioctl`](http://www.linuxmanpages.com/man4/console_ioctl.4.php) man pages. – Brad Mace Aug 20 '13 at 15:41
0

After combining @chrylis's pointer with this answer and doing a little tweaking, what I finally ended up with is:

  1. create and compile Java class with native method signature
  2. use javah to generate C header file
  3. create .cpp file, implementing function with isatty
  4. compile C++ code into shared library
  5. run Java program, using -Djava.library.path=... to tell it where your custom library is

Java class:

package com.example.cli;

class LinuxTerminalSupport {

    public native boolean isStderrVisible();

    static {
        System.loadLibrary("term");
    }
}

ant target to generate .h:

<target name="generate-native-headers">
    <javah destdir="native/" verbose="yes">
        <classpath refid="compile.class.path"/>
        <class name="com.example.cli.LinuxTerminalSupport" />
    </javah>
</target>

.cpp file:

#include "com_example_cli_LinuxTerminalSupport.h"
#include "unistd.h"

using namespace std;

JNIEXPORT jboolean JNICALL Java_com_example_cli_LinuxTerminalSupport_isStderrVisible(JNIEnv * env, jobject obj) {
    return isatty(fileno(stderr)) == 1;
}

Makefile (change java includes to reflect your $JAVA_HOME):

linux: LinuxTerminalSupport.o
    g++ -I/usr/java/jdk1.6.0_13/include -I/usr/java/jdk1.6.0_13/include/linux \
            -o libterm.so -shared -Wl,-soname,term.so LinuxTerminalSupport.o -lc

LinuxTerminalSupport.o: LinuxTerminalSupport.cpp
    g++ -c -I/usr/java/jdk1.6.0_13/include -I/usr/java/jdk1.6.0_13/include/linux LinuxTerminalSupport.cpp
Community
  • 1
  • 1
Brad Mace
  • 27,194
  • 17
  • 102
  • 148
  • You didn't need to create a library for this (see [NOT INVENTED HERE](https://en.wikipedia.org/wiki/Not_invented_here#In_computing)). isatty() is already part of the export table of the glibc binaries and could be found via /lib64/libc.so.6 or /lib/lib.so.6 (Also, please notify @ me as I don't check for answers manually) – user2284570 Nov 25 '13 at 01:28
  • @user2284570 - I *did* use `isatty`. If you know how to use native libraries without the JNI wrapper then you should tell us-- I've never seen any information regarding that. – Brad Mace Nov 25 '13 at 20:22
  • @BradMace user doesn't have any idea what he's talking about. – chrylis -cautiouslyoptimistic- Nov 27 '13 at 07:07