簡體   English   中英

Runtime.exec字符串給出語法錯誤

[英]Runtime.exec String gives syntax error

我正在使用runtime.exec嘗試將.SQL腳本執行到本地具有的SQLite數據庫文件中。 我目前正在MacOS上運行它。

    Runtime runtime = Runtime.getRuntime();
    try
    {
        Process process = runtime.exec("sqlite3 /Users/Documents/Uni/Music\\ Player/src/Common/database.db  < /Users/Documents/Uni/Music\\ Player/src/Common/database.sql");

        BufferedReader stdError = new BufferedReader(newInputStreamReader(process.getErrorStream()));

        try {
            if (process.waitFor() != 0) {
                String s = "";
                System.err.println("exit value = " + process.exitValue());
                System.out.println("Here is the standard error of the command (if any):");
                while ((s = stdError.readLine()) != null) {
                    System.out.println(s);
                }
            }
        } catch (InterruptedException e) {
            System.err.println(e);
        } 

這是我當前正在使用的代碼。 如果我要通過終端執行命令,它將成功執行,並且我的數據庫將使用.SQL腳本的內容進行更新。

但是,當我通過代碼執行此操作時,出現錯誤:

Error: near "Player": syntax error以及進程退出

我一輩子都無法找出String語法出了什么問題。 我嘗試了多種不成功的方法來轉義字符串中的空格。 有任何想法嗎?

如果查看`Runtime.exec()的文檔,並按照實用程序方法進行擴展,則會讀到:

“更准確地說,使用調用新StringTokenizer(command)創建的StringTokenizer將命令字符串分解為令牌,而無需進一步修改字符類別。”

現在,如果您嘗試自己使用StringTokenizer破壞字符串,則會看到它被拆分為:

sqlite3
/Users/Documents/Uni/Music\
Player/src/Common/database.db
<
/Users/Documents/Uni/Music\
Player/src/Common/database.sql

不要將整個命令傳遞給exec ,而應使用exec的版本,該版本接受命令及其參數為String[]

runtime.exec(new String[]{
    "sqlite3",
    " /Users/Documents/Uni/Music Player/src/Common/database.db"
    });

但是,這樣重定向輸入是行不通的。 鍵入命令時,shell會解釋該命令。 除了使用exec ,您可能還想使用通過重定向Java的stdin和stdout運行外部程序中所示的Process

與其嘗試使用手動的硬編碼String生成文件的路徑,不如讓我們創建一個Path並讓它為您生成文件路徑。 它使用起來不那么困難,並且無論生成何種操作系統或文件系統,它都有生成正確文件路徑的好處。

以此替換當前的exec命令以實現該更改:

Path parentDirectory = Paths.get("Users","Documents","Uni","Music Player","src","Common");
String sqlCommand = "sqlite3 database.db < database.sql";        
Process process = runtime.exec(sqlCommand, null, parentDirectory.toFile());

這將使用從父進程(JVM)繼承的環境變量運行“ sqlite3 database.db <database.sql”命令,並從parentDirectory指定的目錄中運行該命令,該目錄恰好是您在問題中提供的目錄。

但是,盡管解決了原始問題,您的代碼也包含與使用文件重定向有關的另一個問題。 通過<>進行的文件重定向是一個shell構造,在shell外部無法使用。 使用Runtime.exec()基本上就像在Windows上使用Run... GUI一樣,由於文件重定向在那里不起作用,因此在這里將不起作用。 您將需要修復用於解決此問題的命令。 幸運的是,這里有一個StackOverflow 問題可以解決這個確切的問題

這是已修復兩個問題的代碼:

Path parentDirectory = Paths.get("Users","Documents","Uni","Music Player","src","Common");
String sqlCommand = "sqlite3 database.db"; 
File inputSqlFile = new File("database.sql");        

Process process = new ProcessBuilder()
        .directory(parentDirectory.toFile())
        .command(sqlCommand)
        .redirectInput(Redirect.from(inputSqlFile))
        .start();

此代碼使用ProcessBuilder將目錄設置為與以前相同的父目錄,但是已修改命令以刪除文件重定向,而是使用redirectinput()方法實現文件重定向。 如果在外殼程序中執行此操作,將完成與"sqlite3 database.db < database.sql"相同的操作,但是此代碼將在Java中運行。


編輯:

上面的代碼無法正常工作,因此我嘗試對其進行修復,並添加了調試語句以幫助查找問題:

Path databasePath = FileSystems.getDefault().getPath("Users", "Documents", "Uni", "Music Player", "src", "Common", "database.db");
Path sqlPath = FileSystems.getDefault().getPath("Users", "Documents", "Uni", "Music Player", "src", "Common", "database.sql");

File database = databasePath.toFile().getAbsoluteFile();
File sqlFile = sqlPath.toFile().getAbsoluteFile();

System.out.println("Database location:\n\t" + database.getCanonicalPath());
System.out.println("SQL file location:\n\t" + sqlFile.getCanonicalPath());

assert database.exists() && database.isFile() && database.canRead() && database.canWrite(): "No such database file!";
assert sqlFile.exists() && sqlFile.isFile() && database.canRead() : "No such SQL file!";

String[] sqlCommand = {"sqlite3", database.getCanonicalPath()};
Redirect sqlInput = Redirect.from(sqlFile);
File workingDirectory = database.getParentFile().getCanonicalFile();

System.out.println("Running this command:\n\t" +
sqlCommand[0] + sqlCommand[1] + "<" + sqlInput.file().getCanonicalPath());

Process process = new ProcessBuilder()
        .directory(workingDirectory)
        .command(sqlCommand)
        .redirectInput(sqlInput)
        .start();

這是除去調試代碼並稍作清除的相同代碼:

File database = FileSystems.getDefault()
    .getPath("Users", "Documents", "Uni","Music Player", "src", "Common", "database.db")
    .toFile().getAbsoluteFile();
File sqlFile = FileSystems.getDefault()
    .getPath("Users", "Documents", "Uni","Music Player", "src", "Common", "database.sql")
    .toFile().getAbsoluteFile();

String[] sqlCommand = {"sqlite3", database.getCanonicalPath()};
Redirect sqlInput = Redirect.from(sqlFile);
File workingDirectory = database.getParentFile().getCanonicalFile();

Process process = new ProcessBuilder()
        .directory(workingDirectory)
        .command(sqlCommand)
        .redirectInput(sqlInput)
        .start();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM