LINQPad Command-Line and Scripting

LINQPad ships with lprun for a full command-line experience.

lprun is suffixed with the LINQPad major version; the latest is lprun8.exe.

Here are some examples to get you started:

lprun8 foo.linq
Executes foo.linq, writing the result to the Console.
lprun8 -format=html foo.linq > output.html
Formats the output as HTML and redirects it to output.html.
lprun8 "queries\foo bar.linq" CustomArg
Runs foo bar.linq, passing 'CustomArg' into its main method.

Output Formatting

The default output format is plain text. Lists and objects with properties are formatted as JSON. To change the output format, use the -format option:

-format={text|html|htmlfrag|csv|csvi}

html returns complete HTML that can be written to a file, whereas htmlfrag returns a fragment that can be inserted into an existing HTML document.

csv renders simple lists in Excel-friendly CSV. csvi is the same, but forces culture-insensitive formatting. Csv/csvi generates the same output as LINQPad's Util.WriteCsv method.

Output Redirection

To redirect output, use the standard > operator:

lprun8 foo.linq > output.txt

Error messages and warnings are written to stderr and so are not redirected, unless you request otherwise.

Another way to write output to a file is to have the script itself do the job. To make this easier, LINQPad has the following new methods in the Util class:

  • Util.ToCsvString (IEnumerable<T> elements, ...)
  • Util.ToHtmlString (params object[] objectsToDump, ...)

The latter pushes the objects you supply through LINQPad's standard Dump-to-HTML pipeline. For instance:

File.WriteAllText (@"c:\temp\foo.html", Util.ToHtmlString (myQuery));

There's also a method called Util.WriteCsv for efficiently streaming large data sets directly to a file or TextWriter in CSV format. To stream HTML to a file, use Util.CreateXhtmlWriter() and then call its Write/WriteLine methods.

Input

Console.ReadLine accepts user input just as you'd expect. And you can pipe the output of one query into another:

lprun8 script1.linq | lprun8 script2.linq

More on piping later.

Error Handling

Errors and warnings are written to stderr (Console.Error). Errors set the %errorlevel% variable:
lprun8 test.linq
if %errorlevel% neq 0 echo Error!

Plain Text Scripts

lprun will execute plain-text files (without an XML header) if you tell it the language via the -lang switch. The valid options are:

Expression, Statements, Program, VBExpression, VBStatements, VBProgram, FSharpExpression, FSharpProgram, SQL, ESQL

The first three can be abbreviated to their first letter. For example, the following prints 24:

echo 12+12 > script.linq
lprun8 -lang=e script.linq

Without the -lang=e switch, you would need to include an XML header in script.linq:

<Query Kind="Expression" />

12+12

The XML header describes properties of the query, such as its language, connection, namespaces to import, etc. Not having a header can save a bit of typing with ad-hoc scripts. It also lets you execute scripts written for scriptcs.

Connections

