Java开发网 Java开发网
注册 | 登录 | 帮助 | 搜索 | 排行榜 | 发帖统计  

您没有登录

» Java开发网 » 技术文章库  

按打印兼容模式打印这个话题 打印话题    把这个话题寄给朋友 寄给朋友    该主题的所有更新都将Email到你的邮箱 订阅主题
flat modethreaded modego to previous topicgo to next topicgo to back
作者 USING RUNTIME.EXEC TO INVOKE CHILD PROCESSES
rainman

阿熊

元老


发贴: 5644
积分: 454
于 2003-03-07 15:09 user profilesend a private message to usersearch all posts byselect and copy to clipboard. 
ie only, sorry for netscape users:-)add this post to my favorite list
From: http://developer.java.sun.com/developer/JDCTechTips/2003/tt0304.html#1

USING RUNTIME.EXEC TO INVOKE CHILD PROCESSES
Suppose that you are writing a Java application, and you need to obtain the contents of a directory, that is, all the files and subdirectories found within the directory.

You're running on a UNIX system, and you're familiar with the ls command. So you decide to invoke ls from within your Java code. This tip illustrates how to do this, and it also describes why it's probably a bad idea. A command like ls is not portable. Even if the command was portable, there is a simple replacement that uses only Java library features, and thus works across multiple platforms.

Here is what the code looks like:

import java.io.*;

public class ExecDemo1 {
static void listDirContents1(String dir)
throws IOException {

// start the ls command running

Runtime runtime = Runtime.getRuntime();
Process proc = runtime.exec(
"ls" + " " + dir);

// put a BufferedReader on the ls output

InputStream inputstream =
proc.getInputStream();
InputStreamReader inputstreamreader =
new InputStreamReader(inputstream);
BufferedReader bufferedreader =
new BufferedReader(inputstreamreader);

// read the ls output

String line;
while ((line = bufferedreader.readLine())
!= null) {
System.out.println(line);
}

// check for ls failure

try {
if (proc.waitFor() != 0) {
System.err.println("exit value = " +
proc.exitValue());
}
}
catch (InterruptedException e) {
System.err.println(e);
}
}

static void listDirContents2(String dir) {

// create File object for directory
// and then list directory contents

String[] list = new File(dir).list();
int len = (list == null ? 0 : list.length);
for (int i = 0; i < len; i++) {
System.out.println(list[i]);
}
}

public static void main(String[] args)
throws IOException {
if (args.length != 1) {
System.out.println(
"missing directory");
System.exit(1);
}
listDirContents1(args[0]);
listDirContents2(args[0]);
}
}

When you run the ExecDemo1 program, specify a directory, for example:

java ExecDemo1 /home/techtips

Your results should list the contents of the directory, perhaps something like this:

AttrDemo1.java
AttrDemo2.java
AttrDemo3.java
AttrDemo4.java
AttrDemo5.java
ExecDemo1.class
ExecDemo1.java
ExecDemo2.java
prev
prev2
x
.
.
.
(repeat all of above)

The list is repeated because two methods that produce the file list are used. Each method illustrates a different approach. The first file list is produced by listDirContents1. The repetition is produced by listDirContents2.

Unfortunately, this program will work only on UNIX or Linux systems, or on systems that emulate UNIX with tools such as the MKS Toolkit available on Windows. That's because of the dependency the program has on the ls command.

Runtime.exec is used to create a Process object (Process is abstract, so actually an object of a Process subclass is created). The process runs the ls command. Note that no pathname is specified for ls. The Java system apparently observes the local PATH environment variable, although this is not mentioned in the documentation. Giving a complete path is safer, though it assumes that the ls command can be found in the same place on every system where you run your application. The previous example assumes that the ls command is found in the PATH, typically in a directory such as /bin or /usr/bin (the same assumption is made for the next example which uses the wc command). This issue is an example of the kinds of dependencies you must sort through when using Runtime.exec.

In the ExecDemo1 example, the argument to Runtime.exec is a string. There are other forms of the exec method that are available. For example, one form takes an array of string arguments, and another supports the setting of environment variables. If a string is specified as an argument, it is broken into individual tokens using StringTokenizer.

Input to and output from the child process are managed through streams, available through the Process object. For example, the standard output of the child process is treated as an input stream. This stream is read in the parent in order to obtain the output from the child process.

The waitFor method is used to wait for child process completion, and to obtain the exit status of the process. By convention, a status of 0 indicates success.

The listDirContents2 method shows another way to list directory contents. This approach uses only Java library features. It uses a lot less code, and is much more portable. So listDirContents1 cannot be recommended as a useful approach.

However, suppose that after you've studied the example above, you are still convinced that invoking ls is a good idea, and because your application runs only on UNIX and Windows systems, you think that with a slight tweak, you can make this code portable. Specifically, you decide to somehow detect what kind of system you're on. If it's a Windows system, you'll use the "dir" command to obtain a directory listing.

Unfortunately, this sort of straightforward substitution won't work. On at least some Windows systems, such as Windows 2000, dir is not an executable command. Instead it's built into the command processor or shell. So in doing the substitution for ls, you need to say:

cmd /c dir

