java - 用Java执行外部程序会返回“访问被拒绝”或停止执行

标签 java sqlcipher

更新3
进步!我写了一个带三个参数的CMD脚本:
sqlite3cipher.exe的绝对路径
testdb的绝对路径
statements.sql的绝对路径
脚本所做的就是执行命令

path\to\sqlite3cipher.exe path\to\testdb < path\to\statements.sql

这个脚本是从我的java代码中调用的。它就像一个符咒!至少所有文件都在主目录的文件夹中。将文件放在C:\Program Files的子文件夹中不起作用,因此可能存在权限问题,我必须与管理员一起解决。
更新2
我在主目录中创建了一个虚拟sql文件。代码现在应该从中读取sql文件,并将数据库文件写入主目录。
再次在命令中省略cmd /c将读取和写入sql文件的第一行,但在这之后将停止(整个程序的执行将停止)。
执行命令,包括cmd /c读取和写入sql文件的每一行,但一旦达到eof(在这种情况下,当然不会写入任何内容),我将再次收到Access denied消息。
更新1
skarist的注释考虑到以下命令(省略cmd /c,因为它毕竟不是执行exe文件所必需的)
String cmdForRuntime = "\"" + prog.getAbsolutePath() + "\" \"" + fDb.getAbsolutePath() + "\"";

上面写的好像
"C:\Program Files\Apache Software Foundation\bin\sqlite3cipher.exe" "C:\Program Files\Apache Software Foundation\pages\dbdir\testdb"

不会产生任何输出。同时代码停止执行,所以我认为I/O有问题?
问题
为了对数据库进行编码,我想在java的命令行上执行sqlite3cipher.exe
我在各种方法中遇到了一些不同的结果。首先,安装程序(请注意,这是一个测试环境,在该环境中,目前无法更改文件的位置):
sqlite3cipher.exe位于C:\Program Files\Apache Software Foundation\bin\
空文件testdb位于C:\Program Files\Apache Software Foundation\pages\dbdir\
包含sql语句statements.sql的文件与testdb位于同一文件夹中
在命令行上执行sqlite3cipher.exe -help将打印用法。这里有一个练习:
C:\Program Files\Apache Software Foundation\bin\sqlite3cipher.exe [OPTIONS] FILENAME [SQL] 
FILENAME is the name of an SQLite database. A new database is created if the file does not previously exist.

在命令行上执行"C:\Program Files\Apache Software Foundation\bin\sqlite3cipher.exe" "C:\Program Files\Apache Software Foundation\pages\dbdir\testdb" < "C:\Program Files\Apache Software Foundation\pages\dbdir\statements.sql"具有所需的效果:testdb现在是根据通过statements.sql输入的sql语句加密的数据库。
现在在Java中,我的代码如下所示:
String cmdForRuntime = "cmd /C \"" + prog.getAbsolutePath() + "\" \"" + fDb.getAbsolutePath() + "\" < \"" + fSqlScript.getAbsolutePath() + "\"";
Process process = Runtime.getRuntime().exec( cmdForRuntime );

BufferedReader stdIn = new BufferedReader(new InputStreamReader(process.getInputStream()));
String s;

while ((s = stdIn.readLine()) != null) {
    logger.info("OUTPUT FROM PROG: " + s);
}

if (process.waitFor() == 0) {
    logger.info("Process finished");
}
else {
    logger.error("Error!");

    // Get input streams
    BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));

    System.out.println("Standard error: ");
    while ((s = stdError.readLine()) != null) {
        System.out.println(s);
    }
}

其中progfDbfSqlScriptFile类型,分别表示sqlite3cipher.exetestdbstatements.sql
我对Process.execute()不太熟悉,但我从阅读中了解到,在windows(测试服务器正在运行windows 8)上启动命令解释器需要cmd /c作为命令的一部分。
执行时,此代码在命令行(通过进程的流)返回Access denied。在命令行上执行这个精确的命令(包括cmd /c)将直接返回相同的结果:Access denied。即使我以管理员身份启动命令行。
不使用cmd /c命令(因为它以前在命令行上没有工作过),我得到消息说存在too many options
在进一步的调查中,我了解到这个问题是由<操作符引起的,该操作符用于重定向输入。exec()方法不能将<识别为运算符,建议使用进程的OutputStream。所以我把代码改成这样:
String cmdForRuntime = "cmd /c \"" + prog.getAbsolutePath() + "\" \"" + fDb.getAbsolutePath() + "\"";
Process process = Runtime.getRuntime().exec( cmdForRuntime );

BufferedWriter bufferedwriter = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
BufferedReader br = new BufferedReader(new FileReader(fSqlScript.getAbsolutePath()));

BufferedReader stdIn = new BufferedReader(new InputStreamReader(process.getInputStream()));
String s;
String fileInput = br.readLine();

