Рабочий каталог при запуске в eclipse, cmd и jar

1

Я пишу программу Java, которая будет выполнять внешний файл ~/Java/exampleProject/bin/import.sh. Моя программа находится в пакете gqqnbig. Таким образом, структура каталогов

exampleProject/
  bin/
    import.sh
    gqqnbig/
      *.class

Когда я отлаживаю программу в eclipse, рабочий каталог ~/Java/exampleProject/. Мне нужно выполнить bin/import.sh.

Когда я запускаю программу в cmd, текущий каталог ~/Java/exampleProject/bin, мой код не найдет import.sh.

Программа должна быть переносимой (распространять с import.sh). При правильной структуре каталогов она должна работать как на моем компьютере, так и на вашем компьютере, поэтому я не могу жестко закодировать путь import.sh.

Я также хочу упаковать его в один файл jar. Желаемая структура (рис. 1)

bin/
    import.sh
    program.jar

Итак, как моя программа может найти import.sh при запуске в eclipse, cmd и jar?

ОБНОВИТЬ

Я задаю свой вопрос по-другому. getAbsolutePath функцию getAbsolutePath, чтобы не работать в eclipse, в cmd или в файле jar в папке, которая также имеет import.sh (см. Рис. 1), результат идентичен.

public static void main(String[] args)
{
    System.out.println("Look for "+getAbsolutePath()+"\\import.sh");
}
Теги:

3 ответа

3
Лучший ответ

Здесь метод вытащил из одного из моих проектов. Он получает папку, в которой находится файл jar, в отличие от каталога, если он был запущен, если он вызывается в командной строке.

/**
 * Retrieve a File representation of the folder this application is
 * located in.
 * 
 * @return
 */
