15

Has anybody found a way how to specify the Java line.separator property on VM startup? I was thinking of something like this:

java -Dline.separator="\n"

But this doesn't interprete the "\n" as linefeed character. Any ideas?

Lii
  • 11,553
  • 8
  • 64
  • 88

3 Answers3

17

Try using java -Dline.separator=$'\n'. That should do the trick, at least in bash.

Here is a test-run:

aioobe@r60:~/tmp$ cat Test.java 
public class Test {
    public static void main(String[] args) {
        System.out.println("\"" + System.getProperty("line.separator") + "\"");
    }
}
aioobe@r60:~/tmp$ javac Test.java && java -Dline.separator=$'\n' Test
"
"
aioobe@r60:~/tmp$ 

Note:

The expression $'' uses the Bash feature ANSI-C Quoting. It expands backslash-escaped characters, thus $'\n' produces a line feed (ASCII code 10) character, enclosed in single quotes. See Bash manual, section 3.1.2.4 ANSI-C Quoting.

sleske
  • 81,358
  • 34
  • 189
  • 227
aioobe
  • 413,195
  • 112
  • 811
  • 826
  • 4
    I can definitely see how it could be useful in, for instance, a test-scenario. – aioobe Sep 14 '10 at 13:25
  • Thanks, that solved it from command line. It still doesn't work from within the run config of Eclipse IDE, but that's not important. –  Sep 14 '10 at 14:17
6

To bridge the gap between aioobe and Bozho's answers, I also would advise against setting the line.separator parameter at JVM startup, as this potentially breaks many fundamental assumptions the JVM and library code makes about the environment being run in. For instance, if a library you depend on relies on line.separator in order to store a config file in a cross-platform way, you've just broken that behavior. Yes, it's an edge case, but that makes it all the more nefarious when, years from now, a problem does crop up, and now all your code is dependent on this tweak being in place, while your libraries are (correctly) assuming it isn't.

That said, sometimes these things are out of your control, like when a library relies on line.separator and provides no way for you to override that behavior explicitly. In such a case, you're stuck overriding the value, or something more painful like re-implementing or patching the code manually.

For those limited cases, the it's acceptable to override line.separator, but we've got to follow two rules:

  1. Minimize the scope of the override
  2. Revert the override no matter what

Both of these requirements are well served by AutoCloseable and the try-with-resources syntax, so I've implemented a PropertiesModifier class that cleanly provides both.

/**
 * Class which enables temporary modifications to the System properties,
 * via an AutoCloseable.  Wrap the behavior that needs your modification
 * in a try-with-resources block in order to have your properties
 * apply only to code within that block.  Generally, alternatives
 * such as explicitly passing in the value you need, rather than pulling
 * it from System.getProperties(), should be preferred to using this class.
 */
public class PropertiesModifier  implements AutoCloseable {
  private final String original;

  public PropertiesModifier(String key, String value) {
    this(ImmutableMap.of(key, value));
  }

  public PropertiesModifier(Map<String, String> map) {
    StringWriter sw = new StringWriter();
    try {
      System.getProperties().store(sw, "");
    } catch (IOException e) {
      throw new AssertionError("Impossible with StringWriter", e);
    }
    original = sw.toString();
    for(Map.Entry<String, String> e : map.entrySet()) {
      System.setProperty(e.getKey(), e.getValue());
    }
  }

  @Override
  public void close() {
    Properties set = new Properties();
    try {
      set.load(new StringReader(original));
    } catch (IOException e) {
      throw new AssertionError("Impossible with StringWriter", e);
    }
    System.setProperties(set);
  }
}

My use case was with Files.write(), which is a very convenient method, except it explicitly relies on line.separator. By wrapping the call to Files.write() I can cleanly specify the line separator I want to use, without risking exposing this to any other parts of my application (take note of course, that this still isn't thread-safe).

try(PropertiesModifier pm = new PropertiesModifier("line.separator", "\n")) {
  Files.write(file, ImmutableList.of(line), Charsets.UTF_8);
}
dimo414
  • 47,227
  • 18
  • 148
  • 244
3

I wouldn't do that if I were you. The line-separator is platform specific, and should remain so. If you want to write windows-only or linux-only files, define a UNIX_LINE_SEPARATOR constant somewhere and use it instead.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • 2
    exactly. some things shouldn't be changed. any api that relies on line.separator having a specific value is broken – Sean Patrick Floyd Sep 14 '10 at 13:07
  • 17
    I would appriciate if you consider that other people are not just stupid but have their reasons. Mine is, that a Java program is called from within a shell script. The shell script used stdout of the Java app and processes it further. If you run that script from Cygwin, Java uses Windows linefeeds. However, that causes problems in the script. Hence, I'd like to change Javas default line.separator in case it's run from Cygwin. –  Sep 14 '10 at 13:36
  • 1
    first, you could've shared these details in the question. Second, what is so special about that shell-script that can't be programmed in Java? It doesn't sound resonable to me to have a linux-only java software, that you like to run with Cygwin. Either provide a `.bat` file (like Tomcat, for example), or move the functionality to a Java class. And finally, I don't consider anyone stupid. But people are often inexperienced. – Bozho Sep 14 '10 at 13:43
  • Fair enough. I'm just supporting a co-developer here which deals with some legacy software. Hence, there are not much decisions on my part. I'm just the guy for Java questions. And you are my guys for complicated Java questions. ;-) –  Sep 14 '10 at 14:19