which means "invoke the command processor and execute a single command (dir) and then exit the command processor". On both UNIX and Windows systems, many commands are shell built-ins, and so this technique is required.

Let's look at another example to illustrate these ideas:

import java.io.IOException;

public class ExecDemo2 {

static void doExec1() throws IOException {

// use pipes and I/O redirection
// but without using a shell

Runtime runtime = Runtime.getRuntime();
runtime.exec("ls | wc >out1");
}

static void doExec2() throws IOException {

// invoke a shell and give command to it

Runtime runtime = Runtime.getRuntime();
String[] args =
new String[]{"sh", "-c", "ls | wc >out2"};

Process p = runtime.exec(args);
}

public static void main(String[] args) throws IOException {
doExec1();
doExec2();
}
}

In this ExecDemo2 program, the command to be executed is:

ls | wc >out1

This is UNIX shell syntax which says "run the ls command, direct its output to the wc command, and direct the output of the wc command to a file out1". The wc command counts the numbers of lines, words, and characters in its input.

When you run ExecDemo2, out1 is not created, and doExec1 fails. However doExec2 succeeds, and the contents of out2 are something like this:

13 13 168

The problem with doExec1 is that Runtime.exec invokes actual executable binary programs. Syntax such as | and > are part of a particular command processor, and are only understood by that processor. ls is executed, but the rest of the shell command is not. However, doExec2 succeeds because it invokes a command processor or shell (sh), and gives the shell the input containing the | and > characters. Note that Runtime.exec could have been invoked with a single string argument like this:

"sh -c 'ls | wc >out2'"

However StringTokenizer will split this string apart without observing the single inner quotes.

These examples serve to illustrate the type of issues you will encounter if you use Runtime.exec. It's a good idea to ask yourself some hard questions before you start using this feature. In particular, you need to determine whether there's a way to accomplish the same end using standard Java features only.

Runtime.exec makes the most sense when you're invoking a large, complex application such as a word processor or web browser, that is, an application that isn't feasible to implement using Java features.

For more information about using Runtime.exec, see section 18.2, Creating Processes, in "The JavaTM Programming Language Third Edition" by Arnold, Gosling, and Holmes.

back to top



PROGRAMMING WITH FILE ATTRIBUTES
A file attribute is a characteristic of a file, for example its read and write permissions, its length, or whether it is a directory. The java.io.File class supports setting and querying file attributes. This tip looks at some of these attributes.

Java file attributes are a subset of what is available on a particular system like UNIX or Windows. There are some attributes that an underlying system such as UNIX supports that the Java system does not. At the same time, using the attributes made available in the File class means that your applications are more portable than if you use underlying facilities.

Another general point about Java file attributes concerns security. The techniques illustrated in this tip might be under the control of a security manager (assuming one is installed). This means that you might not be allowed to use the techniques. For example, in order to directly set the modification time on a file, your program must be able to pass the security checks of the security manager's checkWrite method.

The first type of attribute is one you might consider obvious: the name of a file. But there are a couple of interesting things to consider here. Let's look at an example:

import java.io.*;

public class AttrDemo1 {
public static void main(String[] args)
throws IOException {
File testfile = new File("." +
File.separatorChar + "testfile1");
testfile.createNewFile();

System.out.println(
"name = " + testfile.getName());

System.out.println(
"path = " + testfile.getPath());

System.out.println("absolute path = " +
testfile.getAbsolutePath());

System.out.println("canonical path = " +
testfile.getCanonicalPath());
}
}

The AttrDemo1 program creates a file testfile1 with a contrived pathname. The pathname has a ./ or .\ in front of the name, to refer to the current directory. A single dot (".") refers to the current directory on UNIX and Windows systems, but isn't necessarily supported on other systems. But this particular example assumes that a file pathname makes use of this convention.

If you run the program on a Windows systems, the output should look something like this:

name = testfile1
path = .\testfile1
absolute path = G:\JOB\work\.\testfile1
canonical path = G:\JOB\work\testfile1

The first line of output is the result of calling File.getName, and is the last name in a file's pathname. The second line comes from the File.getPath method, and is the pathname of the file. The pathname is a series of directories with separators like / or \ between them, ending with a final name. File.pathSeparatorChar specifies the separator character for a given system.

The third and fourth lines are absolute pathnames. These are typically determined by resolving against a current working directory. In this example, the current directory is G:\JOB\work.

Every pathname that specifies an existing directory or file has a unique canonical form. The canonical form is typically derived by eliminating . and .. in the pathname, resolving symbolic links, and converting drive letters (in Windows) to a standard case. In the AttrDemo1 example, the redundant \.\ in the pathname is converted to \.

Let's look at another kind of attribute: file modification times. Here's an example:

import java.io.*;
import java.util.*;

public class AttrDemo2 {
public static void main(String[] args)
throws IOException {
File testfile = new File("testfile2");
testfile.delete();
testfile.createNewFile();

long modtime = testfile.lastModified();
System.out.println(
"last modification time #1 = " +
new Date(modtime));

testfile.setLastModified(0);
modtime = testfile.lastModified();
System.out.println(
"last modification time #2 = " +
new Date(modtime));
}
}

