Rogach
Rogach

Reputation: 27190

Encode a String to be used as shell argument

I need to use a name of file as an argument in linux shell command. The problem is, java gives me that name as it is, saving all that spaces and other characters, and thus shell complains. Is there a method to escape all those problematic characters before passing the string to shell?

Upvotes: 6

Views: 6902

Answers (5)

Joseph
Joseph

Reputation: 160

/**
 * According to the syntax of the shell command, the string is escaped by 
 * enclosing it with single quote.
 * 
 * eg. 11'22 ==> '11'\''22'
 *
 * @param addQuote add outer quote or not
 */
public static String escapeShellSingleQuoteString(String s, boolean addOuterQuote) {
    String replace = s.replace("'", "'\\''");
    return addOuterQuote ?  "'" + replace + "'" : replace;
}

/**
 * According to the syntax of the shell command, the string is escaped by
 * enclosing it with double quote.
 * 
 * eg. 11\22"33$44`55 ==> "11\\22\"33\$44\`55"
 *
 * @param addQuote add outer quote or not
 */
public static String escapeShellDoubleQuoteString(String s, boolean addOuterQuote) {
    final List<String> targets = Arrays.asList("\"", "$", "`");
    String escape = escape(s, "\\", targets);
    return addOuterQuote ? '"' + escape + '"' : escape;
}

private static String escape(String s, String escaper, List<String> targets) {
    s = s.replace(escaper, escaper + escaper);
    for (String t : targets) {
        s = s.replace(t, escaper + t);
    }
    return s;
}

Upvotes: 2

Mike Gebirge
Mike Gebirge

Reputation: 602

Had the same problem, single quotes wasn't sufficient (as already pointed out by Robert)

Solution:

import com.google.common.escape.Escaper;
import com.google.common.escape.Escapers;

public class YourFancyClass {
    public static final Escaper SHELL_ESCAPE;
    static {
        final Escapers.Builder builder = Escapers.builder();
        builder.addEscape('\'', "'\"'\"'");
        SHELL_ESCAPE = builder.build();
    }
}

Why such an awfully complex replacement? That's why.

Use case:

System.out.format("ln -f '%s' '%s'%n", 
    SHELL_ESCAPE.escape(anyOrig.toString()),
    SHELL_ESCAPE.escape(duplicate.toString()));

Works as intended:

ln -f '/home/user/Musik/mix-2012-13/aesthesys~ I Am Free, That Is Why I'"'"'m Lost..mp3' '/home/user/Musik/youtube converted/aesthesys~ I Am Free, That Is Why I'"'"'m Lost..mp3'

Upvotes: 12

Friek
Friek

Reputation: 1541

How about using the Exec module from Apache Commons? It includes a commandline builder. Also be aware that if the filename is retrieved from user input, you should be very careful with executing commands with the user input as a program argument. Escaping improperly may lead to execution of additional commands (unless the commons module is used I guess).

Upvotes: 3

Bernhard
Bernhard

Reputation: 8831

Adding quotes single or double around the file name is often sufficient, depending on the characters you have in the name it may not.

Upvotes: -1

Jordan
Jordan

Reputation: 32522

You should be able to put single quotes around the argument and avoid escaping it altogether. Will that work for you?

Old: myapp -f /bad/path/to/file

New: myapp -f '/good/path/to/file'

Upvotes: -5

Related Questions