Alterando o diretório de trabalho atual em Java?

169

Como posso alterar o diretório de trabalho atual de um programa Java? Tudo o que pude encontrar sobre o problema afirma que você simplesmente não pode fazê-lo, mas não acredito que esse seja realmente o caso.

Eu tenho um pedaço de código que abre um arquivo usando um caminho de arquivo relativo codificado no diretório em que ele normalmente é iniciado e eu só quero poder usar esse código em um programa Java diferente sem precisar iniciá-lo de dentro um diretório específico. Parece que você deveria ser capaz de ligar System.setProperty( "user.dir", "/path/to/dir" ), mas até onde eu sei, ligar para essa linha falha silenciosamente e não faz nada.

Eu entenderia se o Java não permitiu que você fizesse isso, se não fosse pelo fato de permitir que você obtenha o diretório de trabalho atual e até mesmo que você abra arquivos usando caminhos de arquivo relativos ...

usuario
fonte
1
Obter e usar informações é diferente de alterá-las. Por exemplo, no Windows, você pode obter facilmente variáveis ​​de ambiente, mas é mais difícil alterá-las (em todo o sistema).
#
1
bugs.java.com/bugdatabase/view_bug.do?bug_id=4045688 declara na seção de avaliação "Desde então, nenhum cliente adicional se apresentou ou foi identificado de outra forma. ..." e a partir de 2018, temos 175.000 pontos de vista desta pergunta :-(
Wolfgang Fahl

Respostas:

146

Não há maneira confiável de fazer isso em Java puro. Definir a user.dirpropriedade via System.setProperty()ou java -Duser.dir=...parece afetar criações subsequentes de Files, mas não por exemplo FileOutputStreams.

O File(String parent, String child)construtor pode ajudar se você criar o caminho do diretório separadamente do caminho do arquivo, permitindo uma troca mais fácil.

Uma alternativa é configurar um script para executar o Java a partir de um diretório diferente ou usar o código nativo JNI, conforme sugerido abaixo .

O bug relevante da Sun foi encerrado em 2008 como "não será corrigido".

Michael Myers
fonte
12
Eu não acho que eu encontrei uma única diferença entre Java e C # que me faz pensar, "esses caras java certeza sabem o que estão fazendo"
Jake
2
Difícil de acreditar que java não tem algum parâmetro "start nesse diretório ..." pelo menos ...
rogerdpack
5
Em defesa de Java (e eu sou um cara do UNIX que deseja esse recurso) ... é uma VM que deve ser independente de detalhes do sistema operacional. O idioma "presente diretório de trabalho" não está disponível em alguns sistemas operacionais.
Tony K.
4
Para ser justo com Java, eles tiveram que fazê-lo primeiro, o C # teve o benefício de poder aprender com seus erros em muitas áreas.
Ryan The Leach
3
@ VolkerSeibt: Em uma investigação mais aprofundada, parece que o user.dir funciona apenas para algumas classes, incluindo a que eu testei no início. new FileOutputStream("foo.txt").close();cria o arquivo no diretório de trabalho original, mesmo que o user.dir seja alterado pelo programa.
Michael Myers
37

Se você executar seu programa legado com o ProcessBuilder , poderá especificar seu diretório de trabalho .

PhiLho
fonte
1
A rota é a rota que eu segui. Consegui executar um executável de um diretório de trabalho diferente com o seguinte: Arquivo WorkingDir = new File ("C: \\ caminho \\ para \\ working \\ dir \\"); ProcessBuilder pBuilder = novo ProcessBuilder ("C: \\ caminho \\ para \\ trabalhando \\ dir \\ executable.exe"); pBuilder.directory (WorkingDir); Processo p = pBuilder.start ();
CatsAndCode
29

Não é uma maneira de fazer isso usando a propriedade do sistema "user.dir". A parte principal a entender é que getAbsoluteFile () deve ser chamado (como mostrado abaixo) ou os caminhos relativos serão resolvidos com o valor padrão "user.dir".

import java.io.*;

public class FileUtils
{
    public static boolean setCurrentDirectory(String directory_name)
    {
        boolean result = false;  // Boolean indicating whether directory was set
        File    directory;       // Desired current working directory

        directory = new File(directory_name).getAbsoluteFile();
        if (directory.exists() || directory.mkdirs())
        {
            result = (System.setProperty("user.dir", directory.getAbsolutePath()) != null);
        }

        return result;
    }

    public static PrintWriter openOutputFile(String file_name)
    {
        PrintWriter output = null;  // File to open for writing

        try
        {
            output = new PrintWriter(new File(file_name).getAbsoluteFile());
        }
        catch (Exception exception) {}

        return output;
    }

    public static void main(String[] args) throws Exception
    {
        FileUtils.openOutputFile("DefaultDirectoryFile.txt");
        FileUtils.setCurrentDirectory("NewCurrentDirectory");
        FileUtils.openOutputFile("CurrentDirectoryFile.txt");
    }
}
Steve K
fonte
3
caminho absoluto é fundamental
HackNone
5
Mas isso não altera o diretório de trabalho atual. Somente o valor de user.dir. O fato de o caminho absoluto se tornar crítico prova isso.
Marquês de Lorne
18

É possível alterar o PWD, usando JNA / JNI para fazer chamadas para libc. Os caras do JRuby têm uma biblioteca java útil para fazer chamadas POSIX chamadas jna-posix. Aqui estão as informações importantes

Você pode ver um exemplo de seu uso aqui (código Clojure, desculpe). Veja a função chdirToRoot

Allen Rohner
fonte
1
Não parece haver uma versão moderna do jna-posix. Garfoi e adicionei um: github.com/pbiggar/jnr-posix . Posso confirmar que posso alterar o PWD com isso.
precisa
Sim. Desculpe, refatorei esse arquivo e esqueci esta resposta vinculada ao arquivo. Fixo.
Allen Rohner
Você pode fazer isso, mas se você também não alterar a user.dirpropriedade do sistema, File.getAbsolutePath()será resolvido user.dir, enquanto o nome do caminho em Arquivo será resolvido no diretório de trabalho do SO.
Morrie
Em relação à pergunta original, usando jnr-posix, como posso alterar o diretório de trabalho atual em Java. De que classe eu tenho que criar uma instância para usar o método chdir? Eu realmente não entendi o exemplo de Clojure dado. Desde já, obrigado.
Sam Saint-Pettersen
12

Como mencionado, você não pode alterar o CWD da JVM, mas se você iniciar outro processo usando Runtime.exec (), poderá usar o método sobrecarregado que permite especificar o diretório de trabalho. Isso não é realmente para executar seu programa Java em outro diretório, mas em muitos casos quando é necessário iniciar outro programa como um script Perl, por exemplo, você pode especificar o diretório de trabalho desse script enquanto mantém inalterado o diretório de trabalho da JVM.

Veja Runtime.exec javadocs

Especificamente,

public Process exec(String[] cmdarray,String[] envp, File dir) throws IOException

onde diré o diretório de trabalho para executar o subprocesso no

Bizmarck
fonte
1
Essa parece ser uma resposta melhor do que a resposta aceita (que começa com "Não há maneira confiável de fazer isso em Java puro"). Existe uma maneira de solicitar que esta resposta seja considerada a resposta aceita?
John
11

Se bem entendi, um programa Java começa com uma cópia das variáveis ​​de ambiente atuais. Quaisquer alterações via System.setProperty(String, String)estão modificando a cópia, não as variáveis ​​de ambiente originais. Não que isso forneça uma razão completa da razão pela qual a Sun escolheu esse comportamento, mas talvez ele mostre um pouco de luz ...

Adam Paynter
fonte
2
Você parece estar misturando variáveis ​​e propriedades do ambiente. O primeiro é herdado do sistema operacional, enquanto o último pode ser definido na linha de comando usando -D. Mas eu concordo que, na JVM, inicie propriedades predefinidas, como user.dircopiadas do sistema operacional e alterá-las posteriormente, não ajuda.
Maaartinus
A alteração user.dirafeta File.getAbsolutePath()e File.getCanonicalPath(), mas não a idéia do diretório de trabalho do sistema operacional, que determina como os nomes de caminho de arquivos são resolvidos ao acessar arquivos.
Morrie
5

O diretório de trabalho é um recurso do sistema operacional (definido quando o processo é iniciado). Por que você simplesmente não passa sua própria propriedade System ( -Dsomeprop=/my/path) e a usa no seu código como pai do seu arquivo:

File f = new File ( System.getProperty("someprop"), myFilename)
raphaëλ
fonte
Como o trecho de código contém um caminho de arquivo codificado e provavelmente usa um construtor codificado que não especifica o diretório pai para o qual trabalhar. Pelo menos, essa é a situação que eu tenho :)
David Mann
4

