Is there a good description of swift system command? For example, this code
let x = system("ls -l `which which`")
println(x)
produces
-rwxr-xr-x 1 root wheel 14496 Aug 30 04:29 /usr/bin/which
0
I would like to separate the output from the return code
system() is not a Swift command but a BSD library function. You get the documentation
with "man system" in a Terminal Window:
The system() function hands the argument command to the command
interpreter sh(1). The calling process
waits for the shell to finish executing the command, ignoring SIGINT and SIGQUIT, and blocking SIGCHLD.
The output of the "ls" command is just written to the standard output and not to any
Swift variable.
If you need more control then you have to use NSTask from the Foundation framework.
Here is a simple example:
let task = NSTask()
task.launchPath = "/bin/sh"
task.arguments = ["-c", "ls -l `which which`"]
let pipe = NSPipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
if let output = NSString(data: data, encoding: NSUTF8StringEncoding) {
println(output)
}
task.waitUntilExit()
let status = task.terminationStatus
println(status)
Executing the command via the shell "/bin/sh -c command ..." is necessary here because
of the "back tick" argument. Generally, it is better to invoke the commands directly,
for example:
task.launchPath = "/bin/ls"
task.arguments = ["-l", "/tmp"]
Related
I'm trying to make a hook for Github in js.
So I need to get the previous command, but child_process doesn't recognize the history command, which runs fine in my windows git bash.
This is the code I have
const { exec } = require("child_process");
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
exec('history 2', (err, res, er) => {
console.log(res)
});
history is no a program (not a binary or a script) but a command builtin into bash. You only can run it with bash — and only interactively:
$ bash -c "history 2"
(no output because it's non-interactive)
In a non-interactive session you have to read the history before using it:
$ bash -c "history -r && history 2"
the previous command
the last command
My advice is to use fc instead of history. With fc you can select one command to show. For example to get the previous command use index -2:
$ bash -c "history -r && fc -ln -2 -2"
the previous command
I'm trying to write an npm script that will execute an ssh shell command. Currently it's working by executing an osascript command to open a Terminal window and run the command.
I'd like to change this to execute the command in the current terminal. The script is including both shelljs and executive. The script ends without anything happening when I use executive. With shelljs I get:
Pseudo-terminal will not be allocated because stdin is not a terminal.
the input device is not a TTY
The command being executed is: ssh -i [ssh-key] -t ubuntu#[ip-address] eval $(base64 -D <<< [command in base64])
The base64 command is sudo docker exec -i -t $(sudo docker ps -aqf "ancestor=' + containerName + '") /bin/bash
If I output the command and copy and paste it, it will work as expected, sshing into a remote machine and running a docker exec command.
If I remove the -t option I don't get the warning messages but there's no output in the console and the script hangs (I assume it's running the command in the background with no output). If I remove the eval ... part I get an output that looks like what you'd see when sshing into a server but without the input terminal.
What can I do to execute this command in the same terminal or in a new tab. If I have to use an osascript command to do this, that's fine as well. I'll be executing this command from the terminal in PhpStorm though.
Edit
Here's the block of code:
var execCommand = 'sudo docker exec -i -t $(sudo docker ps -aqf "ancestor=nginx") /bin/bash';
var buffer = new Buffer(execCommand);
var encoded = buffer.toString('base64');
var cmd = "ssh -i " + this.keyPath + " -t ubuntu#" + ip + " eval $(base64 -D <<< " + encoded + ") ";
shell.exec(cmd);
Edit 2
I can ssh into the machine successfully and get a command prompt but I'm getting a the input device is not a TTY error now when I add the eval command.
var docker_exec = 'sudo docker exec -it $(sudo docker ps -aqf "ancestor=' + containerName + '") /bin/bash';
var encoded = new Buffer(docker_exec).toString('base64');
var sshTerm = spawn('ssh', [
'-i',
this.keyPath,
'ubuntu#' + ip,
'eval',
'eval $(base64 -D <<< ' + encoded + ')'
], {
stdio: 'inherit',
shell: true
});
sshTerm.on('exit', function() {
process.exit(0);
});
I checked and it shelljs.exec is good for non TTY commands. For TTY based command you can use normal spawn method. Below is a sample code that works great for me
var spawn = require('child_process').spawn;
var sshTerm = spawn('ssh', ["vagrant#192.168.33.100", ""], {
stdio: 'inherit'
});
// listen for the 'exit' event
// which fires when the process exits
sshTerm.on('exit', function(code, signal) {
if (code === 0) {
// process completed successfully
} else {
// handle error
}
});
To run xcpretty command in xcodebuild, i use the below code:
import Foundation
class Command{
func command(args: String...) -> Int32 {
let task = NSTask()
task.launchPath = "/usr/bin/env"
task.arguments = args
task.currentDirectoryPath = "/Users/Desktop/XCode/Test/"
task.launch()
task.waitUntilExit()
return task.terminationStatus
}
let xcodebuildCommand = Command()
xcodebuildCommand.command("xcodebuild","test","-project","proj.xcodeproj","-scheme","projScheme","-destination","platform=iOS Simulator,name=iPad Air"," | /usr/local/bin/xcpretty --report html --output /Desktop/test_output/report.html")
the error is
xcodebuild: error: Unknown build action ' | /usr/local/bin/xcpretty --report html --output /Desktop/test_output/report.html'.
the below command run properly from terminal:
xcodebuild test -project proj.xcodeproj.xcodeproj -scheme projScheme -destination 'platform=iOS Simulator,name=iPad Air' | xcpretty --repor html --output /pathToReportfolder/report.html
NSTask is not a shell, it will not interpret your shell script for you.
You'll need to manually set up an NSPipe to connect the standard output of your xcodebuild NSTask to an xcpretty NSTask.
import Foundation
func runCommand(workingDirectory: String? = nil,
stdin: NSPipe? = nil,
stdout: NSPipe? = nil,
stderr: NSPipe? = nil,
args: String...) -> Int32 {
let task = NSTask()
task.launchPath = "/usr/bin/env"
task.arguments = args
if let workingDirectory = workingDirectory {
task.currentDirectoryPath = workingDirectory
}
if let stdin = stdin { task.standardInput = stdin }
if let stdout = stdout { task.standardOutput = stdout }
if let stderr = stderr { task.standardError = stderr }
task.launch()
task.waitUntilExit()
return (task.terminationStatus)
}
let pipe = NSPipe()
//omit "workingDirectory:" in Swift 2
runCommand(workingDirectory: "/Users/Desktop/XCode/Test/",
stdout: pipe,
args: "xcodebuild","test",
"-project","proj.xcodeproj",
"-scheme","projScheme",
"-destination","platform=iOS Simulator,name=iPad Air")
//omit "workingDirectory:" in Swift 2
runCommand(workingDirectory: "/Users/Desktop/XCode/Test/",
stdin: pipe,
args: "/usr/local/bin/xcpretty",
"--report html",
"--output",
"/Desktop/test_output/report.html")
I'm trying to run a command line tool and taking the result.
I checked it in terminal:
/usr/bin/exiftool -filename -directory -createdate -model "/Users/dirk/Desktop\" -t -S -q -r -f >"RenamerOutfile0.txt"
This runs fine and delivers result in file.
Using SWIFT I tried this:
let task = NSTask()
task.launchPath = "/usr/bin/exiftool"
task.arguments = ["-filename -directory -createdate -model \"/Users/dirk/Desktop\" -t -S -q -r -f"]
let pipe = NSPipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
Unfortunatly nothing happens. data is assigned 0 byte - no result.
If I insert the redirection to file no file is created.
Any idea what's the difference in calling the tool from terminal than with this task?
I found a workaround:
let x = system("/usr/bin/exiftool -filename -directory -createdate -model \"/Users/dirk/Desktop\" -t -S -q -r -f >\"/var/tmp/RenamerOutfile0.txt\"")
This works fine. Maybe because I added the path /var/tmp for output file? Doesn't matter! :-)
I want call unix command in cocoa application,but it doesn't work,
the command:"lua -v"
cocoa code:
NSArray *cmdArray = [cmd componentsSeparatedByString:#" "];
NSPipe *pipe = [NSPipe pipe];
NSFileHandle *file = pipe.fileHandleForReading;
NSTask *task = [[NSTask alloc] init];
[task setStandardOutput:pipe];
task.launchPath = cmdArray[0];
if(cmdArray.count > 1)
{
task.arguments = [cmdArray subarrayWithRange:NSMakeRange(1, cmdArray.count - 1)];
}
[task launch] ;
NSData *data = [file readDataToEndOfFile];
[file closeFile] ;
NSString *grepOutput = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
return grepOutput;
the data always is 0 byte
NSTask does not run a Unix "command", rather it runs a Unix "process", and the difference is crucial.
When your code is run in Xcode you should see "launch path not accessible" in the console window, this is your clue as to the problem. The documentation for launch tells you that an exception is thrown if the launch path is invalid.
When you type, say, "echo Hello" at the Unix command prompt you are entering data into a command interpreter, the "shell". The shell has to locate the application "echo" in order to start a process running it. The shell does this by searching through a set of directories specified by the PATH environment variable, in the case of "echo" it finds "/bin/echo". It is this path you must set the launchPath property to.
If you wish to use NSTask for this purpose you have two basic choices (a) have your code locate the "lua" binary itself or (b) use NSTask to run a shell and have that shell do its normal processing for you.
The standard "sh" shell is located at "/bin/sh". If you read it's manual page you will see it takes an option "-c string" which instructs it to treat string as command input. E.g consider the following Terminal transcript:
crd$ echo Hello
Hello
crd$ sh -c "echo Hello"
Hello
crd$
In the first case the shell found the binary for "echo" and executed that passing it "Hello". In the second it found the binary for "sh" and executed that passing it "-c" and "echo Hello", that process in turn found the binary for "echo".
So using NSTask you can invoke "/bin/sh" and pass it as arguments "-c" and your command line, the shell will then parse your command line just like in the Terminal and invoke your command, something along the lines of:
NSPipe *pipe = [NSPipe pipe];
NSFileHandle *file = pipe.fileHandleForReading;
NSTask *task = [[NSTask alloc] init];
task.standardOutput = pipe;
task.standardError =pipe; // capture any error messages the sh writes, could send to another pipe
task.launchPath = #"/bin/sh";
task.arguments = #[#"-c", cmd];
[task launch];
NSData *data = [file readDataToEndOfFile];
[file closeFile];
HTH