views:

2250

answers:

13

I believe people still write a lot of console applications, including interactive ones, especially small utilities and administrative interfaces. Mostly for sake of simplicity.

Do you write interactive console applications? Do you think it should be even easier than already is?

U: for clarity, let's define 'interactive console application' an any application running in terminal listening to user's commands. I.e. some command shell.

Java and .NET suggestions especially welcome.

I do have a very simple solution: cliche.sourceforge.net.

package asg.cliche.sample;

import asg.cliche.Command;
import asg.cliche.ShellFactory;
import java.io.IOException;

public class HelloWorld {

    @Command // One,
    public String hello() {
        return "Hello, World!";
    }

    @Command // two,
    public int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) throws IOException {
        ShellFactory.createConsoleShell("hello", "", new HelloWorld())
            .commandLoop(); // and three.
    }
}

Three additional lines of code aren't bad, I think...

A: 

Depends what you define "interactive". I like to write programs I run in the terminal. You don't always need a GUI.

Sometimes it's just a little program that automatically converts data it finds in a given directory. Just a tool, quick and dirty.

stesch
+2  A: 

For *nix (Linux, etc.) take a look at ncurses library.

I wrote a few application using it in the past few years. Some small utility applications that need to run on everything from 486 to Quad Core. And you also get a benefit when using it over slow network, since there is no need to load the entire graphic screen.

Milan Babuškov
There are also curses-for-windows libraries that allow the applications to port to windows as well.
ConcernedOfTunbridgeWells
+5  A: 

In respect to Java, creating interactive console applications have gotten a whole lot easier with the introduction of the Scanner class in Java 5.

When I first learned Java in a class room, interactivity in Java automatically meant either an applet or a GUI application using AWT or Swing. Today, when taking a look at beginner tutorials, I've found that they have used the Scanner class to perform console input. There is a section on Scanning in The Java Tutorial as well.

That said, I haven't had an opportunity to make interactive console applications using Java, but I do find that console applications is a very easy way to produce an application that don't require the use of a graphical user interface. In addition, programming for the console tends to be much easier, as it eliminates the need to have to have code to produce the GUI itself. In trivial programs, there may actually be more code for the GUI than the main functionality of the application itself.

In addition, just as a personal preferences, I like the idea of interactive console applications, as I first started programming back in DOS, so there was no GUI in the first place.

coobird
+1  A: 

I don't very often write interactive console applications. In general, I find that when I'm writing a standalone application or script, it's to help perform some step in the development process at work. As such, non-interactive applications tend to be more useful to me, as they can be used to automate the step, by calling the application from a post-build step or an ant task or whatever's needed.

That being said, I still think there's a place for interactive console applications, and in fact there's nothing keeping my applications from also being interactive - I just tend not to bother, although earlier this week I did find myself thinking about extending a small utility to be interactive, probably using the Python cmd module, which I've used before and found to be helpful.

Blair Conrad
+1  A: 
  • Whenever I write an application that will ultimately run as a service on a server, I write a thin console wrapper around it, with commands to start, stop, interrogate data and so on. Being able to easily start and interact with the prototype service makes testing and debugging much more straightforward.

  • If I'm writing a tool for myself or other developers to use then interactive console applications are often the way to go.

For Java console applications, the Java Curses project is useful.

Dan Vinton
+1  A: 

I prefer, for flexibility, that any input to my console apps comes through stdin. I can still operate on files, by simply cat-ing them through a pipe. This has the added bonus of making the application interactive (though that is in general only useful on text-input). Not very user friendly though, as I generally don't bother adding too much feedback. Like Blair Conrad, I just tend not to bother. There would be nothing wrong with expanding them.

I also like building bigger things as a set of command line tools, using pipes for communication. Ever since I fell in love with the Unix Way back in the 90s.

Pianosaurus
+4  A: 

I write console apps, but not interactive. I find that console apps are better when done in old *nix tradition of using lots of "--" flags. This way you (and your user) can combine them, reuse them, and whatnot. Just saying.

