Execute external command with swift

Execute external command with swift

If you write #! / Usr / bin / swift in your head

chmod a+x hoge.swift
./hoge.swift #swift hoge.Can also be started with swift

I knew that it could be started like a script, so after studying, I translated the perl script I had at hand into swift. A memo at that time.

perl

my $status = system "ls hoge/"  #ret-code
my $ret = `ls hoge/ `           #output

How should I write this in swift? I looked it up.

Simple call

I haven't written any OSX apps, so I haven't used them, but at NSTask I think I can do it. You can receive the output of external commands with task.standardOutput (NSPipe). If you want to specify the current directory and execute it, set it to task.currentDirectoryPath.

func stdOutOfCommand(cmd: String, arguments args: [String], currentDirPath currentDir: String? = nil) -> String {
    let task: NSTask = NSTask()
    task.launchPath = cmd
    task.arguments = args
    if currentDir != nil { task.currentDirectoryPath = currentDir! }
    let pipe: NSPipe = NSPipe()
    task.standardOutput = pipe
    task.launch()
    let out: NSData = pipe.fileHandleForReading.readDataToEndOfFile()
    let outStr: String? = NSString(data: out, encoding: NSUTF8StringEncoding) as? String
    return outStr == nil ? "" : outStr!
}

var ret = stdOutOfCommand("/bin/ls", arguments: ["hoge/"])

Interactive

In the case of interactive (request input) format, use waitForDataInBackgroundAndNotify () to notify that it is a background wait. NSNotificationCenter needs to receive NSFileHandleDataAvailableNotification.

If you want to wait until the task finishes, wait with task.waitUntilExit (). The exit status can be obtained with task.terminationStatus.

In the example below, the input is controlled using NSFileHandle.fileHandleWithStandardInput ().

func printFlush(message: String) {
    print(message, terminator: "")
    fflush(__stdoutp)
}
func scriptWithCmd(cmd: String, arguments args: [String], currentDirPath currentDir: String? = nil) -> Int32 {
    //set task
    let input: NSFileHandle = NSFileHandle.fileHandleWithStandardInput()
    let inPipe: NSPipe = NSPipe()
    let outPipe: NSPipe = NSPipe()
    let task: NSTask = NSTask()
    task.launchPath = cmd
    task.arguments = args
    if currentDir != nil { task.currentDirectoryPath = currentDir! }
    task.standardOutput = outPipe
    task.standardInput = inPipe
    task.launch()
    
    //notification
    input.waitForDataInBackgroundAndNotify()
    outPipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
    NSNotificationCenter.defaultCenter().addObserverForName(NSFileHandleDataAvailableNotification, object: input, queue: nil,
        usingBlock : { (notification: NSNotification!) in
            let inData: NSData = input.availableData
            if inData.length > 0 {
                inPipe.fileHandleForWriting.writeData(inData)
                input.waitForDataInBackgroundAndNotify()
            } else {
                inPipe.fileHandleForWriting.closeFile()
            }
        }
    )
    NSNotificationCenter.defaultCenter().addObserverForName(NSFileHandleDataAvailableNotification, object: outPipe.fileHandleForReading, queue: nil,
        usingBlock:  { (notification: NSNotification!) in
            let outData: NSData = outPipe.fileHandleForReading.availableData
            if let outStr: NSString = NSString(data: outData, encoding: NSUTF8StringEncoding) {
                printFlush(outStr as String)
            }
            outPipe.fileHandleForReading.waitForDataInBackgroundAndNotify()
        }
    )
    
    task.waitUntilExit()
    return task.terminationStatus
}

Simple memo for calling external commands in other languages

The one I did a little research on before

Perl

There is also ʻexec (), but it doesn't come back, so use system ()`. Back quote convenient.

my $status = system "ls hoge/"  #ret-code
my $ret = `ls hoge/ `           #output

C

It's too late with perl before, so a memo when I was trying to do various things with C / C ++

#include <stdlib.h>
	int ret = system("ls hoge/");

With POSIX, you can use popen / pclose.

#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#define BUF 256
int main (void) {
	FILE *fp;
	char buf[BUF];
	char *cmd = "/bin/ls hoge/";
	
	if ((fp=popen(cmd,"r")) == NULL) {
		err(EXIT_FAILURE, "%s", cmd);
	}
	while(fgets(buf, BUF, fp) != NULL) {
		fputs(buf, stdout);
	}
	pclose(fp);

	return 0;
}

I / O cannot be done at the same time with popen, so you need to use around pipe (), dup (), fork (), ʻexec * () `.

Reference

C++

PStreams (POSIX) seems to be usable

Java

It seems to use ProcessBuilder, Can commands that the shell interprets directly be executed directly?

reference

-Java External Process Start Memo (Hishidama's Java Process Memo) -Redirect with Process Builder of Java SE 7

JavaScript(Node.js)

execSync = require("child_process").execSync;
result = execSync("ls");

reference

Recommended Posts

Execute external command with swift
Getting Started with Swift
Starting with Swift Swift UI
Change Wordpress siteurl with command
Progress made with Swift UI
Compare Java 8 Optional with Swift
How to execute Postgresql copy command with column information on Java
Make a Christmas tree with swift
Execute packaged Java code with commands
Create command line app with maven
Swift beginner Debugging memo with Xcode
Create a Maven project with a command