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.
|
Relative file paths are resolved with respect to the current directory, or if that fails, the My Queries folder.
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 that fails to resolve with respect to Environment.CurrentDirectory,
LINQPad will try to resolve it relative to the 'My Queries' directory, and if that fails, the directory in which caller's query was saved (if any).
Specifying .\test.linq instead of test.linq disables the 'My Queries' resolution step.
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)