while (fileInput != null) {

    bufferedwriter.write(fileInput);

    while ((s = stdIn.readLine()) != null) {
        logger.info("OUTPUT FROM PROG: " + s);
    }

    fileInput = br.readLine();
}

if (process.waitFor() == 0) {
    logger.info("Process finished");
}
else {
    logger.error("Error!");

    // Get input streams
    BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));

    System.out.println("Standard error: ");
    while ((s = stdError.readLine()) != null) {
        System.out.println(s);
    }
}

据我所知,阅读外部程序输出的内容很重要,这样它就不会阻塞或停止工作。这就是为什么我从statements.sql(通过BufferedReader br)读取一行并将其写入进程的OutputStream。然后我阅读程序将输出的内容(记录输出)。如果没有东西(左)可以阅读,我会从statements.sql中阅读下一行。将重复此操作,直到没有任何内容可从所述文件中读取。
如果我执行此代码,日志记录器将记录以下内容:
INFO  Executing command: cmd /c "C:\Program Files\Apache Software Foundation\bin\sqlite3cipher.exe" "C:\Program Files\Apache Software Foundation\pages\dbdir\testdb"
ERROR Error!
Standard error: 
Access denied

在这种情况下,代码至少会终止,导致一个空文件testdb。在另一种方法中,我在命令中保留了cmd,但跳过了/c,从而产生以下输出:
INFO  Executing command: cmd "C:\Program Files\Apache Software Foundation\bin\sqlite3cipher.exe" "C:\Program Files\Apache Software Foundation\pages\dbdir\testdb"
INFO  OUTPUT FROM PROG: Microsoft Windows [Version 6.3.9600]
INFO  OUTPUT FROM PROG: (c) 2013 Microsoft Corporation. Alle Rechte vorbehalten.
INFO  OUTPUT FROM PROG: 

当您在命令行上键入cmd时得到的输出是相同的。这就是执行停止的地方。
TL;博士
在命令行上直接执行就像一个符咒
从Java执行的完全相同的命令不起作用
cmd /c预先准备的命令(显然应该是这样)和进程的InputStreamOutputStream的用法不起作用:Access denied
我不知道下一步该怎么办,所以我真心希望有人能帮我。

最佳答案

所以我用更新3中提到的解决方法解决了这个问题。
我的java代码现在看起来是这样的(除了cmdForRuntime变量之外,它应该与我问这个问题时的开头代码几乎相同):

try {
        File fDb = new File(this.inputDirectory + File.separator + dbName);

        String cmdForRuntime =  "\"" + cmdScript.getAbsolutePath() + "\"" + " " +       // the script to be executed 
                                "\"" + prog.getAbsolutePath() + "\"" + " " +            // sqlite3cipher.exe
                                "\"" + fDb.getAbsolutePath() + "\"" + " " +             // testdb
                                "\"" + fSqlScript.getAbsolutePath() + "\"";             // statements.sql

        logger.info("Executing command: " + cmdForRuntime );

        Process process = Runtime.getRuntime().exec( cmdForRuntime );

        BufferedReader stdIn = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String s;

        while ((s = stdIn.readLine()) != null) {
            logger.info("OUTPUT FROM PROG: " + s);
        }

        if (process.waitFor() == 0) {
            logger.info("Process finished");
        }
        else {
            logger.error("!!!! error !!!!");
            // Get input streams
            BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));

            System.out.println("Standard error: ");
            while ((s = stdError.readLine()) != null) {
                System.out.println(s);
            }
        }
    }
    catch(Exception ioe) {
        ioe.printStackTrace();
    }       

cmdForRuntime将读到类似的内容(我将省略路径,这只会增加混乱):
"SQLiteCipher.cmd" "sqlite3cipher.exe" "testdb" "statements.sql"

脚本的内容如下:
%1 %2 < %3

这将使用参数sqlite3cipher.exe调用testdb,并从statements.sql调用重定向输入。
关键是脚本的位置和exe的位置不是系统文件夹。
如果所有这些标准都得到满足,那么一切都像一种魅力。

关于java - 用Java执行外部程序会返回“访问被拒绝”或停止执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24675308/

相关文章:

java - 以秒为单位获取日期表示?

java - 长时间运行的 activiti 服务任务

java - 准备语句: executeUpdate insert wrongly the raw

Ubuntu 中不支持 SQLCipher 的 SQLite 浏览器

sqlcipher附加数据库

python - 安装 pysqlcipher3 时出现问题

java - com.example.RedisApplication 中方法存储库的参数 0 需要找不到类型的 bean

java - 如何从java中的请求中获取客户端的浏览器ip?

c# - 为 SqlCipher 数据库存储加密 key 的正确方法

安卓 Proguard SqlCipher NoClassDefFoundError