文字列として標準出力を返すシステムコールを作成する
-
04-07-2019 - |
質問
PerlとPHPはバックティックを使用してこれを行います。たとえば、
$output = `ls`;
ディレクトリリストを返します。同様の関数system("foo")
は、指定されたコマンドfooのオペレーティングシステムのリターンコードを返します。 fooがstdoutに出力するものを返すバリアントについて話しています。
他の言語はどのようにこれを行いますか?この関数の正規名はありますか? (私は<!> quot; backtick <!> quot;を使用しますが、<!> quot; syslurp <!> quot;を作成することもできます。)
解決 20
Perl:
$output = `foo`;
ADDED:これは実際には多元的なタイです。上記も有効なPHPであり、たとえばRubyは同じバックティック表記も使用します。
他のヒント
Python
from subprocess import check_output as qx
output = qx(['ls', '-lt'])
Python <!> lt; 2.7 または<! > lt; 3.1
subprocess.py からsubprocess.check_output()
を抽出しますまたは次のようなものを適応させます:
import subprocess
def cmd_output(args, **kwds):
kwds.setdefault("stdout", subprocess.PIPE)
kwds.setdefault("stderr", subprocess.STDOUT)
p = subprocess.Popen(args, **kwds)
return p.communicate()[0]
print cmd_output("ls -lt".split())
サブプロセスモジュールは、2.4からstdlibに含まれています。
Python:
import os
output = os.popen("foo").read()
[ Alexman および dreeves -コメントを参照-この DZones Javaスニペットページ は、この例では「ls」を作成するためのOSに依存しないフルバージョンです。これは、彼らの Java <!> quot; Simple!<!> quot;:
E:\classes\com\javaworld\jpitfalls\article2>java GoodWindowsExec "dir *.java"
Executing cmd.exe /C dir *.java
...
またはJavaコード内
String output = GoodWindowsExec.execute("dir");
しかし、それを行うには、コーディングする必要があります...
... これは恥ずかしいです。
import java.util.*;
import java.io.*;
class StreamGobbler extends Thread
{
InputStream is;
String type;
StringBuffer output = new StringBuffer();
StreamGobbler(InputStream is, String type)
{
this.is = is;
this.type = type;
}
public void run()
{
try
{
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line=null;
while ( (line = br.readLine()) != null)
System.out.println(type + ">" + line);
output.append(line+"\r\n")
} catch (IOException ioe)
{
ioe.printStackTrace();
}
}
public String getOutput()
{
return this.output.toString();
}
}
public class GoodWindowsExec
{
public static void main(String args[])
{
if (args.length < 1)
{
System.out.println("USAGE: java GoodWindowsExec <cmd>");
System.exit(1);
}
}
public static String execute(String aCommand)
{
String output = "";
try
{
String osName = System.getProperty("os.name" );
String[] cmd = new String[3];
if( osName.equals( "Windows 95" ) )
{
cmd[0] = "command.com" ;
cmd[1] = "/C" ;
cmd[2] = aCommand;
}
else if( osName.startsWith( "Windows" ) )
{
cmd[0] = "cmd.exe" ;
cmd[1] = "/C" ;
cmd[2] = aCommand;
}
Runtime rt = Runtime.getRuntime();
System.out.println("Executing " + cmd[0] + " " + cmd[1]
+ " " + cmd[2]);
Process proc = rt.exec(cmd);
// any error message?
StreamGobbler errorGobbler = new
StreamGobbler(proc.getErrorStream(), "ERROR");
// any output?
StreamGobbler outputGobbler = new
StreamGobbler(proc.getInputStream(), "OUTPUT");
// kick them off
errorGobbler.start();
outputGobbler.start();
// any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal);
output = outputGobbler.getOutput();
System.out.println("Final output: " + output);
} catch (Throwable t)
{
t.printStackTrace();
}
return output;
}
}
Perlでの別の方法(TIMTOWTDI)
$output = <<`END`;
ls
END
これは、Perlプログラムに比較的大きなシェルスクリプトを埋め込むときに特に便利です
Ruby:バックティックまたは '%x'組み込み構文のいずれか。
puts `ls`;
puts %x{ls};
perlの代替方法
$output = qx/ls/;
これには、区切り文字を選択できるという利点があり、コマンドで `を使用できるようになりました(ただし、本当に必要な場合はデザインを再検討する必要があります)。別の重要な利点は、区切り文字として一重引用符を使用すると、変数が補間されないことです(非常に便利です)
Haskell:
import Control.Exception
import System.IO
import System.Process
main = bracket (runInteractiveCommand "ls") close $ \(_, hOut, _, _) -> do
output <- hGetContents hOut
putStr output
where close (hIn, hOut, hErr, pid) =
mapM_ hClose [hIn, hOut, hErr] >> waitForProcess pid
MissingH がインストールされている場合:
import System.Cmd.Utils
main = do
(pid, output) <- pipeFrom "ls" []
putStr output
forceSuccess pid
これは<!> quot; glue <!> quot;の簡単な操作です。 PerlやRubyなどの言語ですが、Haskellはそうではありません。
シェルで
OUTPUT=`ls`
または代わりに
OUTPUT=$(ls)
この2番目の方法は、ネストを許可しますが、最初の方法とは異なり、すべてのシェルでサポートされているわけではないため、より優れています。
アーラン:
os:cmd("ls")
まあ、これはシステムに依存しているため、必要なさまざまなシステムコール用の組み込みラッパーを持たない多くの言語があります。
たとえば、Common Lisp自体は特定のシステムで実行するようには設計されていません。ただし、SBCL(Steel Banks Common Lisp実装)は、他のほとんどのCL実装と同様に、Unixに似たシステムの拡張機能を提供します。これははるかに<!> quot; mighty <!> quot;もちろん、出力を取得するだけではありません(実行中のプロセスを制御し、あらゆる種類のストリーム方向を指定することができます。SBCLマニュアルの6.3章を参照してください)が、この特定の小さなマクロを簡単に書くことができます目的:
(defmacro with-input-from-command ((stream-name command args) &body body) "Binds the output stream of command to stream-name, then executes the body in an implicit progn." `(with-open-stream (,stream-name (sb-ext:process-output (sb-ext:run-program ,command ,args :search t :output :stream))) ,@body))
今、次のように使用できます:
(with-input-from-command (ls "ls" '("-l")) ;;do fancy stuff with the ls stream )
すべてを1つの文字列に丸intoみしたいかもしれません。マクロは簡単です(おそらく、より簡潔なコードも可能です):
(defmacro syslurp (command args) "Returns the output from command as a string. command is to be supplied as string, args as a list of strings." (let ((istream (gensym)) (ostream (gensym)) (line (gensym))) `(with-input-from-command (,istream ,command ,args) (with-output-to-string (,ostream) (loop (let ((,line (read-line ,istream nil))) (when (null ,line) (return)) (write-line ,line ,ostream)))))))
次の呼び出しで文字列を取得できます:
(syslurp "ls" '("-l"))
Mathematica:
output = Import["!foo", "Text"];
数年前、プラグインを jEdit をネイティブアプリケーションに接続しました。これは、実行中の実行可能ファイルからストリームを取得するために使用したものです。行うべきことはwhile((String s = stdout.readLine())!=null){...}
:
/* File: IOControl.java
*
* created: 10 July 2003
* author: dsm
*/
package org.jpop.io;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
/**
* Controls the I/O for a process. When using the std[in|out|err] streams, they must all be put on
* different threads to avoid blocking!
*
* @author dsm
* @version 1.5
*/
public class IOControl extends Object {
private Process process;
private BufferedReader stdout;
private BufferedReader stderr;
private PrintStream stdin;
/**
* Constructor for the IOControl object
*
* @param process The process to control I/O for
*/
public IOControl(Process process) {
this.process = process;
this.stdin = new PrintStream(process.getOutputStream());
this.stdout = new BufferedReader(new InputStreamReader(process.getInputStream()));
this.stderr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
}
/**
* Gets the stdin attribute of the IOControl object
*
* @return The stdin value
*/
public PrintStream getStdin() {
return this.stdin;
}
/**
* Gets the stdout attribute of the IOControl object
*
* @return The stdout value
*/
public BufferedReader getStdout() {
return this.stdout;
}
/**
* Gets the stderr attribute of the IOControl object
*
* @return The stderr value
*/
public BufferedReader getStderr() {
return this.stderr;
}
/**
* Gets the process attribute of the IOControl object. To monitor the process (as opposed to
* just letting it run by itself) its necessary to create a thread like this: <pre>
*. IOControl ioc;
*.
*. new Thread(){
*. public void run(){
*. while(true){ // only necessary if you want the process to respawn
*. try{
*. ioc = new IOControl(Runtime.getRuntime().exec("procname"));
*. // add some code to handle the IO streams
*. ioc.getProcess().waitFor();
*. }catch(InterruptedException ie){
*. // deal with exception
*. }catch(IOException ioe){
*. // deal with exception
*. }
*.
*. // a break condition can be included here to terminate the loop
*. } // only necessary if you want the process to respawn
*. }
*. }.start();
* </pre>
*
* @return The process value
*/
public Process getProcess() {
return this.process;
}
}
Tclを忘れないでください:
set result [exec ls]
C#3.0、これ:
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
var info = new ProcessStartInfo("cmd", "/c dir") { UseShellExecute = false, RedirectStandardOutput = true };
Console.WriteLine(Process.Start(info).StandardOutput.ReadToEnd());
}
}
警告:実稼働コードはProcessオブジェクトを適切に破棄する必要があります...
Perlの別の方法(または2!)...
open my $pipe, 'ps |';
my @output = < $pipe >;
say @output;
open も次のように記述できます...
open my $pipe, '-|', 'ps'
PHPで
$output = `ls`;
または
$output = shell_exec('ls');
C(glibc
拡張子付き):
#define _GNU_SOURCE
#include <stdio.h>
int main() {
char *s = NULL;
FILE *p = popen("ls", "r");
getdelim(&s, NULL, '\0', p);
pclose(p);
printf("%s", s);
return 0;
}
さて、本当に簡潔でもきれいでもありません。これがCの人生です...
Posix準拠システムのCの場合:
#include <stdio.h>
FILE* stream = popen("/path/to/program", "rw");
fprintf(stream, "foo\n"); /* Use like you would a file stream. */
fclose(stream);
まだc#がいないのはなぜですか:)
これは、C#で行う方法です。組み込みの方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace TestConsole
{
class Program
{
static void Main(string[] args)
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.FileName = "cmd";
p.StartInfo.Arguments = "/c dir";
p.Start();
string res = p.StandardOutput.ReadToEnd();
Console.WriteLine(res);
}
}
}
別のLispの方法は次のとおりです。
(defun execute (program parameters &optional (buffer-size 1000))
(let ((proc (sb-ext:run-program program parameters :search t :output :stream))
(output (make-array buffer-size :adjustable t :fill-pointer t
:element-type 'character)))
(with-open-stream (stream (sb-ext:process-output proc))
(setf (fill-pointer output) (read-sequence output stream)))
output))
次に、文字列を取得します:
(execute "cat" '("/etc/hosts"))
STDOUTに大量の情報を出力するコマンドを実行する場合は、次のように実行できます。
(execute "big-writer" '("some" "parameters") 1000000)
最後のパラメーターは、ビッグライターからの出力用に大量のスペースを事前に割り当てます。この関数は、一度に1行ずつ出力ストリームを読み取るよりも高速であると推測しています。
Lua :
foo = io.popen("ls"):read("*a")
J :
output=:2!:0'ls'
Perl、別の方法:
use IPC::Run3
my ($stdout, $stderr);
run3 ['ls'], undef, \$stdout, \$stderr
or die "ls failed";
コマンド入力をフィードし、stderrとstdoutの両方を個別に取得できるため便利です。サブルーチンへのパイプをセットアップできるIPC::Run
ほどきちんとした/怖い/遅い/邪魔になるほど近くはありません。
アイコン/ユニコン:
stream := open("ls", "p")
while line := read(stream) do {
# stuff
}
ドキュメントでは、これをパイプと呼んでいます。良い点の1つは、出力をファイルを読んでいるように見せることです。また、必要に応じてアプリの標準入力に書き込むこともできます。
Clozure Common Lisp:
(with-output-to-string (stream)
(run-program "ls" '("-l") :output stream))
LispWorks
(with-output-to-string (*standard-output*)
(sys:call-system-showing-output "ls -l" :prefix "" :show-cmd nil))
確かに、(利用可能なすべての言語から)小さくはありませんが、それほど冗長ではありません。
このバージョンは汚れています。例外を処理する必要があり、読み取りが改善される場合があります。これは、Javaバージョンの起動方法を示すためのものです。
Process p = Runtime.getRuntime().exec( "cmd /c " + command );
InputStream i = p.getInputStream();
StringBuilder sb = new StringBuilder();
for( int c = 0 ; ( c = i.read() ) > -1 ; ) {
sb.append( ( char ) c );
}
以下のプログラムを完了します。
import java.io.*;
public class Test {
public static void main ( String [] args ) throws IOException {
String result = execute( args[0] );
System.out.println( result );
}
private static String execute( String command ) throws IOException {
Process p = Runtime.getRuntime().exec( "cmd /c " + command );
InputStream i = p.getInputStream();
StringBuilder sb = new StringBuilder();
for( int c = 0 ; ( c = i.read() ) > -1 ; ) {
sb.append( ( char ) c );
}
i.close();
return sb.toString();
}
}
サンプル出力(typeコマンドを使用)
C:\oreyes\samples\java\readinput>java Test "type hello.txt"
This is a sample file
with some
lines
サンプル出力(dir)
C:\oreyes\samples\java\readinput>java Test "dir"
El volumen de la unidad C no tiene etiqueta.
El número de serie del volumen es:
Directorio de C:\oreyes\samples\java\readinput
12/16/2008 05:51 PM <DIR> .
12/16/2008 05:51 PM <DIR> ..
12/16/2008 05:50 PM 42 hello.txt
12/16/2008 05:38 PM 1,209 Test.class
12/16/2008 05:47 PM 682 Test.java
3 archivos 1,933 bytes
2 dirs 840 bytes libres
いずれかを試してください
java Test netstat
java Test tasklist
java Test "taskkill /pid 416"
編集
これが<!> quot; best <!> quot;であることを100%確信していないことを認めなければなりません。それを行う方法。参照やコードを投稿して、それをどのように改善できるか、何が問題なのかを示してください。