For plain-text queries that require a connection, you must specify the connection name with the -cxname option. (Queries with an XML header don't require this because the connection details are stored in the header, but specifying a connection can still be useful for overriding the connection.) The name of a connection is as it appears in LINQPad's Schema Explorer tree. (You can rename a connection by right-clicking it and choosing 'Rename').

echo Customers.Take(100) > script.linq
lprun8 -lang=e -cxname=CustomerDB script.linq

If the connection refers to a server rather than a database, add a period, followed by the name of the db:

echo Customers.Take(100) > script.linq
lprun8 -lang=e -cxname=CompanyServer.CustomerDb script.linq

Connection details are normally stored in %appdata%\LINQPad\ConnectionsV2.xml. However, if you move/copy this file into the folder where LINQPad.exe resides, LINQPad will use that copy instead. (This makes life easy when xcopy-deploying LINQPad for portable or shared deployments.) And if you put the scripts into a subfolder called queries, the LINQPad GUI will show these in the 'My Queries' treeview.

Namespaces

To import additional namespaces in plain-text queries, LINQPad supports using directives:

using Foo.Bar;
using Bar.Foo;
	
Customers.Take(100)

Assemblies and NuGet

LINQPad also supports the ref directive for referencing additional assemblies. You can specify the assembly either via its filename (including the directory if it's outside the .NET Framework) or its fully qualified name (if it's in the GAC):

ref System.Windows.Forms.dll;
ref System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a;

using System.Windows.Forms;
using System.Drawing;
	
new Label().Anchor

(ref directives must precede using directives.)

Best of all, you can reference NuGet assemblies by prefixing the package ID with nuget:

ref nuget:System.Reactive;
using System.Reactive.Linq;
			
Observable.Range(1,10).AsEnumerable()

Here's the same thing as a .linq file

<Query Kind="Expression">
   <NuGetReference>Rx-Main</NuGetReference>
   <Namespace>System.Reactive.Linq</Namespace>
</Query>

Observable.Range(1,10).AsEnumerable()

lprun will automatically download the required NuGet packages and dependencies upon first execution. (This will work whether or not you have a registered edition of LINQPad.) And if you want to force lprun to download them again (to update to a later version, for instance), use the -nunuget switch.

Passing Arguments To Your Scripts

Any command-line arguments that follow the script path are ignored by lprun and instead forwarded to your script. You can pick these up by writing a query whose language is 'Program' and writing a main method that accepts a string array, like this:

<Query Kind="Program" />

void Main (string[] args)
{
   args.Dump();
}

Assuming that file was called script.linq, the following would write [ "Hello", "World" ]:

lprun8 script.linq Hello World

If the script didn't have the XML header, you'd call it as follows

lprun8 -lang=program script.linq Hello World

The CMD symbol

If you need to programmatically determine whether you're running in the GUI or from the command line, you can do this as follows:

#if CMD
   "I'm been called from lprun!".Dump();
#else
   "I'm running in the LINQPad GUI!".Dump();
#endif

Compilation Options

To enable compiler optimizations, use the -optimize switch. This incurs the usual trade-off: slightly faster execution with compute-intensive code in exchange for less accurate error reporting.

lprun also supports the -warn switch to output compiler warnings. Warnings are written to stderr (Console.Error) so they will not find their way into output if the redirection operator (>) is used.

The -compileonly switch tells lprun to check that the query will compile, without actually running anything.

Composing and Piping

You can use the standard pipe (|) operator to feed the output of one query into the input of another. For example, suppose you want to email the output of a query. The first step is to write a query called SendMail.linq that emails the content of Console.In, using command-line arguments for the host, sender, recipient, and subject:

<Query Kind="Program">
   <Namespace>System.Net.Mail</Namespace>
</Query>

void Main (string[] args)
{
   new SmtpClient (args[0]).Send (from:args[1], recipients:args[2], subject:args[3],
                                  body:Console.In.ReadToEnd());
}

If you write such a script via the LINQPad GUI, you'll want to be able to test it just by hitting F5. LINQPad accepts console input in GUI mode (press Ctrl+Z to 'end' the input), so there's no problem with getting input, but we need some way to supply command-line arguments. The easiest solution is to leverage the CMD symbol we talked about earlier:

<Query Kind="Program">
   <Namespace>System.Net.Mail</Namespace>
</Query>

void Main (string[] args)
{
#if !CMD
   args = new[] { "testhost", "test@foo.com", "test@foo.com", "Test Subject" };
#endif
   new SmtpClient (args[0]).Send (from:args[1], recipients:args[2], subject:args[3],
                                  body:Console.In.ReadToEnd());
}

You can now pipe another query into this. For example

lprun8 -format=html foo.linq | lprun8 SendMail.linq testhost from@foo.com to@foo.com "Test Subject"

Calling One Script From Another

Another way to combine scripts is to dynamically execute one script from another. The Util.Run method does exactly that, and is useful in both interactive and command-line scenarios:

string htmlResult = Util.Run ("test.linq", QueryResultFormat.Html).AsString();
Note: If you feed Util.Run a relative path, it will resolve it relative to the 'My Queries' directory rather than the current directory. You can switch its behavior by specifying .\test.linq instead of test.linq in this example.

Extending the previous example, we could then e-mail the result to someone:

new SmtpClient ("myhost").Send ("test@foo.com", "test@foo.com", "Test Subject", htmlResult);

Util.Run returns an object of type QueryExecuter, which exposes methods to extract the result in various ways. There's even a AsMailAttachment method which assists with emailing results as attachments:

var mm = new MailMessage (...);
mm.Attachments.Add (Util.Run (@"test.linq", QueryResultFormat.Html).AsMailAttachment("test"));
new SmtpClient ("myhost").Send (mm);

To pass custom arguments to a query, just include them when calling Util.Run:

var mm = new MailMessage (...);

for (int i = 0; i < 5; i++)
{
   var attach = Util.Run (@"test.linq", QueryResultFormat.Html, i.ToString())
                    .AsMailAttachment ("test" + i);

   mm.Attachments.Add (attach);
}

new SmtpClient ("myhost").Send (mm);

The latter query sends a mail message with five attachments, each being the result of calling the test.linq query with an argument ranging from 0 to 4.

QueryExecuter also exposes asynchronous versions of its methods. This is useful for efficiently executing queries in parallel. For instance, we can parallelize the preceding script as follows:

var tasks = 
   from i in Enumerable.Range (0, 5)
   select Util.Run (@"test.linq", QueryResultFormat.Html, i.ToString())
              .AsMailAttachmentAsync ("test" + i));

var attachments = await Task.WhenAll (tasks);
	
var mm = new MailMessage (...);
foreach (var attach in attachments) mm.Attachments.Add (attach);
new SmtpClient ("myhost").Send (mm);

You can also dump a QueryExecuter object itself, in which case the output will be merged into the current query's output. This is particularly useful with the HTML formatting option.

For example, the following script executes TestQuery.linq five times in parallel, collating the output into a single HTML table:

from i in Enumerable.Range (0, 5)
select Util.Run (@"TestQuery.linq", QueryResultFormat.Html)