Yoni Roit
+2  A: 

In addition to the Scanner class introduced in Java 5, there's a new Console class introduced in Java 6. You can get the current Console using System.console().

R. Bemrose
+3  A: 

Additional info for dotNet-Mono. You can create interactive console applications in Mono by using MonoCurses

macropas
To bad I can't get this working on .NET
TimothyP
+3  A: 

(This is for C#, but applies for Java too)

The Command pattern is how I've implemented them before, with a while(text != "exit").

It definitely makes it more manageable by having one class per command, and then some kind of "dispatcher" or Factory style class that parses the input text and creates the appropriate class. You can do this with a bog standard Factory or an AbstractFactory, plugins and reflection if you have a big console application.

Each command class implements a simple interface with a

string ProcessCommand(string[] args)
{

}

The string it returns is the message to display back to the client. Of course this limits each command to only having 1 interaction instead of a chain. You can work around that by holding a state/context object too:

string ProcessCommand(string[] args,Context context)
{

}

So you might type:

set connection some-server
set password mypassword
show users

And the connection and password are stored inside the context object, either as a key/value pair or actual properties.

It's not really groundbreaking, but they are quite fulfilling to make.


For parsing commands you can also look at this argument parser, it works quite well and turns the arguments into a dictionary/hashtable.

Chris S
+1  A: 

We have a console application which allows users to execute a series of commands.

The reason we are using a Console application is because it allows for very fast input.

alt text

It sure beats having 50 buttons on a form.

To make life a bit easier, we have some helper classes to read input:

public static class ConsoleReader<T>
{
    public static bool TryReadValue(bool AllowEmptyToTypeDefault, out T value)
    {
        var y = Console.CursorLeft;
        string input = Console.ReadLine();

        if (input.Trim() == String.Empty && AllowEmptyToTypeDefault)
        {
            value = default(T);
            return true;
        }

        try
        {
            value = (T)Convert.ChangeType(input, typeof(T));
            return true;
        }
        catch
        {
            ConsoleReader.ClearInput(input, y);
            value = default(T);
            return false;
        }
    }

    public static bool TryReadValue(out T value)
    {
        return TryReadValue(false, out value);
    }

    public static bool TryReadValueOrEmptyToDefault(T defaultValue, out T value)
    {
        var y = Console.CursorLeft;
        string input = Console.ReadLine();

        if (input.Trim() == String.Empty)
        {
            value = defaultValue;
            return true;
        }

        try
        {
            value = (T)Convert.ChangeType(input, typeof(T));
            return true;
        }
        catch
        {
            ConsoleReader.ClearInput(input, y);
            value = default(T);
            return false;
        }
    }

    public static bool TryReadValueInRange(T minimum, T maximum , out T value)
    {
        return TryReadValueInRange(false, minimum, maximum, out value);
    }

    public static bool TryReadValueInRange(bool AllowEmptyToTypeDefault, T minimum, T maximum, out T value)
    {
        double minimumDouble = 0;
        double maximumDouble = 0;
        try
        {
            minimumDouble = Convert.ToDouble(minimum);
            maximumDouble = Convert.ToDouble(maximum);
        }
        catch
        {
            throw new ArgumentException("The generic type of this instance is not numeric");
        }

        var y = Console.CursorLeft;
        string input = Console.ReadLine();

        if (input.Trim() == string.Empty && AllowEmptyToTypeDefault)
        {
            value = default(T);
            return true;
        }

        try
        {
            value = (T)Convert.ChangeType(input, typeof(T));
        }
        catch
        {
            ConsoleReader.ClearInput(input, y);
            value = default(T);
            return false;
        }

        var valueDouble = Convert.ToDouble(value);
        if (valueDouble >= minimumDouble && valueDouble <= maximumDouble)
        {
            return true;
        }
        else
        {
            ConsoleReader.ClearInput(input, y);
            return false;
        }
    }
}

public static class ConsoleReader
{
    public static bool TryReadString(int minimumLength, int maximumLength, out string value)
    {
        var y = Console.CursorLeft;
        value = Console.ReadLine();

        var result = (value.Length >= minimumLength && value.Length <= maximumLength);

        if (result)
        {
            return true;
        }
        else
        {
            ClearInput(value, y);
            return false;
        } 
    }

    public static bool TryReadString(InputRestriction restriction, int length, out string value)
    {
        if (restriction == InputRestriction.Minimum)
        {
            return TryReadString(length, int.MaxValue, out value);
        }
        else
        {
            return TryReadString(0, length, out value);
        }
    }

    public static bool TryReadNonEmptyString(out string value)
    {
        return TryReadString(1,int.MaxValue, out value);        
    }

    public static string ReadStringOrEmptyToDefault(string defaultValue)
    {
        string input = Console.ReadLine();
        if (input.Trim() == string.Empty)
            input = defaultValue;
        return input;
    }

    public static void ClearInput(string input, int CursorLeftBeforeInput)
    {
        Console.CursorTop--;
        Console.CursorLeft = CursorLeftBeforeInput;
        for (int i = 0; i < input.Length; i++)
            Console.Write(' ');

        Console.CursorLeft = Console.CursorLeft - input.Length;
    }
}

Hope this helps.

TimothyP
+1  A: 

I'll keep it simple, going with your criterion as I understand it:

any application running in terminal listening to user's commands. I.e. some command shell.

I'm a .Net guy (used to be a Java guy, too, but not so much anymore), so mine is a .Net solution.

There are a couple very easy ways with .Net to get input from the user. These work with .Net as well as Mono (I do all my Mono development under OS X, and this stuff all works).

If you wanted to prompt the user for a line of input (a string followed by the user hitting the return key), you could do something as simple as this:

Console.WriteLine("Enter some text, user:");
string userInput = Console.ReadLine();

This would print the line "Enter some text, user:" followed by a newline, so the user's input would appear on the line below "Enter some text, user:".

If you wanted to collect the user's input on the same line as your prompt, you could do this instead (just replace "Console.WriteLine" with "Console.Write" - also note that I put a space at the end of the prompt so the user's input text won't be smashed up against the end of the prompt text):

Console.Write("Enter some text, user: ");
string userInput = Console.ReadLine();

Here, whatever the user types will appear on the same line as "Enter some text, user: ".

If you want to get a single character from the user, you'd use the "ReadKey" method instead. Its return type is System.ConsoleKeyInfo. At its simplest, you can try to cast the return value's "KeyChar" property to a string, but ConsoleKeyInfo can also tell you if any modifier keys (shift, alt, etc.) were pressed, so it's worth reading the docs to understand the flexibility of this method and the ConsoleKeyInfo struct. This is a simple example that just gets what key was pressed without checking for any modifiers:

Console.Write("Press a key, user. G'head. Press one. I dare you.")
ConsoleKeyInfo keyInfo = Console.ReadKey();
string keyString = keyInfo.KeyChar.ToString();

In the above snippet, the key the user presses won't be displayed. If you want the key to be shown, you just call the overload of "ReadKey" that takes a bool - if you pass true, the user's input will be shown:

Console.Write("Press a key, user. G'head. Press one. I dare you.")
ConsoleKeyInfo keyInfo = Console.ReadKey(true); // Here's the difference...
string keyString = keyInfo.KeyChar.ToString();

For more info on ConsoleKeyInfo, such as how to check for modifiers, just follow the link I posted a couple paragraphs back.

I hope this is enough to help you get started. Apologies for not knowing the Java equivalents.

I also recommend that you read the docs for System.Console - there's a lot of sample code. Some of the code is only so-so (not as clear as it ought to be, in my opinion), but there's plenty of info :)

Rory Blyth
A: 

Q.

Do you write interactive console applications?

A.

No

OscarRyz