A coisa mais inteligente / fácil de fazer aqui é alterar seu código para que, em vez de abrir o arquivo, suponha que ele exista no diretório de trabalho atual (suponho que você esteja fazendo algo parecido new File("blah.txt"), apenas construa o caminho para o arquivo).

Permita que o usuário passe no diretório base, leia-o em um arquivo de configuração, volte para user.diroutras propriedades não encontradas, etc. Mas é muito mais fácil melhorar a lógica do seu programa do que mudar a forma como variáveis ​​de ambiente funcionam.

matt b
fonte
2

Eu tentei invocar

String oldDir = System.setProperty("user.dir", currdir.getAbsolutePath());

Parece funcionar. Mas

File myFile = new File("localpath.ext"); InputStream openit = new FileInputStream(myFile);

joga um FileNotFoundExceptionpensamento

myFile.getAbsolutePath()

mostra o caminho correto. Eu li isso . Eu acho que o problema é:

  • Java conhece o diretório atual com a nova configuração.
  • Mas a manipulação de arquivos é feita pelo sistema operacional. Infelizmente, ele não conhece o novo diretório atual definido.

A solução pode ser:

File myFile = new File(System.getPropety("user.dir"), "localpath.ext");

Ele cria um objeto de arquivo como absoluto com o diretório atual que é conhecido pela JVM. Mas esse código deve existir em uma classe usada, ele precisa ser alterado de códigos reutilizados.

