Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass an argument that starts with '@' to jcommander

I am using JCommander(version 1.32) to parse command line arguments passed to a java application(let's call it app). The problem I have is that one of the arguments I need to pass begins with @ and as can be seen here there is a special syntax for @. Thus calling the app with app @arg fails with

Could not read file arg: java.io.FileNotFoundException: arg (No such file or directory).

Reading through this information I tried placing my arguments in a file but apparently the "@ syntax" is recursive thus even when I place @arg in a file my program fails with the same error.

Is there a way to pass an argument that begins with @ to a program that uses jcommander? Is there a way to disable the @ syntax?

like image 947
Ivaylo Strandjev Avatar asked Nov 10 '14 14:11

Ivaylo Strandjev


2 Answers

As a result of the chat discussion about this between OP and myself, we came to the following conclusion:

JCommander calls itself, as part of the way it parses commands - subcommand structures that each have their own parameter definitions.

At the top level it expands the @-parameter and creates a new argument list that includes the contents of the file.

Then, as it calls itself, it parses that argument list again, and therefore expands any parameters beginning with a @ once again.

Luckily, it appears it only does so once, so it is not fully recursive. So the solution for anybody wanting to pass a parameter that begins with @ is to use two indirections. That is, create two files:

file1.txt

@file2.txt

file2.txt

@actualparameter

And then use @file1.txt on the command line itself.

So this is a possible workaround. Personally, I'm not too happy about creating extra files like that, and I'd suggest one of three other solutions:

  1. Use a different command line parser.
  2. Patch the JCommander source so that there is a way to escape the @ mechanism or ask the original author to do so himself.
  3. As a kludge, prepend a character to any parameter that may begin with a @, and then strip that character when I need to use the parameter's value. At least this doesn't create two extra files.

Edit: the authors of Jcommander fixed this issue.

There is a new method, according to the comments to this pull request on Jcommander's github that allows disabling the processing of the @ sign. E.g.

JCommander jc = new JCommander(params);
jc.setExpandAmpersat(false);

The method has been added starting with version 1.54.

like image 181
RealSkeptic Avatar answered Nov 08 '22 08:11

RealSkeptic


Disabling the feature altogether

You can disable the expansion of the @ parameters via:

  • jc.setExpandAmpersat(false) (as noted in this answer)
  • OR , jc.setExpandAtSign(false) (in some versions)

There is also a corresponding option on the builder

Using indirection with a file

If you absolutely rely on @ in your commandline parsing, and you expect that your users need to pass filenames or other parameters that start with @, you could ask them to use indirection using extra files.

E.g. to pass filename @foo to --file, write @foo on a single line of a text file, and pass that file via --file @textfile. The expansion is not recursive.

This doesn't work in all situations. But you could convince yourself this is acceptable.

Escaping arguments starting with @

It should be noted that JCommander only expands @ if they start the argument. Knowing this, and assuming you already have some control over argument parsing in your program, you could leave the JCommander default behavior alone, but ask users to explicitly disable it per-argument, using an escape character prefix .

e.g. You could establish a convention where if a user wishes to pass a filename "@foo" (or say, a twitter handle) on the command line, they can prefix it with a backslash or a space: --file " @foo" or --file "\\@foo" . This will prevent the automatic expansion by JCommander, but then your code needs to detect and strip that extra character before further argument processing.

like image 41
init_js Avatar answered Nov 08 '22 06:11

init_js