Skip to content

Instantly share code, notes, and snippets.

@tfwio
Last active December 23, 2015 11:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tfwio/6626538 to your computer and use it in GitHub Desktop.
Save tfwio/6626538 to your computer and use it in GitHub Desktop.
a batch file-rename tool that could use some work.
#region User/License
// Copyright (c) 2005-2013 tfwroble
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
// ----------------------------------------------------------------------------
// 'it just works'
// ----------------------------------------------------------------------------
// usage:
// [todo] web address or article
// ----------------------------------------------------------------------------
// why:
// you can look up command-line arguments fairly easy with very
// few lines of code.
// ----------------------------------------------------------------------------
// history:
// (1) in a old 'study-project' using sqlite to store 'resources' such as images and
// what-ever you might think to store in a resources or resx file.
// (2) added to my personal litte cor3 library (of dependency-less) cor3 library
// for use in a csharp command-line application/tool 'xrename' for batch rename
// of indexed (easily-sortable) files in a directory.
// ----------------------------------------------------------------------------
// TODO: BETTER DOCUMENTATION!!!
// best way to see how to use this is to see it in action with some examples.
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace System
{
// feel free to optimize and/or
class Commander
{
static public readonly char[] char_trimExt = {'*','.',' '};
static public int ArgIndex = 0;
internal List<string> Args, ArgsBackup;
/// <summary>
/// look up the first found index of the same argument.
/// </summary>
/// <param name="arguments">N arguments, all representing the same flag.</param>
/// <returns>-1 when none found or the first found index.</returns>
internal int GetIndex(params string[] arguments)
{
foreach (string argument in arguments)
{
if (Args.Contains(argument))
return Args.IndexOf(argument);
}
return -1;
}
/// <summary>
/// equivelant to 'GetValue(index,false);'
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
internal string PeekValue(int index)
{
return GetValue(index,false);
}
/// <summary>
/// Get a value and delete from list of arguments: 'Args';
/// Something like a Stack.Pop() operation.
/// </summary>
/// <param name="index">parameter index</param>
/// <param name="andRemove">default is true.</param>
/// <returns></returns>
internal string GetValue(int index, bool andRemove=true)
{
string v = Args[index];
if (andRemove) Args.RemoveAt(index);
return v;
}
/// <summary>
/// The following (param: getValue) statements apply only if the argument or tag is found.
/// </summary>
/// <param name="getValue">
/// <para>True to return a value for the argument clearing both.</para>
/// <para>False to clear the argument and return string.empty or null.</para>
/// </param>
/// <param name="attributes">
/// <para>attributes to search for</para>
/// <para>The first found attribute is processed and removed.</para>
/// </param>
/// <returns>
/// (1) null if we have not found our argument.
/// (2) string.Empty if we have found the argument (getValue=false).
/// (3) string value if getValue=true and there is a value to return.
/// </returns>
internal string GetValue(bool getValue, params string[] attributes)
{
int index = -1;
string returned = null;
foreach (string attribute in attributes)
{
if (!Args.Contains(attribute)) continue;
index = Args.FindIndex(x => x==attribute);
if (index!=-1) Args.RemoveAt(index);
else continue;
// this could cause issues, but we leave it.
if (Args.Count==index) continue;
if (Args[index][0]=='-') continue;
if (Args[index][0]=='/') continue;
// if we've gotten here, we get what we came for.
if (getValue) { returned = Args[ index ]; Args.RemoveAt( index ); }
else { returned = string.Empty; break; }
}
return returned;
}
/// <summary>
/// this one is not ready yet.
/// <para>We're looking up values for a provided flag.</para>
///
/// </summary>
/// <param name="attributes"></param>
/// <returns></returns>
internal List<string> GetValues(params string[] attributes)
{
int index = -1;
List<string> values = new List<string>();
foreach (string attribute in attributes)
{
// get the index of the attribute
index = Args.FindIndex(x => x==attribute);
// if found, assign index and remove first item at index
if (index!=-1) {
values.Add(Args[index]); // back up our current
Args.RemoveAt(index);
}
// foreach-continue if no index
else continue;
while (true)
{
// break on next tag or end
if (index == Args.Count) break;
if (Args[index][0] == '-') break;
if (Args[index][0] == '/') break;
// continue if we've got arguments to count.
string x = Args[index];
Args.RemoveAt(index);
values.Add(x.Trim('*','.',' '));
}
}
return values;
}
public List<string> GetValuesForArgument(params string[] attributes)
{
return GetValuesForArgument(false, attributes);
}
// var finalize has not yet been implemented.
List<string> GetValuesForArgument(bool finalize, params string[] attributes)
{
List<string> VALUE = new List<string>();
foreach (var attr in attributes)
{
if (Args.Contains(attr)) {
// get the index of the attribute
int index = Args.FindIndex(x => x==attr);
// iterate to the next for-loop point if necessary.
if (index==-1) continue;
// if found, assign index and remove first item at index
// note that we always will find due to our 'args.contains()' usage.
else if (index!=-1) {
// VALUE.Add(Args[index]); // un-comment if we want the starting var-name
Args.RemoveAt(index);
VALUE.AddRange(GetValuesForIndex(index));
}
// foreach-continue if no index
else continue;
}
}
return VALUE;
}
public List<String> GetValuesForIndex(int index)
{
List<string> ITEMS = new List<string>();
while (true) {
if (index == Args.Count) break;
if (Args[index][0] == '-') break;
if (Args[index][0] == '/') break;
string x = Args[index];
Args.RemoveAt(index);
Debug.WriteLine("adding: {0}",x);
ITEMS.Add(x.Trim(char_trimExt));
}
return ITEMS;
}
/// <summary>
/// use this method if
/// (1) all known flags are stripped from arguments.
/// </summary>
/// <param name="attributes"></param>
/// <returns></returns>
List<string> GetLeftovers(params string[] attributes)
{
return GetValuesForArgument(true,attributes);
}
public string GetFlag(params string[] arg)
{
int index = GetIndex(arg);
return (GetIndex(arg)!=-1) ? GetValue(true,arg) : null;
}
public bool HasValue(params string[] arg)
{
foreach (string a in arg) if (Args.Contains(a)) return true;
return false;
}
}
}
#region Using
/* User: oio * Date: 9/18/2013 * Time: 12:54 AM */
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using System.IO;
#endregion
#region AssemblyProperties
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("xrename")]
[assembly: AssemblyProduct("xrename")]
[assembly: ComVisible(false)]
[assembly: AssemblyVersion("1.0.*")]
#endregion AssemblyProperties
//
namespace xrename
{
// public interface ILesserAction {
// void Run();
// }
/// <summary>
/// <para>A batch tool for renaming multiple files to a format such as "file000.ext"</para>
/// </summary>
class Rename : Commander /*, ILesserAction*/
{
#region Node
class Node
{
public FileInfo OldFile;
/// <summary>
/// the new name identity of the file.
/// </summary>
public string Name;
public string NameExt { get { return string.Concat(Name,OldFile.Extension); } }
public string NameFull { get { return OldFile.FullName.Replace(OldFile.Name,NameExt); } }
public bool HasMatch = false;
}
#endregion
#region Settings
class Setting
{
/// <summary>
/// default state is un-initialized.
/// </summary>
public List<string> Items {
get { return items; }
set { items = value; }
} List<string> items;
// OPTIONS
// ================================================================
/// <summary>
/// default = 'false'
/// </summary>
public bool Recursive {
get { return recursive; }
set { recursive = value; }
} bool recursive = false;
/// <summary>
/// default = 'false'
/// </summary>
public bool Reverse {
get { return reverse; }
set { reverse = value; }
} bool reverse = false;
/// <summary>
/// default = 'false'
/// </summary>
public bool OnlyUseMatches {
get { return onlyUseMatches; }
set { onlyUseMatches = value; }
} bool onlyUseMatches = false;
/// <summary>
/// default = 'false'
/// </summary>
public bool SortPost {
get { return sortPost; }
set { sortPost = value; }
} bool sortPost = false;
public bool SortPre {
get { return sortPre; }
set { sortPre = value; }
} bool sortPre = false;
/// <summary>
/// default = 'null'
/// </summary>
public string Expression {
get { return expression; }
set { expression = value; }
} string expression = null;
/// <summary>
/// default = 'rename-temp'
/// </summary>
public string OutputDir {
get { return outputDir; }
set { outputDir = value; }
} string outputDir = "rename-temp";
/// <summary>
/// default = 'null'
/// </summary>
public string ExpressionReplace {
get { return expressionReplace; }
set { expressionReplace = value; }
} string expressionReplace = null;
#region Automate
const RegexOptions DefaultRegexOptions = RegexOptions.Singleline|RegexOptions.Compiled;
/// <summary>
///
/// </summary>
public Regex RegularExpression { get { return new Regex(Expression,DefaultRegexOptions); } }
bool IsMatch(string input) { return string.IsNullOrEmpty(Expression) ? false : RegularExpression.IsMatch(input); }
#endregion
}
Setting Settings { get; set; }
#endregion
// -sort -r -i "c:\blah\blah\blah\" -x "a-title-([0-9.-]*)" -xr "SortIndex=$1"
// -spr -i "c:\blah\blah\blah\" -x "a-title-([0-9.-]*)" -xr "a-title-{0:000}" -o "newer"
virtual public void Run()
{
// initialize some stuff
DirectoryInfo dir;
Setting settings = new Setting();
List<Node> list = new List<Node>();
#region Set Variables from Input
string arg = null;
int index = -1;
// input directories
List<string> InputDirectories = null;
InputDirectories = GetValuesForArgument("-i");
// input expression
settings.Expression = GetValue(true,"-x");
Console.WriteLine("expression : {0}",expression);
// replacement expression
settings.ExpressionReplace = GetValue(true, "-xr");
settings.OutputDir = GetValue(true, "-o");
// pre sorting?
settings.SortPre = HasValue("-spr"); GetValue(false,"-spr");
Console.WriteLine("sort-pre : {0}",sortPre);
// post sorting?
settings.SortPost = HasValue("-spo"); GetValue(false,"-spo");
Console.WriteLine("sort-post : {0}",sortPost);
// order 'asc' | 'desc'
settings.Reverse = HasValue("-r"); GetValue(false,"-r");
Console.WriteLine("reverse : {0}",reverse);
#endregion
// Throw Exception (write to StdError)
if (InputDirectories==null) return;
if (InputDirectories.Count==0) return;
foreach (var d in InputDirectories)
{
dir = new DirectoryInfo(d);
if (!dir.Exists) {
Console.Error.WriteLine("Directory \"{0}\" does not exist.",dir.FullName);
continue;
}
Console.WriteLine("directory-name : {0}",dir.Name);
Console.WriteLine("directory-path : {0}",dir.Parent.FullName);
Console.WriteLine("------------------------------------------------------------");
if (InputDirectories.Count > 0)
{
foreach (FileInfo f in dir.GetFiles())
{
Node node = new Node(){ OldFile=f, Name=Path.GetFileNameWithoutExtension(f.Name) };
if (string.IsNullOrEmpty(settings.Expression) && !settings.OnlyUseMatches) {
list.Add(node);
}
else if (settings.RegularExpression.IsMatch(f.Name))
{
node.HasMatch = true;
if (!string.IsNullOrEmpty(settings.ExpressionReplace))
node.Name=settings.RegularExpression.Replace(node.Name,settings.ExpressionReplace);
else if (!settings.OnlyUseMatches) {
node.Name = f.Name;
}
}
list.Add(node);
}
// the rest of the code following the '} ... }' could go in here to be able to handle more than
// one input directory.
if (settings..SortPre) list.Sort(delegate(Node a, Node b){ return a.OldFile.Name.CompareTo(b.OldFile.Name); });
if (settings.SortPost) list.Sort(delegate(Node a, Node b){ return a.OldFile.Name.CompareTo(b.NameExt); });
if (settings.Reverse) list.Reverse();
if (list.Count > 0) {
DirectoryInfo newout = new DirectoryInfo(outputDir);
string temp = list[0].OldFile.Directory.Parent.FullName;
if (settings.OutputDir[1]==':') newout = new DirectoryInfo(settings.OutputDir);
else newout = new DirectoryInfo(Path.Combine(temp,settings.OutputDir));
if (!newout.Exists) newout.Create();
Console.WriteLine(newout.FullName);
int i=0;
foreach (Node s in list)
{
s.OldFile.CopyTo(Path.Combine(newout.FullName,string.Format(s.NameExt,i)),true);
Console.WriteLine("Old: {0,-9}, New: {1,-9}",s.OldFile.Name,string.Format(s.NameExt,i++));
}
list.Clear();
}
}
}
Console.ReadKey();
}
}
class Program
{
// public FileInfo AppExecutable { get { return new FileInfo(Assembly.GetExecutingAssembly().Location); } }
// public FileInfo AppCaller { get { return new FileInfo(Assembly.GetCallingAssembly().Location); } }
// public FileInfo AppEntry { get { return new FileInfo(Assembly.GetEntryAssembly().Location); } }
// Environment.CurrentDirectory
// things that are needed:
// (1) only rename matched regular expressions.
// only regex flag
// (2) cbz/cbr compression configuration
// look in windows registry
// look for uninstall
// look in environment
// (2A) CBZ/R Creation From Directory as input COMMAND STRING
// (2B) Execute the command
public static void Main(string[] args)
{
Rename r = new Rename(){ Args=new List<string>(args) };
r.Run();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<PropertyGroup>
<ProjectGuid>{86612E18-F5ED-4265-AB67-47E1142C2A6C}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<OutputType>Exe</OutputType>
<RootNamespace>xrename</RootNamespace>
<AssemblyName>xrename</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<AppDesignerFolder>Properties</AppDesignerFolder>
<NoWin32Manifest>False</NoWin32Manifest>
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
<NoStdLib>False</NoStdLib>
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<BaseAddress>4194304</BaseAddress>
<RegisterForComInterop>False</RegisterForComInterop>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
<FileAlignment>4096</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>bin\Debug\</OutputPath>
<DebugSymbols>True</DebugSymbols>
<DebugType>Full</DebugType>
<Optimize>False</Optimize>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<BaseIntermediateOutputPath>obj\</BaseIntermediateOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>bin\Release\</OutputPath>
<DebugSymbols>False</DebugSymbols>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Data" />
<Reference Include="System.Data.DataSetExtensions">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="commander.cs" />
<Compile Include="xrename.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment