diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
index 8c6219e025259da327baa3ef5e0c2db103981188..917a24bf4a5a52e7146a01577dc495f2eff96261 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java
@@ -22,6 +22,12 @@
  */
 package com.oracle.truffle.r.engine;
 
+import java.io.ByteArrayOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 
@@ -31,6 +37,8 @@ import com.oracle.truffle.api.frame.Frame;
 import com.oracle.truffle.api.frame.FrameInstance.FrameAccess;
 import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.nodes.RootNode;
+import com.oracle.truffle.r.engine.shell.RCommand;
+import com.oracle.truffle.r.engine.shell.RscriptCommand;
 import com.oracle.truffle.r.nodes.RASTBuilder;
 import com.oracle.truffle.r.nodes.RASTUtils;
 import com.oracle.truffle.r.nodes.RRootNode;
@@ -658,4 +666,106 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess {
         return DebugHandling.undebug(func);
     }
 
+    @Override
+    public Object rcommandMain(String[] args, boolean intern) {
+        IORedirect redirect = handleIORedirect(args, intern);
+        Object result = RCommand.doMain(redirect.args, false, redirect.in, redirect.out);
+        return redirect.getInternResult(result);
+    }
+
+    @Override
+    public Object rscriptMain(String[] args, boolean intern) {
+        IORedirect redirect = handleIORedirect(args, intern);
+        Object result = RscriptCommand.doMain(redirect.args, false, redirect.in, redirect.out);
+        return redirect.getInternResult(result);
+    }
+
+    private static final class IORedirect {
+        private final InputStream in;
+        private final OutputStream out;
+        private final String[] args;
+        private final boolean intern;
+
+        private IORedirect(InputStream in, OutputStream out, String[] args, boolean intern) {
+            this.in = in;
+            this.out = out;
+            this.args = args;
+            this.intern = intern;
+        }
+
+        private Object getInternResult(Object result) {
+            if (intern) {
+                int status = (int) result;
+                ByteArrayOutputStream bos = (ByteArrayOutputStream) out;
+                String s = new String(bos.toByteArray());
+                RStringVector sresult = s.length() == 0 ? RDataFactory.createEmptyStringVector() : RDataFactory.createStringVectorFromScalar(s);
+                if (status != 0) {
+                    sresult.setAttr("status", RDataFactory.createIntVectorFromScalar(status));
+                }
+                return sresult;
+            } else {
+                return result;
+            }
+
+        }
+    }
+
+    private static IORedirect handleIORedirect(String[] args, boolean intern) {
+        /*
+         * This code is primarily intended to handle the "system" .Internal so the possible I/O
+         * redirects are taken from the system/system2 R code. N.B. stdout redirection is never set
+         * if "intern == true. Both input and output can be redirected to /dev/null.
+         */
+        InputStream in = System.in;
+        OutputStream out = System.out;
+        ArrayList<String> newArgsList = new ArrayList<>();
+        int i = 0;
+        while (i < args.length) {
+            String arg = args[i];
+            if (arg.equals("<")) {
+                String file;
+                if (i < args.length - 1) {
+                    file = Utils.tildeExpand(Utils.unShQuote(args[i + 1]));
+                } else {
+                    throw RError.error(RError.NO_CALLER, RError.Message.GENERIC, "redirect missing");
+                }
+                try {
+                    in = new FileInputStream(file);
+                } catch (IOException ex) {
+                    throw RError.error(RError.NO_CALLER, RError.Message.NO_SUCH_FILE, file);
+                }
+                arg = null;
+                i++;
+            } else if (arg.startsWith("2>")) {
+                if (arg.equals("2>&1")) {
+                    // happens anyway
+                } else {
+                    assert !intern;
+                    throw RError.nyi(RError.NO_CALLER, "stderr redirect");
+                }
+                arg = null;
+            } else if (arg.startsWith(">")) {
+                assert !intern;
+                arg = null;
+                throw RError.nyi(RError.NO_CALLER, "stdout redirect");
+            }
+            if (arg != null) {
+                newArgsList.add(arg);
+            }
+            i++;
+        }
+        String[] newArgs;
+        if (newArgsList.size() == args.length) {
+            newArgs = args;
+        } else {
+            newArgs = new String[newArgsList.size()];
+            newArgsList.toArray(newArgs);
+        }
+        // to implement intern, we create a ByteArryOutputStream to capture the output
+        if (intern) {
+            out = new ByteArrayOutputStream();
+        }
+        return new IORedirect(in, out, newArgs, intern);
+    }
+
 }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java
