因为一些原因,想在Java中模拟部分Shell命令的功能,比如cd、pwd、ls等。
- 实现
pwd
很简单,返回System.getProperty("user.dir")
即可; - 实现
ls
也不难,用java.nio.file.Files::walk
还能实现递归遍历目录; - 但在纯Java中实现
cd
——动态的切换当前工作目录——似乎不是那么容易。
调用System.setProperty("user.dir", "xxx")
修改user.dir
后,能影响java.io.File
和java.nio.file.Path
的后续创建,但不会影响java.io.FileOutputStream
、java.lang.Process
等,即写入文件、子进程等当前目录还是进程启动时的目录,不会随系统属性的修改而修改。例如:
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class Application {
public static void main(String[] args) throws Exception {
System.out.println(new File(".").getCanonicalPath());
String home = System.getProperty("user.home");
String target = home.concat("/Desktop");
System.setProperty("user.dir", target);
System.out.println(new File(".").getCanonicalPath());
try (OutputStream fout = new FileOutputStream("a.txt")) {
fout.write("hello world".getBytes());
}
}
}
在任何非桌面目录下执行上述代码,第一行输出当前路径,第二行输出桌面路径,但a.txt不是创建在桌面。
经过Google,在StackOverflow上找到了一个解决方案:通过jna-posix修改当前目录。不过原始答案中的方法有些过时,经过折腾,找到了在Mac下行之有效的方法。细节如下。
一、pom.xml 引入依赖包
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>4.5.2</version>
</dependency>
<dependency>
<groupId>com.github.jnr</groupId>
<artifactId>jnr-posix</artifactId>
<version>3.0.46</version>
</dependency>
二、实现POSIXHandler
import jnr.constants.platform.Errno;
import jnr.posix.POSIXHandler;
import java.io.File;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Map;
import static java.util.stream.Collectors.toList;
public class PosixHandler implements POSIXHandler {
@Override
public void error(Errno error, String extraData) {
System.err.printf("%s %s\n", error, extraData);
}
@Override
public void error(Errno error, String methodName, String extraData) {
System.err.printf("%s %s %s\n", error, methodName, extraData);
}
@Override
public void unimplementedError(String methodName) {
System.err.printf("%s\n", methodName);
}
@Override
public void warn(WARNING_ID id, String message, Object... data) {
System.err.printf("%s %s %s\n", id, message, data);
}
@Override
public boolean isVerbose() {
return false;
}
@Override
public File getCurrentWorkingDirectory() {
return new File(System.getProperty("user.dir"));
}
@Override
public String[] getEnv() {
return System.getenv()
.entrySet()
.stream()
.map(Map.Entry::toString)
.collect(toList())
.toArray(new String[0]);
}
@Override
public InputStream getInputStream() {
return System.in;
}
@Override
public PrintStream getOutputStream() {
return System.out;
}
@Override
public int getPID() {
return 0;
}
@Override
public PrintStream getErrorStream() {
return System.err;
}
}
三、利用Posix对象修改当前路径
import jnr.posix.POSIX;
import jnr.posix.POSIXFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class Application {
public static void main(String[] args) throws Exception {
System.out.println(new File(".").getCanonicalPath());
String home = System.getProperty("user.home");
String target = home.concat("/Desktop");
System.setProperty("user.dir", target);
POSIX posix = POSIXFactory.getPOSIX(new PosixHandler(), true);
posix.chdir(target);
System.out.println(new File(".").getCanonicalPath());
try (OutputStream fout = new FileOutputStream("a.txt")) {
fout.write("hello world".getBytes());
}
}
}
此时,再运行该程序,a.txt会被创建在桌面目录下。