空字符串解析ntpq命令结果
Empty string parsing ntpq command result
我正在解析执行这个复合命令的结果
ntpq -c peers | awk ' [=10=] ~ /^*/ {print }'
为了获取活动ntp服务器的偏移量。
这是java定期使用和执行的代码
public Double getClockOffset() {
Double localClockOffset = null;
try {
String[] cmd = {"/bin/sh",
"-c",
"ntpq -c peers | awk \' [=11=] ~ /^\*/ {print }\'"};
Process p = Runtime.getRuntime().exec(cmd);
p.waitFor();
BufferedReader buf = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = buf.readLine();
if (!StringUtils.isEmpty(line)) {
localClockOffset = Double.parseDouble(line.trim());
} else {
// Log "NTP -> Empty line - No active servers - Unsynchronized"
}
} catch (Exception e) {
// Log exception
}
return localClockOffset;
}
ntpq 结果示例
> remote refid st t when poll reach delay offset jitter
> ==============================================================================
> *server001s1 .LOCL. 1 u 33 64 377 0.111 -0.017 0.011
> +server002s1 10.30.10.6 2 u 42 64 377 0.106 -0.006 0.027
> +server003s1 10.30.10.6 2 u 13 64 377 0.120 -0.009 0.016
请注意 awk 搜索以“*”开头的第一行并提取其第九列。在示例中:-0.017
问题是有时我在通过控制台执行命令时获取 no-active-servers 日志消息 - 旨在在没有带“*”的服务器时出现 - returns一个数字。
我知道我没有关闭该代码中的 BufferedReader
但这是此行为的原因吗?在每个方法调用中都会创建一个新实例(并在垃圾收集之前保持打开状态),但我认为这不应该是导致此问题的原因。
正如 Andrew Thompson 所指出的,您应该尝试 ProcessBuilder
。
String[] cmd = {"/bin/sh",
"-c",
"ntpq -c peers | awk \' [=10=] ~ /^\*/ {print }\'"};
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true);
Process proc = pb.start();
BufferedReader buf = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
String line = null;
while ((line = buf.readLine()) != null) {
localClockOffset = Double.parseDouble(line.trim());
break;
}
proc.destroy();
Runtime.exec()
只是调用其中的 ProcessBuilder,就像这样:
public Process More ...exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}
所以使用它代替 ProcessBuilder
没有任何问题。
问题是你调用了:
p.waitFor();
在您获得InputStream
之前。
这意味着当您获得 InputStream
时,该进程已经终止,并且输出流数据可能对您可用,也可能不可用,具体取决于 OS缓冲实施的细微差别和精确的操作时间。
因此,如果您将 waitFor()
移至底部,您的代码应该会开始更可靠地工作。
然而,在 Linux 下,您通常应该能够从 PIPE 缓冲区读取剩余数据,即使在写入过程结束后也是如此。
而 OpenJDK 中的 UNIXProcess 实现实际上明确地使用了它,并在进程退出后尝试耗尽剩余数据,以便可以回收文件描述符:
/** Called by the process reaper thread when the process exits. */
synchronized void processExited() {
synchronized (closeLock) {
try {
InputStream in = this.in;
// this stream is closed if and only if: in == null
if (in != null) {
byte[] stragglers = drainInputStream(in);
in.close();
this.in = (stragglers == null) ?
ProcessBuilder.NullInputStream.INSTANCE :
new ByteArrayInputStream(stragglers);
}
} catch (IOException ignored) {}
}
}
这似乎足够可靠,至少在我的测试中是这样,所以很高兴知道 Linux|Unix 和 JRE 的具体版本 运行.
您是否也考虑过应用程序级问题的可能性?
IE。 ntpq
并不能真正保证总是 return 一个 *
行。
所以,最好从管道中删除 awk
部分,看看是否始终会有一些输出。
另一件需要注意的事情是,如果您的 shell 管道步骤之一失败(例如 ntpq 本身),您也会得到一个空输出,因此您还必须跟踪 STDERR(例如通过 ProcessBuilder
).
将其与 STDOUT 合并
旁注
在开始使用数据之前执行 waitFor
在任何情况下都是一个坏主意,因为如果您的外部进程将产生足够的输出来填充管道缓冲区,它就会挂起等待有人读取它,这永远不会发生,因为你的 Java 进程将同时被锁定在 waitFor
。
终于找到问题所在了。
我不会更改已接受的答案,我认为它也很有用,但也许有人可以从我们的经验中学习。
我的 java 程序是用 shell 脚本启动的。当我们手动执行脚本时, ntpq
命令被找到并成功调用。当软件完全部署时就会出现问题。在最终环境中,我们有一个 cron 计划的恶魔,它使我们的程序保持活动状态,但 cron 建立的 PATH
与我们的配置文件已分配的 PATH
不同。
PATH
被 cron 使用:
.:/usr/bin:/bin
PATH
我们已登录以手动启动脚本:
/usr/sbin:/usr/bin:/bin:/sbin:/usr/lib:/usr/lib64:/local/users/nor:
/usr/local/bin:/usr/local/lib:.
通常 ntpq
在
/usr/sbin/ntpq
找到问题的关键后,我搜索 Whosebug 并得到了这个相关问题,其中问题得到了更好的解释和解决。
How to get CRON to call in the correct PATHs
我正在解析执行这个复合命令的结果
ntpq -c peers | awk ' [=10=] ~ /^*/ {print }'
为了获取活动ntp服务器的偏移量。
这是java定期使用和执行的代码
public Double getClockOffset() {
Double localClockOffset = null;
try {
String[] cmd = {"/bin/sh",
"-c",
"ntpq -c peers | awk \' [=11=] ~ /^\*/ {print }\'"};
Process p = Runtime.getRuntime().exec(cmd);
p.waitFor();
BufferedReader buf = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = buf.readLine();
if (!StringUtils.isEmpty(line)) {
localClockOffset = Double.parseDouble(line.trim());
} else {
// Log "NTP -> Empty line - No active servers - Unsynchronized"
}
} catch (Exception e) {
// Log exception
}
return localClockOffset;
}
ntpq 结果示例
> remote refid st t when poll reach delay offset jitter
> ==============================================================================
> *server001s1 .LOCL. 1 u 33 64 377 0.111 -0.017 0.011
> +server002s1 10.30.10.6 2 u 42 64 377 0.106 -0.006 0.027
> +server003s1 10.30.10.6 2 u 13 64 377 0.120 -0.009 0.016
请注意 awk 搜索以“*”开头的第一行并提取其第九列。在示例中:-0.017
问题是有时我在通过控制台执行命令时获取 no-active-servers 日志消息 - 旨在在没有带“*”的服务器时出现 - returns一个数字。
我知道我没有关闭该代码中的 BufferedReader
但这是此行为的原因吗?在每个方法调用中都会创建一个新实例(并在垃圾收集之前保持打开状态),但我认为这不应该是导致此问题的原因。
正如 Andrew Thompson 所指出的,您应该尝试 ProcessBuilder
。
String[] cmd = {"/bin/sh",
"-c",
"ntpq -c peers | awk \' [=10=] ~ /^\*/ {print }\'"};
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true);
Process proc = pb.start();
BufferedReader buf = new BufferedReader(new
InputStreamReader(proc.getInputStream()));
String line = null;
while ((line = buf.readLine()) != null) {
localClockOffset = Double.parseDouble(line.trim());
break;
}
proc.destroy();
Runtime.exec()
只是调用其中的 ProcessBuilder,就像这样:
public Process More ...exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}
所以使用它代替 ProcessBuilder
没有任何问题。
问题是你调用了:
p.waitFor();
在您获得InputStream
之前。
这意味着当您获得 InputStream
时,该进程已经终止,并且输出流数据可能对您可用,也可能不可用,具体取决于 OS缓冲实施的细微差别和精确的操作时间。
因此,如果您将 waitFor()
移至底部,您的代码应该会开始更可靠地工作。
然而,在 Linux 下,您通常应该能够从 PIPE 缓冲区读取剩余数据,即使在写入过程结束后也是如此。
而 OpenJDK 中的 UNIXProcess 实现实际上明确地使用了它,并在进程退出后尝试耗尽剩余数据,以便可以回收文件描述符:
/** Called by the process reaper thread when the process exits. */
synchronized void processExited() {
synchronized (closeLock) {
try {
InputStream in = this.in;
// this stream is closed if and only if: in == null
if (in != null) {
byte[] stragglers = drainInputStream(in);
in.close();
this.in = (stragglers == null) ?
ProcessBuilder.NullInputStream.INSTANCE :
new ByteArrayInputStream(stragglers);
}
} catch (IOException ignored) {}
}
}
这似乎足够可靠,至少在我的测试中是这样,所以很高兴知道 Linux|Unix 和 JRE 的具体版本 运行.
您是否也考虑过应用程序级问题的可能性?
IE。 ntpq
并不能真正保证总是 return 一个 *
行。
所以,最好从管道中删除 awk
部分,看看是否始终会有一些输出。
另一件需要注意的事情是,如果您的 shell 管道步骤之一失败(例如 ntpq 本身),您也会得到一个空输出,因此您还必须跟踪 STDERR(例如通过 ProcessBuilder
).
旁注
在开始使用数据之前执行 waitFor
在任何情况下都是一个坏主意,因为如果您的外部进程将产生足够的输出来填充管道缓冲区,它就会挂起等待有人读取它,这永远不会发生,因为你的 Java 进程将同时被锁定在 waitFor
。
终于找到问题所在了。
我不会更改已接受的答案,我认为它也很有用,但也许有人可以从我们的经验中学习。
我的 java 程序是用 shell 脚本启动的。当我们手动执行脚本时, ntpq
命令被找到并成功调用。当软件完全部署时就会出现问题。在最终环境中,我们有一个 cron 计划的恶魔,它使我们的程序保持活动状态,但 cron 建立的 PATH
与我们的配置文件已分配的 PATH
不同。
PATH
被 cron 使用:
.:/usr/bin:/bin
PATH
我们已登录以手动启动脚本:
/usr/sbin:/usr/bin:/bin:/sbin:/usr/lib:/usr/lib64:/local/users/nor:
/usr/local/bin:/usr/local/lib:.
通常 ntpq
在
/usr/sbin/ntpq
找到问题的关键后,我搜索 Whosebug 并得到了这个相关问题,其中问题得到了更好的解释和解决。
How to get CRON to call in the correct PATHs