~~~~ JcHartmut

Hartmut Schorrig
fonte
"Ele cria um arquivo Object" - sim. Mas ele não cria um arquivo no sistema de arquivos. Você esqueceu de usar file.createNewFile () depois disso.
Gangnus 19/05/19
2

Você pode usar

novo arquivo ("relativo / caminho"). getAbsoluteFile ()

depois de

System.setProperty ("user.dir", "/ some / directory")

System.setProperty("user.dir", "C:/OtherProject");
File file = new File("data/data.csv").getAbsoluteFile();
System.out.println(file.getPath());

Vai imprimir

C:\OtherProject\data\data.csv
javacommons
fonte
1
Observe que esse comportamento foi alterado no Java 11, consulte bugs.openjdk.java.net/browse/JDK-8202127 . Eles não recomendam o usoSystem.setProperty("user.dir", "/some/directory")
Martin
0

A outra resposta possível a esta pergunta pode depender do motivo pelo qual você está abrindo o arquivo. Este é um arquivo de propriedades ou um arquivo que possui alguma configuração relacionada ao seu aplicativo?

Se for esse o caso, considere tentar carregar o arquivo através do carregador de caminho de classe, dessa forma, você poderá carregar qualquer arquivo ao qual Java tenha acesso.

Nathan Feger
fonte
0

Se você executar seus comandos em um shell, poderá escrever algo como "java -cp" e adicionar os diretórios que desejar, separados por ":" se o java não encontrar algo em um diretório, ele tentará encontrá-los nos outros diretórios. é o que eu faço.

lesolorzanov
fonte
0

Você pode alterar o diretório de trabalho real do processo usando JNI ou JNA.

Com o JNI, você pode usar funções nativas para definir o diretório. O método POSIX é chdir(). No Windows, você pode usar SetCurrentDirectory().

Com o JNA, você pode agrupar as funções nativas nos ligadores Java.

Para Windows:

private static interface MyKernel32 extends Library {
    public MyKernel32 INSTANCE = (MyKernel32) Native.loadLibrary("Kernel32", MyKernel32.class);

    /** BOOL SetCurrentDirectory( LPCTSTR lpPathName ); */
    int SetCurrentDirectoryW(char[] pathName);
}

Para sistemas POSIX:

private interface MyCLibrary extends Library {
    MyCLibrary INSTANCE = (MyCLibrary) Native.loadLibrary("c", MyCLibrary.class);

    /** int chdir(const char *path); */
    int chdir( String path );
}
Andy Thomas
fonte
-1

Use o FileSystemView

private FileSystemView fileSystemView;
fileSystemView = FileSystemView.getFileSystemView();
currentDirectory = new File(".");
//listing currentDirectory
File[] filesAndDirs = fileSystemView.getFiles(currentDirectory, false);
fileList = new ArrayList<File>();
dirList = new ArrayList<File>();
for (File file : filesAndDirs) {
if (file.isDirectory())
    dirList.add(file);
else
    fileList.add(file);
}
Collections.sort(dirList);
if (!fileSystemView.isFileSystemRoot(currentDirectory))
    dirList.add(0, new File(".."));
Collections.sort(fileList);
//change
currentDirectory = fileSystemView.getParentDirectory(currentDirectory);
Borneq
fonte
import javax.swing.filechooser.FileSystemView;
Borneq
1
Esta resposta não está relacionada à pergunta.
Aaron Digulla