is it possible to execute a command line order such as 'll', 'pwd' or whatever from a Coffee script?
I've tried to find examples without luck so far.
Thanks!
If you execute CoffeeScript via Node.js you will have full access to the abilities of your OS. Use the spawn method of the child_process module to create a new process:
{spawn} = require 'child_process'
ls = spawn 'ls', ['array', 'of', 'options']
# receive all output and process
ls.stdout.on 'data', (data) -> console.log data.toString().trim()
# receive error messages and process
ls.stderr.on 'data', (data) -> console.log data.toString().trim()
Related
This has been a thorn in my side and I'm wondering if I'm missing something simple or not. I need to run .bxs scripts from the jobs scheduler.
I tried to start a service with a .bxs script file from the jobs module but it does not run. It registers as a service but the script does not run.
let $home := Q{org.basex.util.Prop}HOMEDIR()
let $job := $home || 'webapp/sync/update_jira.bxs'
let $job2 := $home || 'webapp/sync/update_commit_data.bxs'
return (jobs:eval(xs:anyURI($job), (), map { 'id':'update_jira_job', 'start':'14:54:02', 'interval':'P1D', 'service': true(), 'log': 'update_jira_job'}),
jobs:eval(xs:anyURI($job2), (), map { 'id':'update_commit_data', 'start':'15:03:02', 'interval':'P1D', 'service': true(), 'log': 'update_commit_data'}))
I also tried to run a query that executes the command line to run the scripts for example within the update_jira.xq there is a line proc:execute('basex update_jira.bxs') from an initial query that looks something like this...
let $home := Q{org.basex.util.Prop}HOMEDIR()
let $job := $home || '/srv/webapp/sync/update_jira.xq'
let $job2 := $home || '/src/webapp/sync/update_commit_data.xq'
return (jobs:eval(xs:anyURI($job), (), map { 'id':'update_jira_job', 'start':'14:54:02', 'interval':'P1D', 'service': true(), 'log': 'update_jira_job'}),
jobs:eval(xs:anyURI($job2), (), map { 'id':'update_commit_data', 'start':'15:03:02', 'interval':'P1D', 'service': true(), 'log': 'update_commit_data'}))
When this ran as a service, the database did not update as expected and I got this output in the log:
22:47:02.001 JOB:update_commit_data admin OK 0.30 update_commit_data
22:41:00.000 JOB:update_jira_job admin ERROR 0.00 update_jira_job; Unexpected end of query: '0'.
But that is strange because when I ran the query itself -- that starts the service with jobs:eval -- then it actually ran ok when I ran the query for the first time.
16:42:52.257 10.244.144.142:57444 admin 200 221563.70 [GET] /rest?run=sync/update_jira.bxs
16:49:39.862 10.244.144.142:57591 admin 200 101413.21 [GET] /rest?run=sync/update_commit_data.bxs
This is my latest attempt where the query runs initially but then doesn't seem to execute as a service interval. I added the base-uri as the path to the query and I hope that's the right way to do that.
let $home := Q{org.basex.util.Prop}HOMEDIR()
return jobs:eval(proc:execute('/usr/local/bin/basex', '/srv/basex/webapp/sync/update_jira.bxs'), (),
map { 'id':'update_jira_job', 'interval':'PT5M', 'base-uri': '/srv/basex/webapp/sync/',
'service': true(), 'log': 'update_jira_job'})
When I run this through the database admin tool query window, it runs right away
14:02:54.494 10.244.144.142:54402 admin 200 296095.32 [POST] /dba/query-update
And then after 5 the minute interval a .05 ms log entry shows up when the service kicked off:
14:57:50.564 JOB:update_jira_job admin OK 0.05 update_jira_job
Please note that BaseX command scripts contain plain database commands, whereas the functions in the Jobs Module were tailored to execute XQuery code. If you want to use jobs:eval, the best solution is to rewrite the contents of your command scripts to XQuery.
If you want to stick with the command scripts, you could indeed try to invoke BaseX via proc:execute, but you should be aware that the two BaseX instance will run independently of each other and could lead to corrupt databases (see https://docs.basex.org/wiki/Startup#Concurrent_Operations).
If the invocation fails…
Cannot run program "basex": CreateProcess error=2, ...
…you may need to address BaseX with the full path:
(: Windows installation :)
proc:execute('c:\Program Files (x86)\BaseX\bin\basex.bat', 'commands.bxs')
(: Linux :)
proc:execute('/path/to/basex', 'commands.bxs')
I am trying to create a simple VSCode extension to run a set of commands when I open a folder. Basically these commands will set up our development environment. I have started off creating the boilerplace and ran through the example that VSCode provided but I am not clear how to run system commands. Appreciate any help or point me to some documentation about this topic.
Your extension environment has access to node.js libraries, so you can just use child_process or any helper libraries to execute commands:
const cp = require('child_process')
cp.exec('pwd', (err, stdout, stderr) => {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (err) {
console.log('error: ' + err);
}
});
One alternative could be to use the Terminal API which is the best option if you have the need for the process to be fully observable and controllable to/by the user.
Biggest downside: The Terminal API does not yet offer a way to introspect the processes that run inside of it.
If you actually want to run the process in the terminal, the only way to do this safely for now, would be to use a two-layer approach, where you start a wrapper process that in turn launches and observes the actual process (taken in via command line args).
Our self-made TerminalWrapper
We tried this ourselves.
In our first approach, the wrapper used a socket.io connection that allows for communication with and control by the extension.
In our second approach, we simplified and instead created the terminal using bash -c (non-interactive shell), and used a file watcher to get the results instead. Easier this way but after the process is done, the user won't be able to use the Terminal window (because its non-interactive). A lot less error-prone and does not require fulfilling socket.io dependency.
Implementation Details
In our extension, we use a TerminalWrapper which runs the command inside a wrapper process, and waits for a file to contain the results.
The wrapper process is here. It writes the result to a file.
Usage example here:
const cwd = '.';
const command = `node -e "console.log('hi!');"`;
const { code } = await TerminalWrapper.execInTerminal(cwd, command, {}).waitForResult();
if (code) {
const processExecMsg = `${cwd}$ ${command}`;
throw new Error(`Process failed with exit code ${code} (${processExecMsg})`);
}
Biggest downside with the second approach is that we now need bash to be present, however (i) we do have a dependency checker that will warn you if you don't and explain how to get it, and (ii) using a unified shell, makes running commands a lot easier, as we now have a pretty strong unified feature set, we know we can rely on, rather than only being able to use common command execution syntax, and (iii) we can even run *.sh files without having to worry.
Introducing: VSCode Terminal API
VSCode API Documentation: Terminal
Official Terminal API Samples
All of the following imagery and excerpts are just straight up copy-and-paste'd from their official sample repository:
Create a terminal and run a command in it
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createAndSend', () => {
const terminal = vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`);
terminal.sendText("echo 'Sent text immediately after creating'");
}));
Terminal activation event
vscode.window.onDidChangeActiveTerminal(e => {
console.log(`Active terminal changed, name=${e ? e.name : 'undefined'}`);
});
TerminalQuickPickItem
function selectTerminal(): Thenable<vscode.Terminal | undefined> {
interface TerminalQuickPickItem extends vscode.QuickPickItem {
terminal: vscode.Terminal;
}
const terminals = <vscode.Terminal[]>(<any>vscode.window).terminals;
const items: TerminalQuickPickItem[] = terminals.map(t => {
return {
label: `name: ${t.name}`,
terminal: t
};
});
return vscode.window.showQuickPick(items).then(item => {
return item ? item.terminal : undefined;
});
}
...and a lot more!...
(<3 for the VSCode team and their hard work.)
What I did was to create a promise based utility function to run all shell command with child_process
import * as cp from "child_process";
const execShell = (cmd: string) =>
new Promise<string>((resolve, reject) => {
cp.exec(cmd, (err, out) => {
if (err) {
return reject(err);
}
return resolve(out);
});
});
To get current directory
const currentDir = await execShell('pwd');
To get current git branch name
const branchName = await execShell('git rev-parse --abbrev-ref HEAD');
I'm migrating some shell scripts to Chef recipes. Some of these scripts are fairly involved, so just to make life easier in the short term and to avoid introducing bugs in rewriting everything in Chef/Ruby, I'd like to just run some of them as-is. They're all well-written and idempotent, so honestly there's no rush, but of course, the eventual goal is to rewrite them.
One cool feature of Ruby is its __END__ keyword/method: Lines below __END__ will not be executed. Those lines will be available via the special filehandle DATA.
It would be cool to ship the shell scripts as-is inside the the recipe after __END__, maybe something like the following, which I placed in chef-repo/cookbooks/ruby-data-test/recipes/default.rb:
file = Tempfile.new(File.basename(__FILE__))
file << DATA.read
bash file.path
file.unlink
__END__
echo "Hello, world"
However when I run this (with chef-solo -c solo.rb --override-runlist 'recipe[ruby-data-test]'), I get the following error:
[2014-10-03T17:14:56+00:00] ERROR: uninitialized constant Chef::Recipe::DATA
I'm pretty new to Chef, but I'm guessing the above is something about Chef wrapping my recipe in a class, and there's something simple preventing me from accessing DATA. Since it's "global" (?) I tried putting a dollar sign ($DATA) in front of it but that failed with:
NoMethodError
-------------
undefined method `read' for nil:NilClass
So the question is: How do I access DATA in my Chef recipe? Thanks!
It appears you don't have access to DATA, but you can fake it by reading in the current file yourself and splitting on __END__, like Sinatra does.
I ended up making a Chef LWRP for reuse. I don't know if I'll actually end up using this, but I wanted to figure it out. Like I said, I'm a Chef/Ruby noob, so any better ideas or suggestions welcome!
ruby_data_test/recipes/default.rb:
ruby_data_test_execute_ruby_data __FILE__
__END__
#!/bin/bash
set -o errexit
date
echo "Hello, world"
ruby_data_test/resources/execute_ruby_data.rb:
actions :execute_ruby_data
default_action :execute_ruby_data
attribute :source, :name_attribute => true, :required => true
attribute :args, :kind_of => Array
attribute :ignore_errors, :kind_of => [TrueClass, FalseClass], :default => false
ruby_data_test/providers/execute_ruby_data.rb:
def whyrun_supported?
true
end
use_inline_resources
action :execute_ruby_data do
converge_by("Executing #{#new_resource}") do
Chef::Log.info("Executing #{#new_resource}")
file_who_called_me = #new_resource.source
io = ::IO.respond_to?(:binread) ? ::IO.binread(file_who_called_me) : ::IO.read(file_who_called_me)
app, data = io.gsub("\r\n", "\n").split(/^__END__$/, 2)
data.lstrip!
file = Tempfile.new('execute_ruby_data')
file << data
file.chmod(0755)
file.close
exit_status = ::Open3.popen2e(file.path, *#new_resource.args) do |stdin, stdout_and_stderr, wait_thr|
stdout_and_stderr.each { |line| puts line }
wait_thr.value # exit status
end
if exit_status != 0 && !#new_resource.ignore_errors
throw RuntimeError
end
end
end
Here's the output:
$ chef-solo -c solo.rb --override-runlist 'recipe[ruby_data_test]'
Starting Chef Client, version 11.12.4
[2014-10-03T21:50:29+00:00] WARN: Run List override has been provided.
[2014-10-03T21:50:29+00:00] WARN: Original Run List: []
[2014-10-03T21:50:29+00:00] WARN: Overridden Run List: [recipe[ruby_data_test]]
Compiling Cookbooks...
Converging 1 resources
Recipe: ruby_data_test::default
* ruby_data_test_execute_ruby_data[/root/chef/chef-repo/cookbooks/ruby_data_test/recipes/default.rb] action execute_ruby_dataFri Oct 3 21:50:29 UTC 2014
Hello, world
- Executing ruby_data_test_execute_ruby_data[/root/chef/chef-repo/cookbooks/ruby_data_test/recipes/default.rb]
Running handlers:
Running handlers complete
Chef Client finished, 1/1 resources updated in 1.387608 seconds
Is there any way to define CoffeeScript compilation order in sub-directories?
Please consider the following example:
Files:
src/App.coffee
src/view/B.coffee
src/view/a/A.coffee
Where class A extends B.
coffee --join js/app.js --compile src/view/ src/App.coffee
This throws an error in the browser:
Uncaught TypeError: Cannot read property 'prototype' of undefined
If I rename folder a to z, the error is gone and everything works fine.
src/view/z/A.coffee
I would expect the compiler to read all .coffee files from src/view/ before it goes into src/view/ sub-directories. Again, is there any way to do that?
Edit:
PC Windows 7,
CoffeeScript version 1.3.3
The only solution I think is to create the compile order manually within a build script.
You would create an ordered collection with filenames, where as the loop iterates and concatenates a new big string, which can be compiled as one file.
Create a Cakefile with following content, check Syntax first. And run with cake build. That should work, cake comes with CoffeeScript.
fs = require 'fs'
{exec} = require 'child_process'
viewsDir = "src/view"
coffeeFiles = [
'B'
'A'
]
task 'build'
# loops through coffeeFiles.
for file, index in coffeeFiles then do (file, index) ->
fs.readFile "#{viewsDir}/#{file}", 'utf8', (err, content) ->
appCoffee[index] = content
compile() if --remaining is 0
compile = ->
fs.writeFile 'js/app.coffee', appCoffee.join('\n\n'), 'utf8', (err) ->
throw err if err
exec 'coffee --compile js/app.coffee', (err, stdout, stderr) ->
throw err if err
console.log stdout + stderr
# you can skip deleting the app.coffee file
fs.unlink 'js/app.coffee', (err) ->
throw err if err
console.log 'Created app.coffe, compiled to app.js and removes app.coffee'
# maybe additional taks
# invoke 'test'
Documented also in Wiki of Coffeescript https://github.com/jashkenas/coffee-script/wiki/[HowTo]-Compiling-and-Setting-Up-Build-Tools
Before first loop you could also make it loop through different directories. And just list filenames in coffeeFiles to be processed before the others not in listed and the rest could be added to list with fs.readDir().
We created a simple module to solve a similar problem:
https://github.com/Vizir/rehab
Just put #_require [filename].coffee on your file and you're done.
We are using it in productions with complex dependency graphs.
I would like to make a Cakefile task to watch some CoffeeScript files just like if I had run coffee -c -w js/*.coffee.
Its watching and recompiling them successfully, but it doesn't log the usual output to the terminal when there's a compile error like it would if I just ran the script from the terminal. Any idea how to make this happen?
exec = require('child_process').exec
task 'watch','watch all files and compile them as needed', (options) ->
exec 'coffee -c -w js/*.coffee', (err,stdout, stderr) ->
console.log stdout
Also, if there's a better way to invoke a coffeescript command from a cakefile than running 'exec' please post that too.
spawn instead of exec?
{spawn} = require 'child_process'
task 'watch', -> spawn 'coffee', ['-cw', 'js'], customFds: [0..2]
I've used spawn to solve this, here is an example cake file:
{spawn, exec} = require 'child_process'
option '-p', '--prefix [DIR]', 'set the installation prefix for `cake install`'
task 'build', 'continually build with --watch', ->
coffee = spawn 'coffee', ['-cw', '-o', 'lib', 'src']
coffee.stdout.on 'data', (data) -> console.log data.toString().trim()
You can see it in action with the docco project:
https://github.com/jashkenas/docco/blob/master/Cakefile
The problem with your original code was that exec only calls its callback once—after the child process has terminated. (The Node docs aren't so clear on this.) So instead of defining that callback, you should instead try
child = exec 'coffee -c -w js/*.coffee'
child.stdout.on 'data', (data) -> sys.print data
Let me know if that works for you.