The output looks something like this:

last modification time #1 = Wed Feb 12 14:05:34 MST 2003
last modification time #2 = Wed Dec 31 17:00:00 MST 1969

The lastModified method returns the time in standard Java format. This is the number of milliseconds since 1/1/1970 0000 GMT. The second line of output above shows the result of setting the time to 0. The result references the year 1969, because MST (Mountain Standard Time) has a -0700 correction to GMT.

The File.length method is used to find the length of a file. Normally you might not think of file length as an attribute you can set, but it is possible using RandomAccessFile. Here's some code that shows how to do this:

import java.io.*;

public class AttrDemo3 {
public static void main(String[] args)
throws IOException {
File testfile = new File("testfile3");
testfile.delete();
testfile.createNewFile();

System.out.println(
"length #1 = " + testfile.length());

RandomAccessFile raf =
new RandomAccessFile(
"testfile3", "rw");
raf.setLength(100);
raf.close();

System.out.println("length #2 = " +
testfile.length());
}
}

The output is:

length #1 = 0
length #2 = 100

This approach might be useful in a case where you want to preallocate a certain number of fixed-length records in a file.

Another area that's important is read and write permissions on a file. For example, you might want to mark a file as being read-only, so that it cannot be written. Here's an example that illustrates this point:

import java.io.*;

public class AttrDemo4 {
public static void main(String[] args)
throws IOException {
File testfile = new File("testfile4");
testfile.delete();
testfile.createNewFile();

if (testfile.canRead()) {
System.out.println(
"file can be read #1");
}
if (testfile.canWrite()) {
System.out.println(
"file can be written #1");
}

testfile.setReadOnly();

if (testfile.canRead()) {
System.out.println(
"file can be read #2");
}
if (testfile.canWrite()) {
System.out.println(
"file can be written #2");
}
}
}

The output is:

file can be read #1
file can be written #1
file can be read #2

The program creates a file. Initially it's possible to both read and write the file. Then the program makes the file read-only. The canRead method returns true, but canWrite returns false.

There is also a File.isHidden method, to check whether a file has its hidden attribute set. For UNIX systems, a file is considered hidden if its name starts with a dot ("."). On Windows systems, there is a specific Windows file system attribute that you can set to mark a file as hidden.

Let's look at a final example. A common programming task is to go through a file directory structure on disk and enumerate all the files found in that structure. One attribute that hasn't been discussed is the contents of a directory. If a File object refers to a directory, then that directory has entries in it, that is, names of other directories and files. It's possible to recursively go through these entries and make a list of all the pathnames that are found.

Here's what the code looks like:

import java.io.*;
import java.util.*;

class DirTreeWalker {
private List filelist = new ArrayList();

private void getFiles(File file)
throws IOException {
if (file.isDirectory()) {
String[] entries = file.list();
int maxlen = (entries ==
null ? 0 : entries.length);
for (int i = 0; i < maxlen; i++) {
getFiles(
new File(file, entries[i]));
}
}
else if (file.isFile()) {
filelist.add(
file.getCanonicalPath());
}
}

public DirTreeWalker(String filename)
throws IOException {
getFiles(new File(filename));
}

public List getList() {
return filelist;
}
}

public class AttrDemo5 {
public static void main(String[] args)
throws IOException {
if (args.length != 1) {
System.err.println(
"missing argument");
System.exit(1);
}

DirTreeWalker dtw =
new DirTreeWalker(args[0]);
List list = dtw.getList();

for (int i = 0, size = list.size();
i < size; i++) {
System.out.println(list.get(i));
}
}
}

A typical invocation of this program is:

java AttrDemo5 /techtips

for UNIX systems, and

java AttrDemo5 C:\

for Windows systems. The program displays all the file pathnames under the specified starting directory. Here are a few typical lines of output:

C:\arcldr.exe
C:\arcsetup.exe
C:\AUTOEXEC.BAT
C:\boot.ini

In this program, the getFiles method is called with a File object. If the File object refers to a plain file, its canonical path is added to the ArrayList list. If it's a directory, then File.list is called to get the contents of the directory. For each entry in the directory, a new File object is constructed based on the current path passed in to getFiles and the entry in the directory. Then getFiles is called recursively with this object.

You should not assume that if isDirectory is false, then isFile is true, and vice versa. If the File object references a nonexistent file, neither is true. Also, it's worth noting that the directory attribute of a file is not something you can directly set -- it's set for you by virtue of creating a directory using File.mkdir.

For more information about programming with file attributes, see section 15.6.3, The File Class, in "The JavaTM Programming Language Third Edition" by Arnold, Gosling, and Holmes.


rainman edited on 2003-03-07 15:13



话题树型展开
人气 标题 作者 字数 发贴时间
8947 USING RUNTIME.EXEC TO INVOKE CHILD PROCESSES rainman 20327 2003-03-07 15:09

flat modethreaded modego to previous topicgo to next topicgo to back
  已读帖子
  新的帖子
  被删除的帖子
Jump to the top of page

   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2021 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号-1
客服电话 18559299278    客服信箱 714923@qq.com    客服QQ 714923