index 0e5040ee582fd8423eda42aeb596e77094ab5c8d..080dde533c0fc60ecdedd405382a14c34983badd 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/EmbeddedConsoleHandler.java
@@ -58,7 +58,7 @@ public class EmbeddedConsoleHandler implements ConsoleHandler {
                 if (startParams.getNoReadline()) {
                     delegate = new DefaultConsoleHandler(System.in, System.out);
                 } else {
-                    delegate = new JLineConsoleHandler(startParams);
+                    delegate = new JLineConsoleHandler(startParams, System.in, System.out);
                 }
             }
         }
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java
index dc3db25f769838496fc5eb497fa461721ed4e104..b43a2c2e2911c12453ab5b330f91c635124c2b67 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/JLineConsoleHandler.java
@@ -24,6 +24,8 @@ package com.oracle.truffle.r.engine.shell;
 
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.io.PrintWriter;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
@@ -42,9 +44,9 @@ class JLineConsoleHandler implements ConsoleHandler {
     private final boolean isInteractive;
     private final PrintWriter printWriter;
 
-    JLineConsoleHandler(RStartParams startParams) {
+    JLineConsoleHandler(RStartParams startParams, InputStream inStream, OutputStream outStream) {
         try {
-            console = new ConsoleReader(System.in, System.out);
+            console = new ConsoleReader(inStream, outStream);
             console.setHandleUserInterrupt(true);
             console.setExpandEvents(false);
         } catch (IOException ex) {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java
index 04c66c9f1e98f0a38d1966e699a4142dfe2392ad..c62f93d7221477f9421f4419f702c46d2ffb19a1 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RCommand.java
@@ -29,6 +29,8 @@ import java.io.Console;
 import java.io.EOFException;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.nio.file.Files;
 import java.util.List;
 
@@ -66,14 +68,18 @@ public class RCommand {
     // CheckStyle: stop system..print check
 
     public static void main(String[] args) {
-        RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, false);
-        options.printHelpAndVersion();
-        PolyglotEngine vm = createPolyglotEngineFromCommandLine(options, false);
+        doMain(args, true, System.in, System.out);
         // never returns
-        readEvalPrint(vm);
         throw RInternalError.shouldNotReachHere();
     }
 
+    public static int doMain(String[] args, boolean initial, InputStream inStream, OutputStream outStream) {
+        RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, false);
+        options.printHelpAndVersion();
+        PolyglotEngine vm = createPolyglotEngineFromCommandLine(options, false, initial, inStream, outStream);
+        return readEvalPrint(vm);
+    }
+
     /**
      * The standard R script escapes spaces to "~+~" in "-e" and "-f" commands.
      */
@@ -81,7 +87,7 @@ public class RCommand {
         return input.replace("~+~", " ");
     }
 
-    static PolyglotEngine createPolyglotEngineFromCommandLine(RCmdOptions options, boolean embedded) {
+    static PolyglotEngine createPolyglotEngineFromCommandLine(RCmdOptions options, boolean embedded, boolean initial, InputStream inStream, OutputStream outStream) {
         RStartParams rsp = new RStartParams(options, embedded);
 
         String fileArg = options.getString(FILE);
@@ -120,13 +126,21 @@ public class RCommand {
             List<String> lines;
             String filePath;
             try {
-                File file = new File(fileArg);
+                /*
+                 * If initial==false, ~ expansion will not have been done and the open will fail.
+                 * It's harmless to always do it.
+                 */
+                File file = new File(Utils.tildeExpand(fileArg));
                 lines = Files.readAllLines(file.toPath());
                 filePath = file.getCanonicalPath();
             } catch (IOException e) {
-                throw Utils.rSuicide("cannot open file '" + fileArg + "': " + e.getMessage());
+                if (initial) {
+                    throw Utils.rSuicide(String.format(RError.Message.NO_SUCH_FILE.message, fileArg));
+                } else {
+                    throw RError.error(RError.NO_CALLER, RError.Message.NO_SUCH_FILE, fileArg);
+                }
             }
-            consoleHandler = new StringConsoleHandler(lines, System.out, filePath);
+            consoleHandler = new StringConsoleHandler(lines, outStream, filePath);
         } else if (options.getStringList(EXPR) != null) {
             List<String> exprs = options.getStringList(EXPR);
             for (int i = 0; i < exprs.size(); i++) {
@@ -137,7 +151,7 @@ public class RCommand {
             }
             // cf GNU R
             rsp.setInteractive(false);
-            consoleHandler = new StringConsoleHandler(exprs, System.out, RSource.Internal.EXPRESSION_INPUT.string);
+            consoleHandler = new StringConsoleHandler(exprs, outStream, RSource.Internal.EXPRESSION_INPUT.string);
         } else {
             /*
              * GnuR behavior differs from the manual entry for {@code interactive} in that {@code
@@ -148,23 +162,28 @@ public class RCommand {
              * should be lazy, as these may not be necessary and can cause hangs if stdin has been
              * redirected.
              */
-            Console sysConsole = System.console();
+            Console sysConsole = System.console(); // TODO fix for context sessions
             boolean isInteractive = options.getBoolean(INTERACTIVE) || sysConsole != null;
             if (!isInteractive && rsp.getSaveAction() != SA_TYPE.SAVE && rsp.getSaveAction() != SA_TYPE.NOSAVE) {
-                throw Utils.rSuicide("you must specify '--save', '--no-save' or '--vanilla'");
+                String msg = "you must specify '--save', '--no-save' or '--vanilla'";
+                if (initial) {
+                    throw Utils.rSuicide(msg);
+                } else {
+                    throw RError.error(RError.NO_CALLER, RError.Message.GENERIC, msg);
+                }
             }
             if (embedded) {
                 consoleHandler = new EmbeddedConsoleHandler(rsp);
             } else {
                 boolean useReadLine = !rsp.getNoReadline();
                 if (useReadLine) {
-                    consoleHandler = new JLineConsoleHandler(rsp);
+                    consoleHandler = new JLineConsoleHandler(rsp, inStream, outStream);
                 } else {
-                    consoleHandler = new DefaultConsoleHandler(System.in, System.out);
+                    consoleHandler = new DefaultConsoleHandler(inStream, outStream);
                 }
             }
         }
-        return ContextInfo.create(rsp, ContextKind.SHARE_NOTHING, null, consoleHandler).createVM();
+        return ContextInfo.create(rsp, ContextKind.SHARE_NOTHING, initial ? null : RContext.getInstance(), consoleHandler).createVM();
     }
 
     private static final Source GET_ECHO = RSource.fromTextInternal("invisible(getOption('echo'))", RSource.Internal.GET_ECHO);
@@ -181,9 +200,10 @@ public class RCommand {
      * In case 2, we must implicitly execute a {@code quit("default, 0L, TRUE} command before
      * exiting. So,in either case, we never return.
      */
-    static void readEvalPrint(PolyglotEngine vm) {
+    static int readEvalPrint(PolyglotEngine vm) {
         int lastStatus = 0;
-        ConsoleHandler consoleHandler = ContextInfo.getContextInfo(vm).getConsoleHandler();
+        ContextInfo contextInfo = ContextInfo.getContextInfo(vm);
+        ConsoleHandler consoleHandler = contextInfo.getConsoleHandler();
         try {
             // console.println("initialize time: " + (System.currentTimeMillis() - start));
             REPL: for (;;) {
@@ -241,8 +261,13 @@ public class RCommand {
                                 throw (RuntimeException) cause;
                             } else if (cause instanceof ExitException) {
                                 // usually from quit
-                                vm.dispose();
-                                Utils.systemExit(((ExitException) cause).getStatus());
+                                int status = ((ExitException) cause).getStatus();
+                                if (contextInfo.getParent() == null) {
+                                    vm.dispose();
+                                    Utils.systemExit(status);
+                                } else {
+                                    return status;
+                                }
                             } else {
                                 RInternalError.reportErrorAndConsoleLog(cause, consoleHandler, 0);
                                 // We continue the repl even though the system may be broken
@@ -262,13 +287,18 @@ public class RCommand {
             } catch (Throwable e) {
                 if (e.getCause() instanceof ExitException) {
                     // normal quit, but with exit code based on lastStatus
-                    Utils.systemExit(lastStatus);
+                    if (contextInfo.getParent() == null) {
+                        Utils.systemExit(lastStatus);
+                    } else {
+                        return lastStatus;
+                    }
                 }
                 throw RInternalError.shouldNotReachHere(e);
             }
         } finally {
             vm.dispose();
         }
+        return 0;
     }
 
     private static boolean doEcho(PolyglotEngine vm) {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java
index c4a0903de44e7231885b14bfc2dcfb98a58a58e2..d200eba9cb35d29b2f5a45318e5ef5becb4c9876 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/REmbedded.java
@@ -70,7 +70,7 @@ public class REmbedded {
     private static PolyglotEngine initializeR(String[] args) {
         RContext.setEmbedded();
         RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.R, args, true);
-        PolyglotEngine vm = RCommand.createPolyglotEngineFromCommandLine(options, true);
+        PolyglotEngine vm = RCommand.createPolyglotEngineFromCommandLine(options, true, true, System.in, System.out);
         try {
             vm.eval(INIT);
         } catch (IOException ex) {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java
index 300f52e8acdf1ff2ae261947351ff9ba36d515bc..ce0f19aa0773a6387b9c3bb1050906ece806b1d2 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/RscriptCommand.java
@@ -29,6 +29,8 @@ import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.NO_RESTORE;
 import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.SLAVE;
 import static com.oracle.truffle.r.runtime.RCmdOptions.RCmdOption.VERSION;
 
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.ArrayList;
 
 import com.oracle.truffle.api.vm.PolyglotEngine;
@@ -97,14 +99,19 @@ public class RscriptCommand {
     }
 
     public static void main(String[] args) {
+        doMain(args, true, null, null);
+        // never returns
+        throw RInternalError.shouldNotReachHere();
+    }
+
+    public static int doMain(String[] args, boolean initial, InputStream inStream, OutputStream outStream) {
         // Since many of the options are shared parse them from an RSCRIPT perspective.
         // Handle --help and --version specially, as they exit.
         RCmdOptions options = RCmdOptions.parseArguments(RCmdOptions.Client.RSCRIPT, args, false);
         preprocessRScriptOptions(options);
-        PolyglotEngine vm = RCommand.createPolyglotEngineFromCommandLine(options, false);
-        // never returns
-        RCommand.readEvalPrint(vm);
-        throw RInternalError.shouldNotReachHere();
+        PolyglotEngine vm = RCommand.createPolyglotEngineFromCommandLine(options, false, initial, inStream, outStream);
+        return RCommand.readEvalPrint(vm);
+
     }
 
     private static void printVersionAndExit() {
diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java
index e2edc533d17467e02b41229e6293bc86c379f220..60af1f537c607a7152802ea422b86f8dd4459c78 100644
--- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java
+++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/shell/StringConsoleHandler.java
@@ -22,6 +22,7 @@
  */
 package com.oracle.truffle.r.engine.shell;
 
+import java.io.OutputStream;
 import java.io.PrintStream;
 import java.util.List;
 
@@ -35,9 +36,9 @@ class StringConsoleHandler implements ConsoleHandler {
     private String prompt;
     private int currentLine;
 
-    StringConsoleHandler(List<String> lines, PrintStream output, String inputDescription) {
+    StringConsoleHandler(List<String> lines, OutputStream output, String inputDescription) {
         this.lines = lines;
-        this.output = output;
+        this.output = output instanceof PrintStream ? (PrintStream) output : new PrintStream(output);
         this.inputDescription = inputDescription;
     }
 
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index 842c52397077ded5530639fd9aa1201a406e55a3..b808d98b94ced2e7e52e284aca02c8cac50f6ae0 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -44,6 +44,8 @@ import com.oracle.truffle.r.nodes.builtin.base.foreign.DotC;
 import com.oracle.truffle.r.nodes.builtin.base.foreign.DotCNodeGen;
 import com.oracle.truffle.r.nodes.builtin.base.foreign.ForeignFunctions;
 import com.oracle.truffle.r.nodes.builtin.base.foreign.ForeignFunctionsFactory;
+import com.oracle.truffle.r.nodes.builtin.base.system.SystemFunction;
+import com.oracle.truffle.r.nodes.builtin.base.system.SystemFunctionNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRContext;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRContextFactory;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRDebug;
@@ -278,6 +280,8 @@ public class BasePackage extends RBuiltinPackage {
         add(WithVisible.class, WithVisibleNodeGen::create);
         add(Exists.class, ExistsNodeGen::create);
         add(Expression.class, ExpressionNodeGen::create);
+        add(FastRContext.R.class, FastRContextFactory.RNodeGen::create);
+        add(FastRContext.Rscript.class, FastRContextFactory.RscriptNodeGen::create);
         add(FastRContext.CloseChannel.class, FastRContextFactory.CloseChannelNodeGen::create);
         add(FastRContext.CreateChannel.class, FastRContextFactory.CreateChannelNodeGen::create);
         add(FastRContext.Eval.class, FastRContextFactory.EvalNodeGen::create);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ContextSystemFunctionFactory.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ContextSystemFunctionFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..8a20180fa664298496617342b267e2abc18d2795
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ContextSystemFunctionFactory.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.system;
+
+import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.nodes.builtin.base.system.ContextSystemFunctionFactoryFactory.ContextRSystemFunctionNodeGen;
+import com.oracle.truffle.r.nodes.builtin.base.system.ContextSystemFunctionFactoryFactory.ContextRscriptSystemFunctionNodeGen;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRContext;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRContextFactory;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
+import com.oracle.truffle.r.runtime.nodes.RBaseNode;
+
+/**
+ * A variant that uses {@code .fastr.context.r} for R sub-processes and throws an error otherwise.
+ * Used in systems that do not support {@code ProcessBuilder}.
+ */
+public class ContextSystemFunctionFactory extends SystemFunctionFactory {
+    private abstract static class ContextSystemFunctionNode extends RBaseNode {
+        public abstract Object execute(VirtualFrame frame, Object command, Object intern);
+
+    }
+
+    public abstract static class ContextRSystemFunctionNode extends ContextSystemFunctionNode {
+        @Child FastRContext.R contextRNode;
+
+        private void initContextRNode() {
+            if (contextRNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                contextRNode = insert(FastRContextFactory.RNodeGen.create(null));
+            }
+
+        }
+
+        @Specialization
+        protected Object systemFunction(VirtualFrame frame, RAbstractStringVector args, boolean intern) {
+            initContextRNode();
+            Object result = contextRNode.execute(frame, args, intern);
+            return result;
+        }
+    }
+
+    public abstract static class ContextRscriptSystemFunctionNode extends ContextSystemFunctionNode {
+        @Child FastRContext.Rscript contextRscriptNode;
+
+        private void initContextRscriptNode() {
+            if (contextRscriptNode == null) {
+                CompilerDirectives.transferToInterpreterAndInvalidate();
+                contextRscriptNode = insert(FastRContextFactory.RscriptNodeGen.create(null));
+            }
+
+        }
+
+        @Specialization
+        protected Object systemFunction(VirtualFrame frame, RAbstractStringVector args, boolean intern) {
+            initContextRscriptNode();
+            Object result = contextRscriptNode.execute(frame, args, intern);
+            return result;
+        }
+    }
+
+    @Override
+    Object execute(VirtualFrame frame, String command, boolean intern) {
+        log(command, "Context");
+        String[] parts = command.split(" ");
+        String rcommand = isFastR(parts[0]);
+        if (rcommand != null) {
+            String[] args = new String[parts.length - 1];
+            System.arraycopy(parts, 1, args, 0, args.length);
+            ContextSystemFunctionNode node = rcommand.equals("R") ? ContextRSystemFunctionNodeGen.create() : ContextRscriptSystemFunctionNodeGen.create();
+            Object result = node.execute(frame, RDataFactory.createStringVector(args, RDataFactory.COMPLETE_VECTOR), intern);
+            return result;
+        } else {
+            throw RError.error(RError.NO_CALLER, RError.Message.GENERIC, command + " cannot be executed in a context");
+        }
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/PreferContextSystemFunctionFactory.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/PreferContextSystemFunctionFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..abead3646d06c1548a4658bb0d75be5b4163934c
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/PreferContextSystemFunctionFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+/**
+ * A variant that prefers to use {@code .fastr.context.r} for R sub-processes but backs off to
+ * {@code ProcessBuilder}.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.system;
+
+import com.oracle.truffle.api.frame.VirtualFrame;
+
+public class PreferContextSystemFunctionFactory extends SystemFunctionFactory {
+    private ContextSystemFunctionFactory contextSystemFunctionFactory;
+    private ProcessSystemFunctionFactory processSystemFunctionFactory;
+
+    @Override
+    Object execute(VirtualFrame frame, String command, boolean intern) {
+        String[] parts = command.split(" ");
+        String rcommand = isFastR(parts[0]);
+        if (rcommand == null) {
+            if (processSystemFunctionFactory == null) {
+                processSystemFunctionFactory = new ProcessSystemFunctionFactory();
+            }
+            return processSystemFunctionFactory.execute(frame, command, intern);
+        } else {
+            if (contextSystemFunctionFactory == null) {
+                contextSystemFunctionFactory = new ContextSystemFunctionFactory();
+            }
+            return contextSystemFunctionFactory.execute(frame, command, intern);
+
+        }
+    }
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ProcessSystemFunctionFactory.java
similarity index 76%
rename from com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java
rename to com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ProcessSystemFunctionFactory.java
index 0590d2becca1a0bc2cf94f3bafb57a962f85df7c..9be9ca380beaadf2f9c20a535849aadaeeb19a91 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/SystemFunction.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/ProcessSystemFunctionFactory.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -20,11 +20,7 @@
  * or visit www.oracle.com if you need additional information or have any
  * questions.
  */
-package com.oracle.truffle.r.nodes.builtin.base;
-
-import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
-import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
-import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+package com.oracle.truffle.r.nodes.builtin.base.system;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -32,31 +28,28 @@ import java.lang.ProcessBuilder.Redirect;
 import java.util.Map;
 
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
-import com.oracle.truffle.api.dsl.Specialization;
-import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.runtime.ProcessOutputManager;
-import com.oracle.truffle.r.runtime.RRuntime;
-import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
 import com.oracle.truffle.r.runtime.data.RStringVector;
-import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 
-@RBuiltin(name = "system", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"command", "intern"}, behavior = COMPLEX)
-public abstract class SystemFunction extends RBuiltinNode {
-    @Specialization
+public class ProcessSystemFunctionFactory extends SystemFunctionFactory {
+
+    @Override
+    Object execute(VirtualFrame frame, String command, boolean intern) {
+        return execute(command, intern);
+    }
+
     @TruffleBoundary
-    protected Object system(RAbstractStringVector command, byte internLogical) {
+    private Object execute(String command, boolean intern) {
         Object result;
-        boolean intern = RRuntime.fromLogical(internLogical);
         String shell = RContext.getInstance().stateREnvVars.get("SHELL");
         if (shell == null) {
             shell = "/bin/sh";
         }
-        if (RContext.getInstance().stateREnvVars.getMap().get("FASTR_LOGCHILD") != null) {
-            System.out.printf("FastR system: %s -c %s%n", shell, command.getDataAt(0));
-        }
-        ProcessBuilder pb = new ProcessBuilder(shell, "-c", command.getDataAt(0));
+        log(String.format("%s -c %s", shell, command), "Process");
+        ProcessBuilder pb = new ProcessBuilder(shell, "-c", command);
         updateEnvironment(pb);
         pb.redirectInput(Redirect.INHERIT);
         if (intern) {
@@ -110,4 +103,5 @@ public abstract class SystemFunction extends RBuiltinNode {
             }
         }
     }
+
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunction.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunction.java
new file mode 100644
index 0000000000000000000000000000000000000000..2b4d464807199ebf0eaf6898ce640aea8f900333
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunction.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.system;
+
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean;
+import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.INTERNAL;
+
+import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.nodes.builtin.CastBuilder;
+import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+
+@RBuiltin(name = "system", visibility = CUSTOM, kind = INTERNAL, parameterNames = {"command", "intern"}, behavior = COMPLEX)
+public abstract class SystemFunction extends RBuiltinNode {
+    @Override
+    protected void createCasts(CastBuilder casts) {
+        casts.arg("command").mustBe(stringValue(), RError.Message.SYSTEM_CHAR_ARG).asStringVector().findFirst();
+        casts.arg("intern").asLogicalVector().findFirst().notNA(RError.Message.SYSTEM_INTERN_NOT_NA).map(toBoolean());
+    }
+
+    @Specialization
+    protected Object system(VirtualFrame frame, String command, boolean intern) {
+        return SystemFunctionFactory.getInstance().execute(frame, command.trim(), intern);
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunctionFactory.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunctionFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..259a7f5166e40bdfe0a07a021dfdb30a4e833bde
--- /dev/null
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/system/SystemFunctionFactory.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.nodes.builtin.base.system;
+
+import java.io.IOException;
+import java.nio.file.FileSystems;
+import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
+import com.oracle.truffle.api.frame.VirtualFrame;
+import com.oracle.truffle.r.runtime.REnvVars;
+import com.oracle.truffle.r.runtime.Utils;
+import com.oracle.truffle.r.runtime.context.RContext;
+
+abstract class SystemFunctionFactory {
+    private static String kind;
+    private static SystemFunctionFactory theInstance;
+
+    static {
+        kind = System.getProperty("fastr.systemfunction.factory.kind", "Process");
+        String className = "com.oracle.truffle.r.nodes.builtin.base.system." + kind + "SystemFunctionFactory";
+        try {
+            theInstance = (SystemFunctionFactory) Class.forName(className).newInstance();
+        } catch (Exception ex) {
+            // CheckStyle: stop system..print check
+            Utils.rSuicide("Failed to instantiate class: " + className);
+        }
+    }
+
+    @TruffleBoundary
+    public static SystemFunctionFactory getInstance() {
+        return theInstance;
+    }
+
+    /**
+     * Implements the system {@code .Internal}. If {@code intern} is {@code true} the result is a
+     * character vector containing the output of the process, with a {@code status} attribute
+     * carrying the return code, else it is just the return code.
+     *
+     * {@code command} is a string with args separated by spaces with the first element enclosed in
+     * single quotes.
+     */
+    abstract Object execute(VirtualFrame frame, String command, boolean intern);
+
+    @TruffleBoundary
+    protected void log(String command, String useKind) {
+        if (RContext.getInstance().stateREnvVars.getMap().get("FASTR_LOG_SYSTEM") != null) {
+            System.out.printf("FastR system (%s): %s%n", useKind, command);
+        }
+
+    }
+
+    @TruffleBoundary
+    protected void log(String command) {
+        log(command, kind);
+    }
+
+    /**
+     * Returns {@code true} iff, {@code command} is {@code R} or {@code Rscript}.
+     */
+    protected static String isFastR(String command) {
+        // strip off quotes
+        String xc = Utils.unShQuote(command);
+        if (xc.equals("R") || xc.equals("Rscript")) {
+            return xc;
+        }
+        // often it is an absolute path
+        String rhome = REnvVars.rHome();
+        if (isFullPath(rhome, "Rscript", xc)) {
+            return "Rscript";
+        }
+        if (isFullPath(rhome, "R", xc)) {
+            return "R";
+        }
+        return null;
+    }
+
+    private static boolean isFullPath(String rhome, String rcmd, String command) {
+        try {
+            String rpath = FileSystems.getDefault().getPath(rhome, "bin", rcmd).toString();
+            String cpath = FileSystems.getDefault().getPath(command).toRealPath().toString();
+            if (cpath.equals(rpath)) {
+                return true;
+            }
+        } catch (IOException ex) {
+            // should not happen but just return false
+        }
+        return false;
+    }
+
+}
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
index d4e2fe9c14e54bcbd422b3c076807c755cc37efe..a45a017fe4c63a219c4d2646dfe1cd9b25be2514 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRContext.java
@@ -37,6 +37,7 @@ import com.oracle.truffle.r.runtime.RChannel;
 import com.oracle.truffle.r.runtime.RCmdOptions;
 import com.oracle.truffle.r.runtime.RCmdOptions.Client;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource;
 import com.oracle.truffle.r.runtime.RStartParams;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
@@ -55,6 +56,8 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
  */
 public class FastRContext {
 
+    private static final String[] EMPTY = new String[0];
+
     private abstract static class CastHelper extends RBuiltinNode {
         protected void exprs(CastBuilder casts) {
             casts.arg("exprs").asStringVector().mustBe(nullValue().not().and(notEmpty()));
@@ -65,10 +68,6 @@ public class FastRContext {
                             equalTo(RContext.ContextKind.SHARE_NOTHING.name()).or(equalTo(RContext.ContextKind.SHARE_PARENT_RW.name()).or(equalTo(RContext.ContextKind.SHARE_PARENT_RO.name()))));
         }
 
-        protected void args(CastBuilder casts) {
-            casts.arg("args").asStringVector().mustBe(nullValue().not().and(notEmpty()));
-        }
-
         protected void pc(CastBuilder casts) {
             casts.arg("pc").asIntegerVector().findFirst().notNA().mustBe(gt(0));
         }
@@ -89,11 +88,11 @@ public class FastRContext {
      * {@code .fastr.context.join}.
      *
      */
-    @RBuiltin(name = ".fastr.context.spawn", kind = PRIMITIVE, parameterNames = {"exprs", "pc", "kind", "args"}, behavior = COMPLEX)
+    @RBuiltin(name = ".fastr.context.spawn", kind = PRIMITIVE, parameterNames = {"exprs", "pc", "kind"}, behavior = COMPLEX)
     public abstract static class Spawn extends CastHelper {
         @Override
         public Object[] getDefaultParameterValues() {
-            return new Object[]{RMissing.instance, 1, "SHARE_NOTHING", ""};
+            return new Object[]{RMissing.instance, 1, "SHARE_NOTHING"};
         }
 
         @Override
@@ -101,17 +100,16 @@ public class FastRContext {
             exprs(casts);
             pc(casts);
             kind(casts);
-            args(casts);
         }
 
         @Specialization
         @TruffleBoundary
-        protected RIntVector spawn(RAbstractStringVector exprs, int pc, String kind, RAbstractStringVector args) {
+        protected RIntVector spawn(RAbstractStringVector exprs, int pc, String kind) {
             RContext.ContextKind contextKind = RContext.ContextKind.valueOf(kind);
             RContext.EvalThread[] threads = new RContext.EvalThread[pc];
             int[] data = new int[pc];
             for (int i = 0; i < pc; i++) {
-                ContextInfo info = createContextInfo(contextKind, args);
+                ContextInfo info = createContextInfo(contextKind);
                 threads[i] = new RContext.EvalThread(info, RSource.fromTextInternal(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL));
                 data[i] = info.getId();
             }
@@ -162,11 +160,11 @@ public class FastRContext {
      * sublist contains the result of the evaluation with name "result". It may also have an
      * attribute "error" if the evaluation threw an exception, in which case the result will be NA.
      */
-    @RBuiltin(name = ".fastr.context.eval", kind = PRIMITIVE, parameterNames = {"exprs", "pc", "kind", "args"}, behavior = COMPLEX)
+    @RBuiltin(name = ".fastr.context.eval", kind = PRIMITIVE, parameterNames = {"exprs", "pc", "kind"}, behavior = COMPLEX)
     public abstract static class Eval extends CastHelper {
         @Override
         public Object[] getDefaultParameterValues() {
-            return new Object[]{RMissing.instance, 1, "SHARE_NOTHING", ""};
+            return new Object[]{RMissing.instance, 1, "SHARE_NOTHING"};
         }
 
         @Override
@@ -174,24 +172,23 @@ public class FastRContext {
             exprs(casts);
             pc(casts);
             kind(casts);
-            args(casts);
         }
 
         @Specialization
         @TruffleBoundary
-        protected Object eval(RAbstractStringVector exprs, int pc, String kind, RAbstractStringVector args) {
+        protected Object eval(RAbstractStringVector exprs, int pc, String kind) {
             RContext.ContextKind contextKind = RContext.ContextKind.valueOf(kind);
 
             Object[] results = new Object[pc];
             if (pc == 1) {
-                ContextInfo info = createContextInfo(contextKind, args);
+                ContextInfo info = createContextInfo(contextKind);
                 PolyglotEngine vm = info.createVM();
                 results[0] = RContext.EvalThread.run(vm, info, RSource.fromTextInternal(exprs.getDataAt(0), RSource.Internal.CONTEXT_EVAL));
             } else {
                 // separate threads that run in parallel; invoking thread waits for completion
                 RContext.EvalThread[] threads = new RContext.EvalThread[pc];
                 for (int i = 0; i < pc; i++) {
-                    ContextInfo info = createContextInfo(contextKind, args);
+                    ContextInfo info = createContextInfo(contextKind);
                     threads[i] = new RContext.EvalThread(info, RSource.fromTextInternal(exprs.getDataAt(i % exprs.getLength()), RSource.Internal.CONTEXT_EVAL));
                 }
                 for (int i = 0; i < pc; i++) {
@@ -211,8 +208,57 @@ public class FastRContext {
 
     }
 
-    private static ContextInfo createContextInfo(RContext.ContextKind contextKind, RAbstractStringVector args) {
-        RStartParams startParams = new RStartParams(RCmdOptions.parseArguments(Client.RSCRIPT, args.materialize().getDataCopy(), false), false);
+    @RBuiltin(name = ".fastr.context.r", kind = PRIMITIVE, visibility = OFF, parameterNames = {"args", "intern"}, behavior = COMPLEX)
+    public abstract static class R extends CastHelper {
+        @Override
+        public Object[] getDefaultParameterValues() {
+            return new Object[]{RMissing.instance, RRuntime.LOGICAL_FALSE};
+        }
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("args").mustBe(missingValue().or(stringValue()));
+            casts.arg("intern").asLogicalVector().findFirst().map(toBoolean());
+        }
+
+        @Specialization
+        @TruffleBoundary
+        protected Object r(RAbstractStringVector args, boolean intern) {
+            Object rc = RContext.getRRuntimeASTAccess().rcommandMain(args.materialize().getDataCopy(), intern);
+            return rc;
+        }
+
+        @Specialization
+        protected Object r(@SuppressWarnings("unused") RMissing arg, boolean intern) {
+            return r(RDataFactory.createEmptyStringVector(), intern);
+        }
+
+    }
+
+    @RBuiltin(name = ".fastr.context.rscript", kind = PRIMITIVE, visibility = OFF, parameterNames = {"args", "intern"}, behavior = COMPLEX)
+    public abstract static class Rscript extends CastHelper {
+        @Override
+        public Object[] getDefaultParameterValues() {
+            return new Object[]{RMissing.instance, RRuntime.LOGICAL_FALSE};
+        }
+
+        @Override
+        protected void createCasts(CastBuilder casts) {
+            casts.arg("args").mustBe(stringValue(), RError.Message.GENERIC, "usage: /path/to/Rscript [--options] [-e expr [-e expr2 ...] | file] [args]").asStringVector();
+            casts.arg("intern").asLogicalVector().findFirst().map(toBoolean());
+        }
+
+        @Specialization
+        @TruffleBoundary
+        protected Object r(RAbstractStringVector args, boolean intern) {
+            Object rc = RContext.getRRuntimeASTAccess().rscriptMain(args.materialize().getDataCopy(), intern);
+            return rc;
+        }
+
+    }
+
+    private static ContextInfo createContextInfo(RContext.ContextKind contextKind) {
+        RStartParams startParams = new RStartParams(RCmdOptions.parseArguments(Client.RSCRIPT, EMPTY, false), false);
         ContextInfo info = ContextInfo.create(startParams, contextKind, RContext.getInstance(), RContext.getInstance().getConsoleHandler());
         return info;
     }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java
index e65017eed639b37248305c574090978ab300f228..657de9e74701338bfaad2c4e7fccf1be75fddc62 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RCmdOptions.java
@@ -368,7 +368,7 @@ public final class RCmdOptions {
 
     public static void printHelpAndExit(Client client) {
         printHelp(client);
-        System.exit(0);
+        throw new ExitException(0);
     }
 
     private static void printVersionAndExit() {
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index a2527d8e6e5a10fc63638a4654c25af041d201b2..19fdca79bccaab9ff7feb64cf32cd921e99aafab 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -688,6 +688,9 @@ public final class RError extends RuntimeException {
         ARGUMENTS_REQUIRED_COUNT("%d arguments to '%s' which requires %d"),
         ARGUMENT_LENGTH_0("argument of length 0"),
         MUST_BE_VECTOR_BUT_WAS("'%s' must be of a vector type, was '%s'"),
+        SYSTEM_CHAR_ARG("non-empty character argument expected"),
+        SYSTEM_INTERN_NOT_NA("'intern' must be logical and not NA"),
+        NO_SUCH_FILE("cannot open file '%s': No such file or directory"),
         CANNOT_BE_LENGTH("'%s' cannot be of length %d");
 
         public final String message;
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java
index 2e76ac5fd5339dc612c4b842641b8277b85af9b2..7b5e9d7f20ae0a45a6189d93fc9c4de86c1f3adb 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntimeASTAccess.java
@@ -191,4 +191,16 @@ public interface RRuntimeASTAccess {
 
     boolean isDebugged(RFunction func);
 
+    /*
+     * Support for R/RScript sessions ("processes") in an isolated RContext, see
+     * .fastr.context.r/rscript. The args are everything you might legally enter into a
+     * shell,including I/O redirection. The result is an integer status code if "intern==false",
+     * otherwise it is a character vector of the output, with a 'status' attribute containing the
+     * status code.
+     */
+
+    Object rcommandMain(String[] args, boolean intern);
+
+    Object rscriptMain(String[] args, boolean intern);
+
 }
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
index 35ed26d1d5d52539a19cbc667e585d10b4a8b45a..afd902095d2407dfb0b78ade92fe34b125d058e0 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java
@@ -304,6 +304,14 @@ public final class Utils {
         return tildeExpand(path, false);
     }
 
+    public static String unShQuote(String s) {
+        if (s.charAt(0) == '\'') {
+            return s.substring(1, s.length() - 1);
+        } else {
+            return s;
+        }
+    }
+
     /**
      * Retrieve a frame from the call stack. N.B. To avoid the iterator overhead use
      * {@link #getActualCurrentFrame()} for the current frame.