private static File getApplicationRootFolder()
{
    String path = FileGetter.class.getProtectionDomain().getCodeSource()
            .getLocation().getPath();
    try
    {
        String decodedPath = URLDecoder.decode(path, "UTF-8");
        File jarParentFolder = new File(decodedPath).getParentFile();
        if (jarParentFolder.exists() && jarParentFolder.canRead()
        {
            File shellScript = new File(jarParentFolder, "import.sh")
    }
    catch (UnsupportedEncodingException e)
    {
        Main.myLog.error(TAG, "Unencoding jar path failed on:\n\t" + path);
        e.printStackTrace();
        return null;
    }
}

Затем вы можете использовать этот каталог для создания объекта File для вашего сценария оболочки. File shellScript = new File(getApplicationRootFolder(), scriptFilename);


EDIT: последующие вопросы, чтобы попытаться помочь вам и решение

Таким образом, вы хотите иметь доступ к одному файлу, который имеет три местоположения, в зависимости от того, когда/где вы запускаете код. Вот как я вижу эти случаи:

Случай 1: Выполняется непосредственно из Eclipse (неупакованный код):

shell script:    X:/Java/exampleProject/bin/import.sh
class file:      X:/Java/exampleProject/bin/gqqnbig/YourClass.class

Случай 2: Запуск упакованной банки (сценарий оболочки внутри):

shell script:    X:/Java/YourJar.jar/bin/import.sh
class file:      X:/Java/YourJar.jar/bin/gqqnbig/YourClass.class

Случай 3: Запуск упакованной банки (внешний скрипт оболочки):

shell script:    X:/Java/import.sh
class file:      X:/Java/YourJar.jar/bin/gqqnbig/YourClass.class

Я думаю, что вам нужно сделать так, чтобы определить порядок, в котором вы смотрите на эти места, и вернуться к следующему в строке, если сценарий оболочки не найден. Я думаю, вы хотите:

1. external to jar
2. inside packaged jar
3. unpackaged

Поэтому для доступа к ним вам нужно будет писать каждый отдельно и перемещаться по каждому, пока не получите File.exists() == true.

Что-то вроде того, что следует. Примечание. Я не тестировал это, и возможны ошибки. Я оставлю вас разобраться с ними. Мой код основан на предположениях, сделанных выше, и я оставлю вас изменить код на основе любых неправильных догадок.

Итак, вот класс с одним открытым методом, принимающим аргумент имени файла и возвращающий InputStream. Я выбрал InputStream во всех случаях, так как как только вы упаковываете свою банку, вы больше не можете обращаться к ресурсам как к файловым объектам, а только к потокам.

public class FileGetter
{

    private static String RESOURCE_DIRECTORY = "bin";

    /**
     * Retrieve an InputStream for a resource file.
     * 
     * @param filename
     * @return
     */
    public InputStream getResourceFileStream(String filename)
    {
        // this is where you decide your preference or the priority of the locations
        InputStream inputStream = null;

        inputStream = getExternalFile(filename);
        if (inputStream != null)
        {
            return inputStream;
        }
        inputStream = getInternalPackagedFile(filename);
        if (inputStream != null)
        {
            return inputStream;
        }
        inputStream = getInternalUnpackagedFile(filename);
        if (inputStream != null)
        {
            return inputStream;
        }

        // couldn't find the file anywhere so log some error or throw an exception
        return null;
    }

    /**
     * Retrieve an InputStream for a file located outside your Jar
     * 
     * @param filename
     * @return
     */
    private static InputStream getExternalFile(String filename)
    {
        // get the jar absolute location on disk (regardless of current 'working directory')
        String appRootPath = FileGetter.class.getProtectionDomain().getCodeSource()
                .getLocation().getPath();
        try
        {
            String decodedPath = URLDecoder.decode(appRootPath, "UTF-8");
            File jarfile = new File(decodedPath);
            File parentDirectory = jarfile.getParentFile();
            if (testExists(parentDirectory))
            {
                File shellScript = new File(parentDirectory, filename);
                if (testExists(shellScript))
                {
                    return new FileInputStream(shellScript);
                }
            }
        }
        catch (UnsupportedEncodingException e)
        {}
        catch (NullPointerException e)
        {}
        catch (FileNotFoundException e)
        {}
        // if any part fails return null
        return null;
    }

    /**
     * Retrieve an InputStream for a file located inside your Jar.
     * 
     * @param filename
     * @return
     */
    private static InputStream getInternalPackagedFile(String filename)
    {
        // root directory is defined as the jar root so we start with a "/".
        URL resUrl = FileGetter.class.getResource(File.separator + RESOURCE_DIRECTORY
                + File.separator + filename);
        String badPath = resUrl.getPath();
        String goodPath = badPath.substring(badPath.indexOf("!") + 1);
        InputStream input = FileGetter.class.getResourceAsStream(goodPath);
        // returns null if nothing there so just
        return input;
    }

    private static InputStream getInternalUnpackagedFile(String filename)
    {
        // eclipse will 'cd' to the code directory so we use relative paths
        File shellScriptFile = new File(RESOURCE_DIRECTORY + File.separator + filename);
        if (testExists(shellScriptFile))
        {
            try
            {
                InputStream shellScriptStream = new FileInputStream(shellScriptFile);
                if (shellScriptStream != null)
                {
                    return shellScriptStream;
                }
            }
            catch (FileNotFoundException e)
            {}
        }
        return null;
    }

    /**
     * Test that a file exists and can be read.
     * 
     * @param file
     * @return
     */
    private static boolean testExists(File file)
    {
        return file != null && file.exists() && file.canRead();
    }
}

Но при всем том, что лучший способ сортировать это будет гарантировать, что файл существует на диске и создать его, если он не найден. Затем выполните скрипт с диска.

  • 0
    Он не прошел тест CMD. Когда я нахожусь в D:\Source Code\Java\PlayerStatisticReader\bin , ваш метод возвращает D:\Source Code\Java\PlayerStatisticReader .
  • 0
    Ваша банка была в D:\Source Code\Java\PlayerStatisticReader\bin\yourJar.jar ?
Показать ещё 4 комментария
1

Я хотел бы получить окончательный ответ за это сам.

В качестве обходного пути я бы поставил 'import.sh' внутри exampleProject и изменил относительный путь к 'import.sh'.

В теории, которая должна работать внутри Eclipse и как пакетный Jar файл с program.jar и import.sh в том же каталоге.

К сожалению, он не будет работать в командной строке, возможно, кто-то может предложить лучший метод.

-Kaz

  • 0
    Вы также можете взглянуть на это более красноречивое объяснение того, что делает Eclipse при запуске Java-программы; stackoverflow.com/a/5003703/3387644
0

Я составил решение. Вызовите getExecutablePath(), чтобы получить унифицированный путь.

public static File getExecutablePath()
{

    String workingDirectory = System.getProperty("user.dir");

    File binFile = new File(workingDirectory, "bin");
    if (binFile.exists() && (new File(workingDirectory, "src")).exists())
    {
        return binFile;
    }
    else if (isRunningFromJar())
        return getApplicationRootFolder();
    else
        return new File(workingDirectory);

}

public static boolean isRunningFromJar()
{
    String className = SystemHelper.class.getName().replace('.', '/');
    String classJar = SystemHelper.class.getResource("/" + className + ".class").toString();
    return classJar.startsWith("jar:");
}

/**
 * Retrieve a File representation of the folder this application is located in.
 * 
 * @return
 */
private static File getApplicationRootFolder()
{
    try
    {
        String path = SystemHelper.class.getProtectionDomain().getCodeSource().getLocation().getPath();
        String decodedPath;

        decodedPath = URLDecoder.decode(path, "UTF-8");

        File jarfile = new File(decodedPath);
        return jarfile.getParentFile();
    }
    catch (UnsupportedEncodingException e)
    {
        throw new RuntimeException(e);
    }
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню