.NET/C# – TEE filter that also runs on Windows (XP) Embedded – update
Posted by jpluimers on 2010/04/15
Last week, I posted a C# implementation of the tee filter from Sterling W. “Chip” Camden.
Since then I have modified it slightly.
Not because the implementation is bad, but because some pieces of software play dirty when saving their redirected output.
One of those applications is SubInAcl, otherwise a great tool for showing and modifying ACL and Ownership information of Windows NT objects (files, registry entries, etc).
However, when redirecing output or piping it, it writes a zero byte after each byte of text.
I’m not sure why: it might try to face some Unicode output, or just be buggy.
The new sourcecode is below. You can also download the project and binary as tee.C#.7z (you need the freeware 7zip compression tool to decompress this).
For instance, see the output (hexadecimal and ASCII) of this command
subinacl > output.txt
000000: 53 00 75 00 62 00 49 00 6E 00 41 00 63 00 6C 00 S.u.b.I.n.A.c.l. 000010: 20 00 76 00 65 00 72 00 73 00 69 00 6F 00 6E 00 .v.e.r.s.i.o.n. 000020: 20 00 35 00 2E 00 32 00 2E 00 33 00 37 00 39 00 .5...2...3.7.9. 000030: 30 00 2E 00 31 00 31 00 38 00 30 00 0D 00 0A 00 0...1.1.8.0..... 000040: 0D 00 0A 00 49 00 6E 00 76 00 61 00 6C 00 69 00 ....I.n.v.a.l.i. 000050: 64 00 20 00 41 00 72 00 67 00 75 00 6D 00 65 00 d. .A.r.g.u.m.e. 000060: 6E 00 74 00 20 00 21 00 0D 00 0A 00 55 00 73 00 n.t. .!.....U.s. 000070: 65 00 20 00 3A 00 0D 00 0A 00 53 00 75 00 62 00 e. .:.....S.u.b. 000080: 49 00 6E 00 61 00 63 00 6C 00 20 00 2F 00 68 00 I.n.a.c.l. ./.h. 000090: 65 00 6C 00 70 00 20 00 74 00 6F 00 20 00 67 00 e.l.p. .t.o. .g. 0000A0: 65 00 74 00 20 00 74 00 68 00 65 00 20 00 75 00 e.t. .t.h.e. .u. 0000B0: 73 00 61 00 67 00 65 00 20 00 69 00 6E 00 66 00 s.a.g.e. .i.n.f. 0000C0: 6F 00 72 00 6D 00 61 00 74 00 69 00 6F 00 6E 00 o.r.m.a.t.i.o.n. 0000D0: 0D 00 0A 00 6F 00 72 00 0D 00 0A 00 53 00 75 00 ....o.r.....S.u. 0000E0: 62 00 49 00 6E 00 41 00 63 00 6C 00 20 00 2F 00 b.I.n.A.c.l. ./. 0000F0: 68 00 65 00 6C 00 70 00 20 00 73 00 79 00 6E 00 h.e.l.p. .s.y.n. 000100: 74 00 61 00 78 00 20 00 74 00 6F 00 20 00 75 00 t.a.x. .t.o. .u. 000110: 6E 00 64 00 65 00 72 00 73 00 74 00 61 00 6E 00 n.d.e.r.s.t.a.n. 000120: 64 00 20 00 53 00 75 00 62 00 49 00 6E 00 41 00 d. .S.u.b.I.n.A. 000130: 63 00 6C 00 20 00 73 00 79 00 6E 00 74 00 61 00 c.l. .s.y.n.t.a. 000140: 78 00 2E 00 0D 00 0A 00 x.......
I modified the C# version of tee so when piped through tee, you get the output like this: all the zero bytes have been removed.
000000: 53 75 62 49 6E 41 63 6C 20 76 65 72 73 69 6F 6E SubInAcl version 000010: 20 35 2E 32 2E 33 37 39 30 2E 31 31 38 30 0D 0A 5.2.3790.1180.. 000020: 0D 0A 49 6E 76 61 6C 69 64 20 41 72 67 75 6D 65 ..Invalid Argume 000030: 6E 74 20 21 0D 0A 55 73 65 20 3A 0D 0A 53 75 62 nt !..Use :..Sub 000040: 49 6E 61 63 6C 20 2F 68 65 6C 70 20 74 6F 20 67 Inacl /help to g 000050: 65 74 20 74 68 65 20 75 73 61 67 65 20 69 6E 66 et the usage inf 000060: 6F 72 6D 61 74 69 6F 6E 0D 0A 6F 72 0D 0A 53 75 ormation..or..Su 000070: 62 49 6E 41 63 6C 20 2F 68 65 6C 70 20 73 79 6E bInAcl /help syn 000080: 74 61 78 20 74 6F 20 75 6E 64 65 72 73 74 61 6E tax to understan 000090: 64 20 53 75 62 49 6E 41 63 6C 20 73 79 6E 74 61 d SubInAcl synta 0000A0: 78 2E 0D 0A x...
This is the change in the sourcecode:
if (0 == b) //##jpl: 20100407 - bugfix for SubInACL (it fakes Unicode output by putting extra null bytes in the output)
continue;
This is the complete sourcecode:
using System;
using System.IO;
using System.Collections.Generic;
// Sends standard input to standard output and to all files in command line.
// C# implementation april 4th, 2010 by Jeroen Wiert Pluimers
// https://wiert.wordpress.com
// based on tee by Chip Camden, Camden Software Consulting, November 2005
// ... and Anonymous Cowards everywhere!
// http://www.camdensoftware.com
// http://chipstips.com/?tag=cpptee
//
// TEE [-a | --append] [-i | --ignore] [--help | /?] [-f] [file1] [...]
// Example:
// tee --append file0.txt -f --help file2.txt
// will append to file0.txt, --help, and file2.txt
//
// -a | --append Appends files instead of overwriting
// (setting is per tee instance)
// -i | --ignore Ignore cancel Ctrl+C keypress: see UnixUtils tee
// /? | --help Displays this message and immediately quits
// -f Stop recognizing flags, force all following filenames literally
//
// Duplicate filenames are quietly ignored.
// Press Ctrl+Z (End of File character) then Enter to abort.
namespace tee
{
class Program
{
static void help()
{
Console.Error.WriteLine("Sends standard input to standard output and to all files in command line.");
Console.Error.WriteLine("C# implementation april 4th, 2010 by Jeroen Wiert Pluimers (https://wiert.wordpress.com");
Console.Error.WriteLine("https://wiert.wordpress.com");
Console.Error.WriteLine("based on tee by Chip Camden, Camden Software Consulting, November 2005");
Console.Error.WriteLine(" ... and Anonymous Cowards everywhere!");
Console.Error.WriteLine("http://www.camdensoftware.com");
Console.Error.WriteLine("http://chipstips.com/?tag=cpptee");
Console.Error.WriteLine("");
Console.Error.WriteLine("tee [-a | --append] [-i | --ignore] [--help | /?] [-f] [file1] [...]");
Console.Error.WriteLine(" Example:");
Console.Error.WriteLine(" tee --append file0.txt -f --help file2.txt");
Console.Error.WriteLine(" will append to file0.txt, --help, and file2.txt");
Console.Error.WriteLine("");
Console.Error.WriteLine("-a | --append Appends files instead of overwriting");
Console.Error.WriteLine(" (setting is per tee instance)");
Console.Error.WriteLine("-i | --ignore Ignore cancel Ctrl+C keypress: see UnixUtils tee");
Console.Error.WriteLine("/? | --help Displays this message and immediately quits");
Console.Error.WriteLine("-f Stop recognizing flags, force all following filenames literally");
Console.Error.WriteLine("");
Console.Error.WriteLine("Duplicate filenames are quietly ignored.");
Console.Error.WriteLine("Press Ctrl+Z (End of File character) then Enter to abort.");
}
static void OnCancelKeyPressed(Object sender, ConsoleCancelEventArgs args)
{
// Set the Cancel property to true to prevent the process from
// terminating.
args.Cancel = true;
}
static List<String> filenames = new List<String>();
static void addFilename(string value)
{
if (-1 == filenames.IndexOf(value))
filenames.Add(value);
}
static int Main(string[] args)
{
try
{
bool appendToFiles = false;
bool stopInterpretingFlags = false;
bool ignoreCtrlC = false;
foreach (string arg in args)
{
//Since we're already parsing.... might as well check for flags:
if (stopInterpretingFlags) //Stop interpreting flags, assume is filename
{
addFilename(arg);
}
else if (arg.Equals("/?") || arg.Equals("-h") || arg.Equals("--help"))
{
help();
return 1; //Quit immediately
}
else if (arg.Equals("-a") || arg.Equals("--append"))
{
appendToFiles = true;
}
else if (arg.Equals("-i") || arg.Equals("--ignore"))
{
ignoreCtrlC = true;
}
else if (arg.Equals("-f"))
{
stopInterpretingFlags = true;
}
else
{ //If it isn't any of the above, it's a filename
addFilename(arg);
}
//Add more flags as necessary, just remember to SKIP adding them to the file processing stream!
}
if (ignoreCtrlC) //Implement the Ctrl+C fix selectively (mirror UnixUtils tee behavior)
Console.CancelKeyPress += new ConsoleCancelEventHandler(OnCancelKeyPressed);
List<BinaryWriter> binaryWriters = new List<BinaryWriter>(filenames.Count); //Add only as many streams as there are distinct files
try
{
foreach (String filename in filenames)
{
binaryWriters.Add(new BinaryWriter(appendToFiles ?
File.AppendText(filename).BaseStream :
File.Create(filename))); // Open the files specified as arguments
}
using (BinaryReader stdin = new BinaryReader(Console.OpenStandardInput()))
{
using (BinaryWriter stdout = new BinaryWriter(Console.OpenStandardOutput()))
{
Byte b;
while (true)
{
try
{
b = stdin.ReadByte(); // Read standard in
}
catch (EndOfStreamException)
{
break;
}
if (0 == b) //##jpl: 20100407 - bugfix for SubInACL (it fakes Unicode output by putting extra null bytes in the output)
continue;
// The actual tee:
stdout.Write(b); // Write standard out
foreach (BinaryWriter binaryWriter in binaryWriters)
{
binaryWriter.Write(b); // Write to each file
}
}
}
}
}
finally
{
foreach (BinaryWriter binaryWriter in binaryWriters)
{
binaryWriter.Flush(); // Flush and close each file
binaryWriter.Close();
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(String.Concat("tee: ", ex.Message)); // Send error messages to stderr
}
return 0;
}
}
}
–jeroen






Leave a comment