using System; using System.Collections.Generic; using System.Globalization; using System.Runtime.InteropServices; namespace RobvanderWoude { internal class ToString { static readonly string progver = "1.02"; static string template; static string decimalseparator = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator; static int Main( string[] args ) { if ( args.Length == 0 ) { return ShowHelp( ); } foreach ( string arg in args ) { if ( arg == "/?" ) { return ShowHelp( ); } } List arguments = new List( args ); template = arguments[0].Replace( "\\n", "\n" ).Replace( "\\r", "\r" ).Replace( "\\t", "\t" ); arguments.RemoveAt( 0 ); if ( Console.IsInputRedirected ) { if ( args.Length == 1 ) { int rc = 0; string[] lines = Console.In.ReadToEnd( ).Split( '\n' ); for ( int i = 0; i < lines.Length; i++ ) { if ( !string.IsNullOrWhiteSpace( lines[i] ) ) { rc += ParseLine( CommandLineToArgs( lines[i] ) ); } } if ( rc == 0 ) { return 0; } return -1; } else { return ShowHelp( "Specify only template on command line when using redirected arguments" ); } } else if ( args.Length > 1 ) { return ParseLine( arguments.ToArray( ) ); } else { return ShowHelp( ); } } static int ParseLine( string[] arguments ) { object[] values = new object[arguments.Length]; // copy of arguments[] but with different types for ( int i = 0; i < arguments.Length; i++ ) { if ( float.TryParse( arguments[i], out float floatvalue ) ) { if ( int.TryParse( arguments[i], out int intvalue ) ) { if ( floatvalue == intvalue ) { values[i] = intvalue; } else { values[i] = floatvalue; } } else { values[i] = floatvalue; } } else if ( DateTime.TryParse( arguments[i], out DateTime datetimevalue ) ) { values[i] = datetimevalue; } else if ( DateTime.TryParse( arguments[i].Replace( decimalseparator, "." ), out datetimevalue ) ) { // Work-around for issue with decimal separator in time strings (on Dutch systems) values[i] = datetimevalue; } else { values[i] = arguments[i]; } } try { Console.WriteLine( template, values ); return 0; } catch ( Exception e ) { return ShowHelp( e.Message ); } } #region Convert redirected input to command line args // Code by Atif Aziz on StackOverflow.com: // https://stackoverflow.com/a/749653 [DllImport( "shell32.dll", SetLastError = true )] static extern IntPtr CommandLineToArgvW( [MarshalAs( UnmanagedType.LPWStr )] string lpCmdLine, out int pNumArgs ); public static string[] CommandLineToArgs( string commandLine ) { int argc; var argv = CommandLineToArgvW( commandLine, out argc ); if ( argv == IntPtr.Zero ) throw new System.ComponentModel.Win32Exception( ); try { var args = new string[argc]; for ( var i = 0; i < args.Length; i++ ) { var p = Marshal.ReadIntPtr( argv, i * IntPtr.Size ); args[i] = Marshal.PtrToStringUni( p ); } return args; } finally { Marshal.FreeHGlobal( argv ); } } #endregion Convert redirected input to command line args static int ShowHelp( params string[] errmsg ) { #region Error Message if ( errmsg.Length > 0 ) { List errargs = new List( errmsg ); errargs.RemoveAt( 0 ); Console.Error.WriteLine( ); Console.ForegroundColor = ConsoleColor.Red; Console.Error.Write( "ERROR:\t" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( errmsg[0], errargs.ToArray( ) ); Console.ResetColor( ); } #endregion Error Message #region Help Text /* ToString.exe, Version 1.01 Batch tool to format numbers and dates the .NET way Usage: ToString.exe template arguments or: some_command | ToString.exe template Where: template string specifying how the arguments will be formated arguments single or multiple numbers or dates to be formated some_command command supplying the argument(s) in its standard output (multiple lines of input will each be parsed separately) Example: ToString "{0,6:F2}\t0x{1:X8}\t{2:dd-MM-yyyy}" "0.125" 500 2022-02-24 Returns: 0,13 0x000001F4 24-02-2022 Example: ToString "{0:yyyyMMddTHHmmssfffzzz}" %Time% Returns: 20220225T121343800+01:00 Example: TIME /T | ToString "{0:yyyyMMddTHHmmssfffzzz}" Returns: 20220225T121343800+01:00 Example: ToString "{0}" %Time% Returns: 2022-02-25 15:34:18 Example: ToString "{0}" %Date% Returns: 2022-02-25 00:00:00 Example: DIR %UserProfile%\.. | FIND "" | ToString "{0:D}, {1:T}\t{3}" Returns: Saturday, 1 January 2022, 09:44:00 . Saturday, 1 January 2022, 09:44:00 .. Wednesday, 16 February 2022, 17:22:00 Default Wednesday, 3 February 2021, 16:46:00 Public Saturday, 22 January 2022, 09:25:00 Rob Quirks: Regardless of date and time separators specified in the template, ToString may still use your culture's separators in its output. On the other hand, it may have a problem with decimal separators in a specified time string, even if it complies with your culture; some code has been added to provide for this issue. Note: Return code will be -1 if errors were encountered, otherwise 0. Credits: Merge redirected input with command line by Atif Aziz https://stackoverflow.com/a/749653 Written by Rob van der Woude https://www.robvanderwoude.com */ #endregion Help Text #region Display Help Text Console.Error.WriteLine( ); Console.Error.WriteLine( "ToString.exe, Version {0}", progver ); Console.Error.WriteLine( "Batch tool to format numbers and dates the .NET way" ); Console.Error.WriteLine( ); Console.Error.Write( "Usage: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "ToString.exe template arguments" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( " or: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "some_command | ToString.exe template" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( "Where: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( "template" ); Console.ResetColor( ); Console.Error.WriteLine( " string specifying how the arguments will be formated" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " arguments" ); Console.ResetColor( ); Console.Error.WriteLine( " single or multiple numbers or dates to be formated" ); Console.ForegroundColor = ConsoleColor.White; Console.Error.Write( " some_command" ); Console.ResetColor( ); Console.Error.WriteLine( " command supplying the argument(s) in its standard output" ); Console.Error.WriteLine( " (multiple lines of input will each be parsed separately)" ); Console.Error.WriteLine( ); Console.Error.Write( "Example: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "ToString \"{0,6:F2}\\t0x{1:X8}\\t{2:dd-MM-yyyy}\" \"0.125\" 500 2022-02-24" ); Console.ResetColor( ); Console.Error.Write( "Returns: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( " 0,13\t0x000001F4\t24-02-2022" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( "Example: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "ToString \"{0:yyyyMMddTHHmmssfffzzz}\" %Time%" ); Console.ResetColor( ); Console.Error.Write( "Returns: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( DateTime.Now.ToString( "yyyyMMddTHHmmssfffzzz" ) ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( "Example: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "TIME /T | ToString \"{0:yyyyMMddTHHmmssfffzzz}\"" ); Console.ResetColor( ); Console.Error.Write( "Returns: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( DateTime.Now.ToString( "yyyyMMddTHHmmssfffzzz" ) ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( "Example: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "ToString \"{0}\" %Time%" ); Console.ResetColor( ); Console.Error.Write( "Returns: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( DateTime.Now.ToString( ) ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( "Example: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "ToString \"{0}\" %Date%" ); Console.ResetColor( ); Console.Error.Write( "Returns: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( DateTime.Now.Date.ToString( ) ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.Write( "Example: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "DIR %UserProfile%\\.. | FIND \"\" | ToString \"{0:D}, {1:T}\\t{3}\"" ); Console.ResetColor( ); Console.Error.Write( "Returns: " ); Console.ForegroundColor = ConsoleColor.White; Console.Error.WriteLine( "Saturday, 1 January 2022, 09:44:00\t." ); Console.Error.WriteLine( " Saturday, 1 January 2022, 09:44:00\t.." ); Console.Error.WriteLine( " Wednesday, 16 February 2022, 17:22:00\tDefault" ); Console.Error.WriteLine( " Wednesday, 3 February 2021, 16:46:00\tPublic" ); Console.Error.WriteLine( " Saturday, 22 January 2022, 09:25:00\t{0}", Environment.GetEnvironmentVariable( "UserName" ) ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Quirks: Regardless of date and time separators specified in the template," ); Console.Error.WriteLine( " ToString may still use your culture's separators in its output." ); Console.Error.WriteLine( " On the other hand, it may have a problem with decimal separators" ); Console.Error.WriteLine( " in a specified time string, even if it complies with your culture;" ); Console.Error.WriteLine( " some code has been added to provide for this issue." ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Note: Return code will be -1 if errors were encountered, otherwise 0." ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Credits: Merge redirected input with command line by Atif Aziz" ); Console.ForegroundColor = ConsoleColor.DarkGray; Console.Error.WriteLine( " https://stackoverflow.com/a/749653" ); Console.ResetColor( ); Console.Error.WriteLine( ); Console.Error.WriteLine( "Written by Rob van der Woude" ); Console.Error.WriteLine( "https://www.robvanderwoude.com" ); #endregion Display Help Text return -1; } } }