将多个文件(不同名称)重命名并复制到一个带数字的名称
Rename & Copy Multiple Files (Different names) to One Name With Numbers
我如何重命名或复制多个文件(具有不同的名称,没有数字,如 "jhf.mp4" "JJHGF.flv" ..)到一个带有数字的名称,如 (input_01.mp4 input_02 ....) (按情况排序)通过批处理文件,只需将(文件夹文件)的路径提供给批处理文件或将文件拖放到其中(补丁文件)。
-- 平均使用变量或(用 %* 删除).
注意 1:我需要这种使用 X264 编码动画的技术,如果有任何其他好的想法,我会很高兴听到。
注2:我用的是Windows10.
我认为你可以使用 WSHost-scripts 解决这个问题。 WSHost-system 应预装在任何现代 Windows 计算机上。
我设计了一个实用程序系统来解决您的问题:
build-mapping-to-normalized-filenames.js
map-filenames.js
cleanup.cmd
before.cmd
after.cmd
您可以在这个答案的底部找到 3-4-5 的源代码,1-2 的源代码在单独的答案中(每个答案有字符数限制)。
1 和 2 是该系统的主力。强大而不是user-friendly。是的,如果您了解它们使用的输入,您可以将它们用作 stand-alone 实用程序。
3-4-5 - 是绑定此系统并公开 user-friendly 控件的粘合剂。
此系统依赖于绝对路径。但是所有绝对路径都在 3-4-5 的开头设置为变量 - 所以如果你想重组系统 - 你需要在那里改变东西,而且只在那里。
这是在文件系统中的部署方式:
folders:
C:\ <- this is the root of system
C:\aOrig <- folder for original files
C:\norm\ <- folder with files that are part of this system + temp folders
C:\norm\backend <- place for non-user-friendly utilities
files:
C:\norm\backend\build-mapping-to-normalized-filenames.js
C:\norm\backend\map-filenames.js
C:\norm\after.cmd
C:\norm\before.cmd
C:\norm\cleanup.cmd
+files: (these are example international filesnames that you can use for playground to check how system works to make yourserf familiar with it)
C:\aOrig\English.txt <- test files, you could add unique content to each
C:\aOrig\Русский.txt
C:\aOrig\العربية.txt
C:\aOrig\ܐܪܡܝܐ.txt
C:\aOrig\中文.txt
C:\aOrig\日本語.txt
使用方法:
您打开控制台并转到目录 **C:\norm**
您按以下顺序调用程序:
cleanup.cmd
before.cmd
现在发生的是创建了两个有趣的临时文件夹:
C:\norm\bTemp <- 这里是标准化文件。
C:\norm\mappingTemp <- 它包含一个在原始名称和规范化名称之间进行映射的文件。
您现在必须做的 - 是处理 C:\norm\bTemp 中的文件 - 您之前提到的特殊处理。现在出于学习目的,您可以手动更改扩展名和内容。
after.cmd
cleanup.cmd
就是这样。
所以,如果我们相信这就是您在这里的典型 session,简单明了 -
cleanup.cmd
before.cmd
..do your stuff with files at "C:\norm\bTemp"...
after.cmd
cleanup.cmd
此外,您不需要命令行来使用它们 - 您可以在资源管理器中打开 **C:\norm** 并按照我上面解释的顺序双击这些实用程序 - cleanup-before-(..用 bTemp 手动完成你的工作..)-after-cleanup
程序清单 cleanup.cmd :
@ECHO OFF
:: name of this script...
:: useful for debugging...
SET me=%~nx0
:: aOrig - directory that contains source files...
:: bTemp - temporary working directory...
:: mTemp - temporary directory for mapping.txt...
SET aOrig=C:\aOrig
SET bTemp=C:\norm\bTemp
SET mTemp=C:\norm\mappingTemp
:: Removing temporary folders...
:: Be warned - this RD is working
:: in quiet mode /Q - so it wouldn't ask you politely
:: about del y/n - thus, please make shure that
:: %aOrig% doesn't point to anything
:: important for your personal data or system...
IF EXIST "%bTemp%" RD /S /Q "%bTemp%"
IF EXIST "%mTemp%" RD /S /Q "%mTemp%"
IF %ERRORLEVEL% NEQ 0 (
ECHO Script %me% failed on step cleanup-1: [Removing temporary folders]...
ECHO Error: %ERRORLEVEL%
EXIT /B 1
)
:: Successfully finished
ECHO.
ECHO Script %me% successfully finished!
程序列表 before.cmd :
@ECHO OFF
:: name of this script...
:: useful for debugging...
SET me=%~nx0
:: aOrig - directory that contains original files...
:: bTemp - temporary working directory...
:: mTemp - temporary directory for mapping.txt...
SET aOrig=C:\aOrig
SET bTemp=C:\norm\bTemp
SET mTemp=C:\norm\mappingTemp
:: scriptBuild - name of .js-script for building a map
:: from original to normalized filenames...
:: scriptMapFi - name of .js-script interpreting
:: mapping file that was build by scriptBuild...
:: mappingFile - path for file that will contain mapping between
:: original filenames and normalized filenames...
SET scriptBuild=C:\norm\backend\build-mapping-to-normalized-filenames.js
SET scriptMapFi=C:\norm\backend\map-filenames.js
SET mappingFile=C:\norm\mappingTemp\mapping.txt
:: creating temporary folders...
IF NOT EXIST "%bTemp%" MKDIR "%bTemp%"
IF NOT EXIST "%mTemp%" MKDIR "%mTemp%"
IF %ERRORLEVEL% NEQ 0 (
ECHO Script %me% failed on step before-1: [Creating temporary folders]...
ECHO Error: %ERRORLEVEL%
EXIT /B 1
)
:: now we run build-mapping-to-normalized-filenames.js ...
:: that //U thing is important...
CSCRIPT //U //NoLogo "%scriptBuild%" "%aOrig%" 0 1 > "%mappingFile%"
IF %ERRORLEVEL% NEQ 0 (
ECHO Script %me% failed on step before-2: [Building a list of normalized filenames]...
ECHO Error: %ERRORLEVEL%
EXIT /B 1
)
:: now we run map-filenames.js ...
:: that //U thing is important...
CSCRIPT //U //NoLogo "%scriptMapFi%" "%aOrig%" "%bTemp%" /NotTraining < "%mappingFile%"
IF %ERRORLEVEL% NEQ 0 (
ECHO Script %me% failed on step before-3: [Copy files while simultaneously changing their names to normalised form]...
ECHO Error: %ERRORLEVEL%
EXIT /B 1
)
:: Successfully finished
ECHO.
ECHO Script %me% successfully finished!
程序清单 after.cmd :
@ECHO OFF
:: name of this script...
:: useful for debugging...
SET me=%~nx0
:: aOrig - directory that contains original files...
:: bTemp - temporary working directory...
:: mTemp - temporary directory for mapping.txt...
SET aOrig=C:\aOrig
SET bTemp=C:\norm\bTemp
SET mTemp=C:\norm\mappingTemp
:: scriptBuild - name of .js-script for building a map
:: from original to normalized filenames...
:: scriptMapFi - name of .js-script interpreting
:: mapping file that was build by scriptBuild...
:: mappingFile - path for file that will contain mapping between
:: original filenames and normalized filenames...
SET scriptBuild=C:\norm\backend\build-mapping-to-normalized-filenames.js
SET scriptMapFi=C:\norm\backend\map-filenames.js
SET mappingFile=C:\norm\mappingTemp\mapping.txt
:: Removing files in original folder...
:: Not recursive
:: Be warned - this DEL is working
:: in quiet mode /Q - so it wouldn't ask you politely
:: about del y/n - thus, please make shure that
:: %aOrig% doesn't point to anything
:: important for your personal data or system...
IF EXIST "%aOrig%" DEL /Q "%aOrig%"
IF %ERRORLEVEL% NEQ 0 (
ECHO Script %me% failed on step after-1: [Removing files from original folder]...
ECHO Error: %ERRORLEVEL%
EXIT /B 1
)
:: now we run map-filenames.js ...
:: that //U thing is important...
CSCRIPT //U //NoLogo "%scriptMapFi%" "%bTemp%" "%aOrig%" /NotTraining /MapFrom2To1 /ResultExtension:2 /FuzzyExtensionsFrom < "%mappingFile%"
IF %ERRORLEVEL% NEQ 0 (
ECHO Script %me% failed on step after-2: [Copy files while simultaneously changing their names to normalised form]...
ECHO Error: %ERRORLEVEL%
EXIT /B 1
)
:: Successfully finished
ECHO.
ECHO Script %me% successfully finished!
程序列表 build-mapping-to-normalized-filenames.js :
// - - - - -
// #magicVars #config
// Purpose of basicOptions: organize set of most basic settings
// into a compact object package
var basicOptions = {
// Fist things first -
// "scriptName" will be important for error messages.
// Because when you debug a pipeline of scripts, it is always
// crucial to know - which program in pipeline of 10 utilities
// throws that cryptic stderr message "error: not enough arguments".
// I mean huh? Was that calc-tool.js? Or sort-tool.js? Go figure...
scriptName: WScript.ScriptName.toString(),
// What kind of End Of Line sequence
// (they call it New Line sometimes too)
// you prefer when using outputToolbox?
eolStdOut: '\r\n',
eolStdErr: '\r\n',
// Those are all possible errorCodes that this script
// was designed to fail with.
// You can define other error codes.
// Those SHOULD be integer (not strings).
// Code noErrors = 0 is indication that script finished without errors.
errorCodes: {
noErrors: 0,
badArguments: 1,
badGettingFileList: 2
}
};
// - - - - -
// thisOptions
// Purpose of thisOptions: organize set of hard-coded
// normalisation settings
// into a compact object package
var thisOptions = {
normNameBefore: 'input_',
normNameAfter: ''
}
// - - - - -
// outputToolbox depends on
// basicOptions && WScript
// Purpose of outputToolbox: organize in one group
// functions that are used to
// output chars or messages to stdout or stderr
var outputToolbox = {
// Syntactic sugar for printing exact set of chars to stdout.
print: function( data ) {
// For this script to output UTF characters correctly, program must
// run under /U switch - like
// cscript /U //NoLogo program-name-here.js arg1 arg2
WScript.StdOut.Write( data );
},
// Syntactic sugar for printing one line to stdout.
// terminated with your preferred line-terminator.
printLine: function( data ) {
outputToolbox.print( data );
outputToolbox.print( basicOptions.eolStdOut );
},
// Syntactic sugar for printing exact set of chars to stderr.
printErr: function( data ) {
WScript.StdErr.Write( data );
},
// Syntactic sugar for printing one line to stderr
// terminated with your preferred line-terminator.
printErrLine: function( data ) {
outputToolbox.printErr( data );
outputToolbox.printErr( basicOptions.eolStdErr );
},
// Syntactic sugar for printing one line to stderr
// prepended with meaningful script name (that is useful for debug)
// terminated with your preferred line-terminator.
printErrMessage: function( data ) {
outputToolbox.printErr( basicOptions.scriptName + ' failed' );
outputToolbox.printErr( data );
outputToolbox.printErr( basicOptions.eolStdErr );
},
// Syntactic sugar for printing one line to stderr
// prepended with meaningful script name (that is useful for debug)
// terminated with your preferred line-terminator.
printErrWarning: function( data ) {
outputToolbox.printErr( 'Warning for ' + basicOptions.scriptName + ': ' );
outputToolbox.printErr( data );
outputToolbox.printErr( basicOptions.eolStdErr );
},
// Syntactic sugar for printing Error objects
// catched by throw...catch construction
printErrCatchedErr: function( e ) {
outputToolbox.printErrMessage(
' - catched error details - ' + e.name + ': ' + e.message
);
}
};
// - - - - -
// flowToolbox depends on
// WScript
// Purpose of flowToolbox: organize in one group
// functions that are used to
// control general flow of program
var flowToolbox = {
// Shortcut to kill this script.
// When invoked it stops execution.
die: function ( code ) {
if ( typeof code === 'undefined' ) {
code = 0;
}
WScript.Quit(code);
}
};
// - - - - -
// Here is a cleaner alternative: we pollute only name $b.
// Not so easy to type but later, you can easily find all code calls
// dependant on functions from 'basis'.
// '$b' stands for 'library-b' or more exactly - "library named 'basis'"
var $b = {};
$b.print = outputToolbox.print;
$b.printLine = outputToolbox.printLine;
$b.printErr = outputToolbox.printErr;
$b.printErrLine = outputToolbox.printErrLine;
$b.printErrMessage = outputToolbox.printErrMessage;
$b.printErrWarning = outputToolbox.printErrWarning;
$b.die = flowToolbox.die;
// ^ You can use those as functions now
// - - - - -
// $fileListToolbox
// depends on WScript && ActiveXObject( "Scripting.FileSystemObject" ) && $b
var $fileListToolbox = {
/*
* Input:
* any gibberish that is acceptable
* for your local version of
* Scripting.FileSystemObject
* - like
* getFileListForFolder( 'C:\' );
* getFileListForFolder( 'C:\abc' );
* getFileListForFolder( './' );
* getFileListForFolder( './abc/../abc/..' );
*
* Output:
* aFileList with format:
* empty array
* or
* array of oFileData
* oFileData has format:
* {
* fileNameBase: ...[string],
* fileNameExtension: ...[string],
* parentAbsolutePath: ...[string]
* }
*/
getFileListForFolder: function( folderSpec )
{
var aResult = [];
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
if ( !fso.FolderExists( folderSpec ) ) {
$b.printErrMessage(' folder="' + folderSpec + '" doesn\'t exist');
$b.die( basicOptions.errorCodes.badGettingFileList );
}
var folder = fso.GetFolder( folderSpec );
var files = new Enumerator( folder.files );
for ( ; !files.atEnd(); files.moveNext() ) {
var thisFileItem = files.item();
aResult.push({
fileNameBase: fso.GetBaseName( thisFileItem ),
fileNameExtension: fso.GetExtensionName( thisFileItem ),
parentAbsolutePath: (
fso.GetParentFolderName(
fso.GetAbsolutePathName( thisFileItem )
)
)
});
}
return( aResult );
},
/*
* Purpose:
* Sort files by fileNameBase
*
* Input:
* aFileList
* - format is the same
* as for function $fileListToolbox.getFileListForFolder
*
* Output:
* - format is the same
* as for function $fileListToolbox.getFileListForFolder
*/
sortFileListByFileNameBaseAsc: function( aFileList ) {
var fSort = function( a, b ) {
if ( a.fileNameBase > b.fileNameBase ) {
return 1;
}
if ( a.fileNameBase < b.fileNameBase ) {
return -1;
}
return 0;
}
return aFileList.sort( fSort );
},
/*
* Purpose:
* Tool for displaying contents of fileList
*
* Input:
* aFileList
* - format is the same
* as for function $fileListToolbox.getFileListForFolder
*
* Sideeffects:
* Prints aFileList to the stdout stream
*/
printFileList: function( aFileList, callbackPrintLine ) {
if ( typeof callbackPrintLine === 'undefined' ) {
return;
}
var i;
var L = aFileList.length;
var elem;
for ( i = 0; i < L; i++ ) {
elem = aFileList[i];
callbackPrintLine();
callbackPrintLine( 'i: ' + i );
callbackPrintLine( 'fileNameBase: ' + elem.fileNameBase );
callbackPrintLine( 'fileNameExtension: ' + elem.fileNameExtension );
callbackPrintLine( 'parentAbsolutePath: ' + elem.parentAbsolutePath );
}
}
};
/*
// basic test:
$fileListToolbox.printFileList(
$fileListToolbox.sortFileListByFileNameBaseAsc(
$fileListToolbox.getFileListForFolder( 'bTmp' )
),
$b.printLine
);
*/
// - - - - -
// $mappingEngine
var $mappingEngine = {
// this is a relic from a more complex fileformat...
printHeader: function(
functionPrintChars
) {
},
printBasicFilePair: function(
functionPrintChars,
aFileNameBase, aFileNameExtension,
bFileNameBase, bFileNameExtension
) {
var eol = '\r\n';
if ( aFileNameBase.match( /[\r\n]/ ) !== null ) {
throw new Error(
'error: bad input: aFileNameBase contains end of line symbols'
);
}
if ( bFileNameBase.match( /[\r\n]/ ) !== null ) {
throw new Error(
'error: bad input: bFileNameBase contains end of line symbols'
);
}
if ( aFileNameExtension.match( /[\r\n]/ ) !== null ) {
throw new Error(
'error: bad input: aFileNameExtension contains end of line symbols'
);
}
if ( bFileNameExtension.match( /[\r\n]/ ) !== null ) {
throw new Error(
'error: bad input: bFileNameExtension contains end of line symbols'
);
}
var ar = [
'id:1',
'baseName:' + aFileNameBase,
'extensionName:' + aFileNameExtension,
'id:2',
'baseName:' + bFileNameBase,
'extensionName:' + bFileNameExtension,
':',
''
];
functionPrintChars( ar.join( eol ) );
}
};
/*
//basic test:
$mappingEngine.printHeader( $b.print );
$mappingEngine.printBasicFilePair( $b.print, 'abcdef', 'ext', 'input_001', 'txt' );
$mappingEngine.printBasicFilePair( $b.print, 'ghijkl', 'jpg', 'input_002', 'png' );
*/
// it should print to output:
/*
id:1
baseName:abcdef
extensionName:ext
id:2
baseName:input_001
extensionName:txt
:
id:1
baseName:ghijkl
extensionName:jpg
id:2
baseName:input_002
extensionName:png
:
*/
// - - - - -
function generateFAddLeadingZeroes( minimumLength, startFrom ) {
if ( typeof minimumLength === 'undefined' ) {
minimumLength = 0;
}
if ( typeof startFrom === 'undefined' ) {
startFrom = 0;
}
minimumLength = parseInt( minimumLength ) - 1;
startFrom = parseInt( startFrom );
var fAddLeadingZeroes = function( i, L ) {
i += startFrom;
L += startFrom - 1;
var sResult = i.toString();
if ( minimumLength > 0 ) {
var level = 0;
var tmpL = L;
while (
( tmpL = parseInt( tmpL / 10 ) ) >= 1
) {
++level;
}
level = Math.max( level, minimumLength );
var iLength = sResult.length;
while ( iLength <= level-- ) {
sResult = '0' + sResult;
}
}
return sResult;
}
return fAddLeadingZeroes;
}
// - - - - -
var $thisArguments = {
getArgumentsObj: function() {
return WScript.Arguments;
// mockup for tests in browser
/*
var testArr = [1,2,3,4,5];
var mockupArguments = function( i ) {
return testArr[i];
};
mockupArguments.length = testArr.length;
return mockupArguments;
*/
},
printErrorMessageAndDie: function( meaningfulPart ) {
$b.printErrMessage(
' - problem when ' +
'parsing command-line arguments (options): ' +
''
);
$b.die( basicOptions.errorCodes.badArguments );
},
getArgumentsDummy: function() {
return {
folder: 'C:\abc',
minNOfDigits: 0,
countStart: 1
};
},
print: function( argumentsObject ) {
$b.printErrLine( 'folder: ' + argumentsObject.folder );
$b.printErrLine( 'minNOfDigits: ' + argumentsObject.minNOfDigits );
$b.printErrLine( 'countStart: ' + argumentsObject.countStart );
},
// We don't use any flags it this script...
/*
isThereAFlagInArguments: function( inString ) {
var normalizedEthalon = inString.toString().toLowerCase();
var objArgs = $thisArguments.getArgumentsObj();
var i;
var L = objArgs.length;
for ( i = 2; i < L; i++ ) {
var normalizedArgument = objArgs( i ).toString().toLowerCase();
if ( normalizedEthalon === normalizedArgument ) {
return true;
}
}
return false;
},
*/
getArguments: {
folder: function() {
var rR = '';
var objArgs = $thisArguments.getArgumentsObj();
if ( objArgs.length < 1 ) {
$b.printErrMessage(
' - reason: not enough arguments, ' +
'you MUST to provide at least 1 argument, ' +
'but ammount of arguments you provided ' +
'only: ' + objArgs.length
);
$b.die( basicOptions.errorCodes.badArguments );
}
rR = objArgs(0);
var fso = new ActiveXObject("Scripting.FileSystemObject");
rR = fso.GetAbsolutePathName( rR );
return rR;
},
minNOfDigits: function() {
var rR = '0';
var objArgs = $thisArguments.getArgumentsObj();
if ( objArgs.length >= 2 ) {
rR = objArgs(1);
}
var intRR = parseInt( rR );
// test for NaN
if ( intRR !== intRR ) {
$b.printErrMessage(
' - reason: bad argument 2, ' +
'you MUST to provide non-negative integer, ' +
'but you provided non-integer: ' +
'[' + ( typeof rR ) + ']' +
' "' + rR + '"' +
''
);
$b.die( basicOptions.errorCodes.badArguments );
}
// test for negative
if ( intRR < 0 ) {
$b.printErrMessage(
' - reason: bad argument 2, ' +
'you MUST to provide non-negative integer, ' +
'but you provided negtive integer: ' +
'[' + ( typeof rR ) + ']' +
' "' + rR + '"' +
''
);
$b.die( basicOptions.errorCodes.badArguments );
}
rR = intRR;
return rR;
},
countStart: function() {
var rR = '1';
var objArgs = $thisArguments.getArgumentsObj();
if ( objArgs.length >= 3 ) {
rR = objArgs(2);
}
var intRR = parseInt( rR );
// test for NaN
if ( intRR !== intRR ) {
$b.printErrMessage(
' - reason: bad argument 3, ' +
'you MUST to provide non-negative integer, ' +
'but you provided non-integer: ' +
'[' + ( typeof rR ) + ']' +
' "' + rR + '"' +
''
);
$b.die( basicOptions.errorCodes.badArguments );
}
// test for negative
if ( intRR < 0 ) {
$b.printErrMessage(
' - reason: bad argument 3, ' +
'you MUST to provide non-negative integer, ' +
'but you provided negtive integer: ' +
'[' + ( typeof rR ) + ']' +
' "' + rR + '"' +
''
);
$b.die( basicOptions.errorCodes.badArguments );
}
rR = intRR;
return rR;
}
},
getArgumentsAll: function() {
var $get = $thisArguments.getArguments;
var rR = {
folder: $get.folder(),
minNOfDigits: $get.minNOfDigits(),
countStart: $get.countStart()
};
return rR;
}
};
var $a = $thisArguments;
// - - - - -
function main() {
var $inputOptions = $a.getArgumentsAll();
$b.printErrLine();
$b.printErrLine(
'Arguments for ' + basicOptions.scriptName +
' are interpreted this way:'
);
$a.print( $inputOptions );
$b.printErrLine();
try {
var aFileList = $fileListToolbox.getFileListForFolder( $inputOptions.folder );
} catch (e) {
$b.printErrMessage(
' - problems while trying ' +
'to get list of files from folder: ' +
$inputOptions.folder
);
$b.die( basicOptions.errorCodes.badGettingFileList );
return;
}
aFileList = $fileListToolbox.sortFileListByFileNameBaseAsc( aFileList );
// addLeadingZeroes
// is a function now.
// You can see what arguments it requires
// and output it produces in source for
// generateFAddLeadingZeroes
// function.
var addLeadingZeroes = generateFAddLeadingZeroes(
$inputOptions.minNOfDigits,
$inputOptions.countStart
);
var normNameBefore = thisOptions.normNameBefore;
var normNameAfter = thisOptions.normNameAfter;
var i;
var L = aFileList.length;
for ( i = 0; i < L; i++ ) {
var elem = aFileList[i];
var aBase = elem.fileNameBase;
var aExtension = elem.fileNameExtension;
var count = addLeadingZeroes( i, L );
var bBase = normNameBefore + count + normNameAfter;
var bExtension = aExtension;
$mappingEngine.printBasicFilePair(
$b.print,
aBase, aExtension,
bBase, bExtension
);
}
$b.printErr( 'Script ' + basicOptions.scriptName + ' - finished!' );
};
main();
程序清单 map-filenames.js :
// - - - - -
// #magicVars #config
// Purpose of basicOptions: organize set of most basic settings
// into a compact object package
var basicOptions = {
// Fist things first -
// "scriptName" will be important for error messages.
// Because when you debug a pipeline of scripts, it is always
// crucial to know - which program in pipeline of 10 utilities
// throws that cryptic stderr message "error: not enough arguments".
// I mean huh? Was that calc-tool.js? Or sort-tool.js? Go figure...
scriptName: WScript.ScriptName.toString(),
// What kind of End Of Line sequence
// (they call it New Line sometimes too)
// you prefer when using outputToolbox?
eolStdOut: '\r\n',
eolStdErr: '\r\n',
// Those are all possible errorCodes that this script
// was designed to fail with.
// You can define other error codes.
// Those SHOULD be integer (not strings).
// Code noErrors = 0 is indication that script finished without errors.
errorCodes: {
noErrors: 0,
badArguments: 1,
badCopy: 2,
badParsing: 3,
badGettingFileList: 4
}
};
// - - - - -
// - - - - -
// outputToolbox depends on
// basicOptions && WScript
// Purpose of outputToolbox: organize in one group
// functions that are used to
// output chars or messages to stdout or stderr
var outputToolbox = {
// Syntactic sugar for printing exact set of chars to stdout.
print: function( data ) {
// For this script to output UTF characters correctly, program must
// run under /U switch - like
// cscript /U //NoLogo program-name-here.js arg1 arg2
WScript.StdOut.Write( data );
},
// Syntactic sugar for printing one line to stdout.
// terminated with your preferred line-terminator.
printLine: function( data ) {
outputToolbox.print( data );
outputToolbox.print( basicOptions.eolStdOut );
},
// Syntactic sugar for printing exact set of chars to stderr.
printErr: function( data ) {
WScript.StdErr.Write( data );
},
// Syntactic sugar for printing one line to stderr
// terminated with your preferred line-terminator.
printErrLine: function( data ) {
outputToolbox.printErr( data );
outputToolbox.printErr( basicOptions.eolStdErr );
},
// Syntactic sugar for printing one line to stderr
// prepended with meaningful script name (that is useful for debug)
// terminated with your preferred line-terminator.
printErrMessage: function( data ) {
outputToolbox.printErr( basicOptions.scriptName + ' failed' );
outputToolbox.printErr( data );
outputToolbox.printErr( basicOptions.eolStdErr );
},
// Syntactic sugar for printing one line to stderr
// prepended with meaningful script name (that is useful for debug)
// terminated with your preferred line-terminator.
printErrWarning: function( data ) {
outputToolbox.printErr( 'Warning for ' + basicOptions.scriptName + ': ' );
outputToolbox.printErr( data );
outputToolbox.printErr( basicOptions.eolStdErr );
},
// Syntactic sugar for printing Error objects
// catched by throw...catch construction
printErrCatchedErr: function( e ) {
outputToolbox.printErrMessage(
' - catched error details - ' + e.name + ': ' + e.message
);
}
};
// - - - - -
// flowToolbox depends on
// WScript
// Purpose of flowToolbox: organize in one group
// functions that are used to
// control general flow of program
var flowToolbox = {
// Shortcut to kill this script.
// When invoked it stops execution.
die: function ( code ) {
if ( typeof code === 'undefined' ) {
code = 0;
}
WScript.Quit(code);
}
};
// - - - - -
// Here is a cleaner alternative: we pollute only name $b.
// Not so easy to type but later, you can easily find all code calls
// dependant on functions from 'basis'.
// '$b' stands for 'library-b' or more exactly - "library named 'basis'"
var $b = {};
$b.print = outputToolbox.print;
$b.printLine = outputToolbox.printLine;
$b.printErr = outputToolbox.printErr;
$b.printErrLine = outputToolbox.printErrLine;
$b.printErrMessage = outputToolbox.printErrMessage;
$b.printErrWarning = outputToolbox.printErrWarning;
$b.die = flowToolbox.die;
// ^ You can use those as functions now
// - - - - -
// $fileListToolbox
// depends on WScript && ActiveXObject( "Scripting.FileSystemObject" ) && $b
var $fileListToolbox = {
/*
* Input:
* any gibberish that is acceptable
* for your local version of
* Scripting.FileSystemObject
* - like
* getFileListForFolder( 'C:\' );
* getFileListForFolder( 'C:\abc' );
* getFileListForFolder( './' );
* getFileListForFolder( './abc/../abc/..' );
*
* Output:
* aFileList with format:
* empty array
* or
* array of oFileData
* oFileData has format:
* {
* fileNameBase: ...[string],
* fileNameExtension: ...[string],
* parentAbsolutePath: ...[string]
* }
*/
getFileListForFolder: function( folderSpec )
{
var aResult = [];
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
if ( !fso.FolderExists( folderSpec ) ) {
$b.printErrMessage(' folder="' + folderSpec + '" doesn\'t exist');
$b.die( basicOptions.errorCodes.badGettingFileList );
}
var folder = fso.GetFolder( folderSpec );
var files = new Enumerator( folder.files );
for ( ; !files.atEnd(); files.moveNext() ) {
var thisFileItem = files.item();
aResult.push({
fileNameBase: fso.GetBaseName( thisFileItem ),
fileNameExtension: fso.GetExtensionName( thisFileItem ),
parentAbsolutePath: (
fso.GetParentFolderName(
fso.GetAbsolutePathName( thisFileItem )
)
)
});
}
return( aResult );
},
/*
* Purpose:
* Sort files by fileNameBase
*
* Input:
* aFileList
* - format is the same
* as for function $fileListToolbox.getFileListForFolder
*
* Output:
* - format is the same
* as for function $fileListToolbox.getFileListForFolder
*/
sortFileListByFileNameBaseAsc: function( aFileList ) {
var fSort = function( a, b ) {
if ( a.fileNameBase > b.fileNameBase ) {
return 1;
}
if ( a.fileNameBase < b.fileNameBase ) {
return -1;
}
return 0;
}
return aFileList.sort( fSort );
},
/*
Output:
false
when no results
oFileData
when result found
*/
getAnyFileWithBaseName: function( baseName, aFileList ) {
var i;
var L = aFileList.length;
for ( i = 0; i < L; i++ ) {
var a = aFileList[i];
if ( a.fileNameBase === baseName ) {
return a;
}
}
return false;
},
/*
* Purpose:
* Tool for displaying contents of fileList
*
* Input:
* aFileList
* - format is the same
* as for function $fileListToolbox.getFileListForFolder
*
* Sideeffects:
* Prints aFileList to the stdout stream
*/
printFileList: function( aFileList, callbackPrintLine ) {
if ( typeof callbackPrintLine === 'undefined' ) {
return;
}
var i;
var L = aFileList.length;
var elem;
for ( i = 0; i < L; i++ ) {
elem = aFileList[i];
callbackPrintLine();
callbackPrintLine( 'i: ' + i );
callbackPrintLine( 'fileNameBase: ' + elem.fileNameBase );
callbackPrintLine( 'fileNameExtension: ' + elem.fileNameExtension );
callbackPrintLine( 'parentAbsolutePath: ' + elem.parentAbsolutePath );
}
}
};
/*
// basic test:
$fileListToolbox.printFileList(
$fileListToolbox.sortFileListByFileNameBaseAsc(
$fileListToolbox.getFileListForFolder( 'bTmp' )
),
$b.printLine
);
*/
/*
// test for fuzzy search only by file base name:
$fileListToolbox.printFileList(
$fileListToolbox.sortFileListByFileNameBaseAsc(
$fileListToolbox.getFileListForFolder( 'bTmp' )
),
$b.printLine
);
var fuzzy = $fileListToolbox.getAnyFileWithBaseName(
'input_1',
$fileListToolbox.getFileListForFolder( 'bTmp' )
);
$b.printLine( 'fuzzy.fileNameBase: ' + fuzzy.fileNameBase );
$b.printLine( 'fuzzy.fileNameExtension: ' + fuzzy.fileNameExtension );
*/
// $streamToolbox
var $streamToolbox = {
// when end of line returns '' (aka empty string)
getChar: function() {
if ( WScript.StdIn.AtEndOfStream ) {
return '';
}
return WScript.StdIn.Read(1);
},
// tests show that for Windows 7 it understands terminators
// CRLF or LF-only
// when end of line returns '' (aka empty string)
getLine: function() {
if ( WScript.StdIn.AtEndOfStream ) {
return '';
}
return WScript.StdIn.ReadLine();
}
}
var $s = $streamToolbox;
// it could be used like that:
/*
var piece;
while ( ( piece = $s.getLine() ) !== '' ) {
$b.printLine( 'piece: ' + '>' + piece + '<' );
}
*/
// $thisArguments
var $thisArguments = {
getArgumentsObj: function() {
return WScript.Arguments;
// mockup for tests in browser
/*
var testArr = [1,2,3,4,5];
var mockupArguments = function( i ) {
return testArr[i];
};
mockupArguments.length = testArr.length;
return mockupArguments;
*/
},
printErrorMessageAndDie: function( meaningfulPart ) {
$b.printErrMessage(
' - problem when ' +
'parsing command-line arguments (options): ' +
''
);
$b.die( basicOptions.errorCodes.badArguments );
},
getArgumentsDummy: function() {
return {
direction: 'from-id-2-to-id-1',
resultExtension: 'auto',
folderFrom: 'C:\abc',
folderTo: 'D:\abc',
mode: 'training',
modeForExtensionFrom: 'strict'
};
},
print: function( argumentsObject ) {
$b.printErrLine( 'folderFrom: ' + argumentsObject.folderFrom );
$b.printErrLine( 'folderTo: ' + argumentsObject.folderTo );
$b.printErrLine( 'direction: ' + argumentsObject.direction );
$b.printErrLine( 'resultExtension: ' + argumentsObject.resultExtension );
$b.printErrLine( 'mode: ' + argumentsObject.mode );
$b.printErrLine( 'modeForExtensionFrom: ' + argumentsObject.modeForExtensionFrom );
},
isThereAFlagInArguments: function( inString ) {
var normalizedEthalon = inString.toString().toLowerCase();
var objArgs = $thisArguments.getArgumentsObj();
var i;
var L = objArgs.length;
for ( i = 2; i < L; i++ ) {
var normalizedArgument = objArgs( i ).toString().toLowerCase();
if ( normalizedEthalon === normalizedArgument ) {
return true;
}
}
return false;
},
getArguments: {
folderFrom: function() {
var rR = '';
var objArgs = $thisArguments.getArgumentsObj();
if ( objArgs.length < 2 ) {
$b.printErrMessage(
' - reason: not enough arguments, ' +
'you MUST to provide at least 2 arguments, ' +
'but ammount of arguments you provided ' +
'is only: ' + objArgs.length
);
$b.die( basicOptions.errorCodes.badArguments );
}
rR = objArgs(0);
var fso = new ActiveXObject("Scripting.FileSystemObject");
rR = fso.GetAbsolutePathName( rR );
return rR;
},
folderTo: function() {
var rR = '';
var objArgs = $thisArguments.getArgumentsObj();
if ( objArgs.length < 2 ) {
$b.printErrMessage(
' - reason: not enough arguments, ' +
'you MUST to provide at least 2 arguments, ' +
'but ammount of arguments you provided ' +
'is only: ' + objArgs.length
);
$b.die( basicOptions.errorCodes.badArguments );
}
rR = objArgs(1);
var fso = new ActiveXObject("Scripting.FileSystemObject");
rR = fso.GetAbsolutePathName( rR );
return rR;
},
direction: function() {
var rR = 'from-id-1-to-id-2';
var hasFlag = $thisArguments.isThereAFlagInArguments;
if ( hasFlag( '/MapFrom2To1' ) ) {
rR = 'from-id-2-to-id-1';
}
return rR;
},
resultExtension: function() {
var rR = 'auto';
var hasFlag = $thisArguments.isThereAFlagInArguments;
if ( hasFlag( '/ResultExtension:none' ) ) {
rR = 'none';
}
if ( hasFlag( '/ResultExtension:21' ) ) {
rR = '21';
}
if ( hasFlag( '/ResultExtension:12' ) ) {
rR = '12';
}
if ( hasFlag( '/ResultExtension:2' ) ) {
rR = '2';
}
if ( hasFlag( '/ResultExtension:1' ) ) {
rR = '1';
}
return rR;
},
mode: function() {
var rR = 'training';
var hasFlag = $thisArguments.isThereAFlagInArguments;
if ( hasFlag( '/NotTraining' ) ) {
rR = 'not-training';
}
return rR;
},
modeForExtensionFrom: function() {
var rR = 'strict';
var hasFlag = $thisArguments.isThereAFlagInArguments;
if ( hasFlag( '/FuzzyExtensionsFrom' ) ) {
rR = 'fuzzy';
}
return rR;
}
},
getArgumentsAll: function() {
var $get = $thisArguments.getArguments;
var rR = {
folderFrom: $get.folderFrom(),
folderTo: $get.folderTo(),
direction: $get.direction(),
resultExtension: $get.resultExtension(),
mode: $get.mode(),
modeForExtensionFrom: $get.modeForExtensionFrom()
};
return rR;
}
};
var $a = $thisArguments;
//you can use it like this:
/*
var $inputOptions = $a.getArgumentsAll()
$a.print( $inputOptions );
*/
// $parser
// mockup options for separate testing
/*
var basicOptions = {
errorCodes: {
badParsing: 3
}
};
var $b = {
printErrMessage: function( a ) { console.log(a); },
die: function() { throw new Error(); }
};
*/
var $parser = {};
$parser.generateDummyLineReader = function( textBlock ) {
var lines = textBlock.split( '\r\n' );
var i = -1;
var L = lines.length;
var f = function() {
i++;
if ( i >= L ) {
return '';
}
return lines[i];
}
return f;
}
$parser.generateDummyInputOptions = function() {
return {
direction: '1-to-2',
resultExtension: '12',
folderFrom: 'C:\abc',
folderTo: 'D:\abc'
}
}
$parser.generateDummyMappingLineReader = function() {
return $parser.generateDummyLineReader(
[
'id:1',
'baseName:abcdef',
'extensionName:ext',
'id:2',
'baseName:input_001',
'extensionName:txt',
':',
'id:1',
'baseName:ghijkl',
'extensionName:jpg',
'id:2',
'baseName:input_002',
'extensionName:png',
':'
].join('\r\n')
);
}
/*
Remark:
functionReadNextLine
must be iterator that returns '' (aka empty string)
when stream of lines ends
functionForEachBlockDo must have
those arguments:
- id1Base
- id1Ext
- id2Base
- id2Ext
- $inputOptions
- functionCopyFile
$inputOptions must have properties
(wherever it mentioned in this block of comments):
- direction
- resultExtension
- folderFrom
- folderTo
functionCopyFile must have these arguments
- folderFrom
- fileFrom
- folderTo
- fileTo
*/
$parser.parseLinesAndForEachBlockDo = function(
functionReadNextLine,
functionForEachBlockDo,
$inputOptions,
functionCopyFile
) {
var buffer = null;
var nOfLine = 0;
var states = [
'waitingForId1',
'waitingForId2',
'waitingForBase',
'waitingForExtension',
'waitingForEndOfGroup'
];
function printErrorMessageAndDie( meaningfulPart ) {
$b.printErrMessage(
' - problem when parsing line ' + nOfLine + ': ' +
'[' + typeof line + ']' +
'"' + line + '"' +
meaningfulPart +
''
);
$b.die( basicOptions.errorCodes.badParsing );
}
var state = 'waitingForId1';
var line;
while (
( line = functionReadNextLine() ) !== ''
) {
++nOfLine;
var arOrNull = line.match( /^([a-zA-Z0-9-]*):(.*)$/ );
if (
( arOrNull === null )
) {
printErrorMessageAndDie(
'every line in mapping must be either' +
'"key:value" or ":"'
);
}
var key = arOrNull[1];
var value = arOrNull[2];
if (
( value.match( '\r' ) !== null )
) {
printErrorMessageAndDie(
'if line is ' +
'"key:value" ' +
'then it MUST NOT contain \r (CR) symbols ' +
'exept in terminator'
);
}
if ( state === 'waitingForId1' ) {
if (
( key === 'id' )
&&
( value === '1' )
) {
buffer = {};
buffer.nowGroup = {
id: 1
};
state = 'waitingForBase';
continue;
} else {
printErrorMessageAndDie(
'but this line must be "id:1"'
);
}
}
if ( state === 'waitingForBase' ) {
if (
( key === 'baseName' )
) {
buffer.nowGroup.base = value;
state = 'waitingForExtension';
continue;
} else {
printErrorMessageAndDie(
'but this line must be "baseName:...anything-here..."'
);
}
}
if ( state === 'waitingForExtension' ) {
if (
( key === 'extensionName' )
) {
buffer.nowGroup.ext = value;
if ( buffer.nowGroup.id === 1 ) {
state = 'waitingForId2';
} else {
state = 'waitingForEndOfGroup';
}
continue;
} else {
printErrorMessageAndDie(
'but this line must be "extensionName:...anything-here..."'
);
}
}
if ( state === 'waitingForId2' ) {
if (
( key === 'id' )
&&
( value === '2' )
) {
buffer.group1 = {
base: buffer.nowGroup.base,
ext: buffer.nowGroup.ext
}
buffer.nowGroup = {
id: 2
};
state = 'waitingForBase';
continue;
} else {
printErrorMessageAndDie(
'but this line must be "id:2"'
);
}
}
if ( state === 'waitingForEndOfGroup' ) {
if (
( key === '' )
&&
( value === '' )
) {
buffer.group2 = {
base: buffer.nowGroup.base,
ext: buffer.nowGroup.ext
}
functionForEachBlockDo(
buffer.group1.base,
buffer.group1.ext,
buffer.group2.base,
buffer.group2.ext,
$inputOptions,
functionCopyFile
);
buffer = null;
state = 'waitingForId1';
continue;
} else {
printErrorMessageAndDie(
'but this line must be ":"'
);
}
}
}
if ( buffer !== null ) {
printErrorMessageAndDie(
'dangling state (not all properties group are defined) - ' + state
);
}
}
/*
// test
// can be tested in for browser with only $parser object
$parser.parseLinesAndForEachBlockDo(
$parser.generateDummyMappingLineReader(),
function( a, b, c, d ) {
console.log( 'a:', a, 'b:', b, 'c:', c, 'd:', d );
}
);
*/
/*
Remark:
$inputOptions must have properties:
- direction
- resultExtension
- folderFrom
- folderTo
*/
$parser.reactionOnGoodParsedBlock = function(
id1Base, id1Ext,
id2Base, id2Ext,
$inputOptions,
functionCopyFile
) {
function glueExtensionToFileName(
fileName, extension
) {
fileName = fileName.toString();
extension = extension.toString();
if ( extension.length > 0 ) {
extension = '.' + extension;
}
return fileName + extension;
}
var folderFrom = $inputOptions.folderFrom;
var folderTo = $inputOptions.folderTo;
if ( $inputOptions.modeForExtensionFrom === 'fuzzy' ) {
var tmpFromBase = id1Base;
if ( $inputOptions.direction == 'from-id-2-to-id-1' ) {
tmpFromBase = id2Base;
}
var fuzzyFile = $fileListToolbox.getAnyFileWithBaseName(
tmpFromBase,
$fileListToolbox.getFileListForFolder( folderFrom )
);
if ( fuzzyFile !== false ) {
if ( $inputOptions.direction == 'from-id-2-to-id-1' ) {
id2Ext = fuzzyFile.fileNameExtension;
} else {
id1Ext = fuzzyFile.fileNameExtension;
}
}
}
var fromSet = { base: id1Base, ext: id1Ext };
var toSet = { base: id2Base, ext: id2Ext };
if ( $inputOptions.direction == 'from-id-2-to-id-1' ) {
fromSet = { base: id2Base, ext: id2Ext };
toSet = { base: id1Base, ext: id1Ext };
}
if ( $inputOptions.resultExtension === '21' ) {
toSet.ext = glueExtensionToFileName( id2Ext, id1Ext );
}
if ( $inputOptions.resultExtension === '12' ) {
toSet.ext = glueExtensionToFileName( id1Ext, id2Ext );
}
if ( $inputOptions.resultExtension === '2' ) {
toSet.ext = id2Ext;
}
if ( $inputOptions.resultExtension === '1' ) {
toSet.ext = id1Ext;
}
if ( $inputOptions.resultExtension === 'none' ) {
toSet.ext = '';
}
var fileFrom = glueExtensionToFileName( fromSet.base, fromSet.ext );
var fileTo = glueExtensionToFileName( toSet.base, toSet.ext );
functionCopyFile(
folderFrom, fileFrom,
folderTo, fileTo
);
}
/*
// test
// can be tested in for browser with only $parser object
$parser.reactionOnGoodParsedBlock(
'originalA', 'extA',
'originalB', 'extB',
{
direction: 'from-id-2-to-id-1',
resultExtension: 'auto',
folderFrom: 'C:\abc',
folderTo: 'D:\abc'
},
function(
folderFrom, fileFrom,
folderTo, fileTo
) {
console.log(
'folderFrom:', folderFrom,
'fileFrom:', fileFrom,
'folderTo:', folderTo,
'fileTo:', fileTo
);
}
);
// expected result:
// folderFrom: C:abc fileFrom: originalA.extA folderTo: D:abc fileTo: originalB.extB
*/
var $copyFileToolbox = {
copyFileTraining: function(
folderFrom, fileFrom,
folderTo, fileTo
) {
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
if ( !fso.FolderExists( folderFrom ) ) {
$b.printErrWarning('folderFrom="' + folderFrom + '" doesn\'t exist');
}
if ( !fso.FolderExists( folderTo ) ) {
$b.printErrWarning('folderTo="' + folderTo + '" doesn\'t exist');
}
var pathFrom = fso.BuildPath( folderFrom, fileFrom );
var pathTo = fso.BuildPath( folderTo, fileTo );
if ( !fso.FileExists( pathFrom ) ) {
$b.printErrLine();
$b.printErrWarning('no source file found, script looking for it at pathFrom="' + pathFrom + '" - this file doesn\'t exist');
}
if ( fso.FileExists( pathTo ) ) {
$b.printErrLine();
$b.printErrWarning('be advised - there was found a file at destination for copy command, and in "NotTraining" mode you will overwrite file at pathTo="' + pathTo + '"');
}
$b.printLine( '' );
$b.printLine( 'Training mode: you ask to copy file' );
$b.printLine( 'from: ' + pathFrom );
$b.printLine( '\/' );
$b.printLine( 'to: ' + pathTo );
},
copyFile: function(
folderFrom, fileFrom,
folderTo, fileTo
) {
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
if ( !fso.FolderExists( folderFrom ) ) {
$b.printErrMessage(' folderFrom="' + folderFrom + '" doesn\'t exist');
$b.die( basicOptions.errorCodes.badCopy );
}
if ( !fso.FolderExists( folderTo ) ) {
$b.printErrMessage (' folderTo="' + folderTo + '" doesn\'t exist');
$b.die( basicOptions.errorCodes.badCopy );
}
var pathFrom = fso.BuildPath( folderFrom, fileFrom );
var pathTo = fso.BuildPath( folderTo, fileTo );
if ( !fso.FileExists( pathFrom ) ) {
$b.printErrMessage(' no source file found, script looking for it at pathFrom="' + pathFrom + '" doesn\'t exist');
$b.die( basicOptions.errorCodes.badCopy );
}
fso.copyFile( pathFrom, pathTo, true );
}
}
var $c = $copyFileToolbox;
// you can use it as:
/*
$c.copyFileTraining(
'./', '123.txt',
'b', '345.txt'
);
*/
// you will have a lot of problems testing if filename is unicode
// only way that I found for Windows 7 is to pass filename as stdin
// from some file (perhaps any streaming from file works ok here...)
// and yes, test shows that it works...
function main() {
var $inputOptions = $a.getArgumentsAll();
$b.printErrLine();
$b.printErrLine(
'Arguments for ' + basicOptions.scriptName +
' are interpreted this way:'
);
$a.print( $inputOptions );
$b.printErrLine();
var fCopyFile = $c.copyFileTraining;
if ( $inputOptions.mode === 'not-training' ) {
fCopyFile = $c.copyFile;
}
/*
var fuzzy = $fileListToolbox.getAnyFileWithBaseName(
'input_1',
$fileListToolbox.getFileListForFolder( 'bTmp' )
);
*/
$parser.parseLinesAndForEachBlockDo(
$s.getLine,
$parser.reactionOnGoodParsedBlock,
$inputOptions,
fCopyFile
);
$b.printErr( 'Script ' + basicOptions.scriptName + ' - finished!' );
};
main();
我如何重命名或复制多个文件(具有不同的名称,没有数字,如 "jhf.mp4" "JJHGF.flv" ..)到一个带有数字的名称,如 (input_01.mp4 input_02 ....) (按情况排序)通过批处理文件,只需将(文件夹文件)的路径提供给批处理文件或将文件拖放到其中(补丁文件)。 -- 平均使用变量或(用 %* 删除).
注意 1:我需要这种使用 X264 编码动画的技术,如果有任何其他好的想法,我会很高兴听到。
注2:我用的是Windows10.
我认为你可以使用 WSHost-scripts 解决这个问题。 WSHost-system 应预装在任何现代 Windows 计算机上。
我设计了一个实用程序系统来解决您的问题:
build-mapping-to-normalized-filenames.js
map-filenames.js
cleanup.cmd
before.cmd
after.cmd
您可以在这个答案的底部找到 3-4-5 的源代码,1-2 的源代码在单独的答案中(每个答案有字符数限制)。
1 和 2 是该系统的主力。强大而不是user-friendly。是的,如果您了解它们使用的输入,您可以将它们用作 stand-alone 实用程序。
3-4-5 - 是绑定此系统并公开 user-friendly 控件的粘合剂。
此系统依赖于绝对路径。但是所有绝对路径都在 3-4-5 的开头设置为变量 - 所以如果你想重组系统 - 你需要在那里改变东西,而且只在那里。
这是在文件系统中的部署方式:
folders:
C:\ <- this is the root of system
C:\aOrig <- folder for original files
C:\norm\ <- folder with files that are part of this system + temp folders
C:\norm\backend <- place for non-user-friendly utilities
files:
C:\norm\backend\build-mapping-to-normalized-filenames.js
C:\norm\backend\map-filenames.js
C:\norm\after.cmd
C:\norm\before.cmd
C:\norm\cleanup.cmd
+files: (these are example international filesnames that you can use for playground to check how system works to make yourserf familiar with it)
C:\aOrig\English.txt <- test files, you could add unique content to each
C:\aOrig\Русский.txt
C:\aOrig\العربية.txt
C:\aOrig\ܐܪܡܝܐ.txt
C:\aOrig\中文.txt
C:\aOrig\日本語.txt
使用方法:
您打开控制台并转到目录 **C:\norm**
您按以下顺序调用程序:
cleanup.cmd
before.cmd
现在发生的是创建了两个有趣的临时文件夹:
C:\norm\bTemp <- 这里是标准化文件。
C:\norm\mappingTemp <- 它包含一个在原始名称和规范化名称之间进行映射的文件。
您现在必须做的 - 是处理 C:\norm\bTemp 中的文件 - 您之前提到的特殊处理。现在出于学习目的,您可以手动更改扩展名和内容。
after.cmd
cleanup.cmd
就是这样。
所以,如果我们相信这就是您在这里的典型 session,简单明了 -
cleanup.cmd
before.cmd
..do your stuff with files at "C:\norm\bTemp"...
after.cmd
cleanup.cmd
此外,您不需要命令行来使用它们 - 您可以在资源管理器中打开 **C:\norm** 并按照我上面解释的顺序双击这些实用程序 - cleanup-before-(..用 bTemp 手动完成你的工作..)-after-cleanup
程序清单 cleanup.cmd :
@ECHO OFF
:: name of this script...
:: useful for debugging...
SET me=%~nx0
:: aOrig - directory that contains source files...
:: bTemp - temporary working directory...
:: mTemp - temporary directory for mapping.txt...
SET aOrig=C:\aOrig
SET bTemp=C:\norm\bTemp
SET mTemp=C:\norm\mappingTemp
:: Removing temporary folders...
:: Be warned - this RD is working
:: in quiet mode /Q - so it wouldn't ask you politely
:: about del y/n - thus, please make shure that
:: %aOrig% doesn't point to anything
:: important for your personal data or system...
IF EXIST "%bTemp%" RD /S /Q "%bTemp%"
IF EXIST "%mTemp%" RD /S /Q "%mTemp%"
IF %ERRORLEVEL% NEQ 0 (
ECHO Script %me% failed on step cleanup-1: [Removing temporary folders]...
ECHO Error: %ERRORLEVEL%
EXIT /B 1
)
:: Successfully finished
ECHO.
ECHO Script %me% successfully finished!
程序列表 before.cmd :
@ECHO OFF
:: name of this script...
:: useful for debugging...
SET me=%~nx0
:: aOrig - directory that contains original files...
:: bTemp - temporary working directory...
:: mTemp - temporary directory for mapping.txt...
SET aOrig=C:\aOrig
SET bTemp=C:\norm\bTemp
SET mTemp=C:\norm\mappingTemp
:: scriptBuild - name of .js-script for building a map
:: from original to normalized filenames...
:: scriptMapFi - name of .js-script interpreting
:: mapping file that was build by scriptBuild...
:: mappingFile - path for file that will contain mapping between
:: original filenames and normalized filenames...
SET scriptBuild=C:\norm\backend\build-mapping-to-normalized-filenames.js
SET scriptMapFi=C:\norm\backend\map-filenames.js
SET mappingFile=C:\norm\mappingTemp\mapping.txt
:: creating temporary folders...
IF NOT EXIST "%bTemp%" MKDIR "%bTemp%"
IF NOT EXIST "%mTemp%" MKDIR "%mTemp%"
IF %ERRORLEVEL% NEQ 0 (
ECHO Script %me% failed on step before-1: [Creating temporary folders]...
ECHO Error: %ERRORLEVEL%
EXIT /B 1
)
:: now we run build-mapping-to-normalized-filenames.js ...
:: that //U thing is important...
CSCRIPT //U //NoLogo "%scriptBuild%" "%aOrig%" 0 1 > "%mappingFile%"
IF %ERRORLEVEL% NEQ 0 (
ECHO Script %me% failed on step before-2: [Building a list of normalized filenames]...
ECHO Error: %ERRORLEVEL%
EXIT /B 1
)
:: now we run map-filenames.js ...
:: that //U thing is important...
CSCRIPT //U //NoLogo "%scriptMapFi%" "%aOrig%" "%bTemp%" /NotTraining < "%mappingFile%"
IF %ERRORLEVEL% NEQ 0 (
ECHO Script %me% failed on step before-3: [Copy files while simultaneously changing their names to normalised form]...
ECHO Error: %ERRORLEVEL%
EXIT /B 1
)
:: Successfully finished
ECHO.
ECHO Script %me% successfully finished!
程序清单 after.cmd :
@ECHO OFF
:: name of this script...
:: useful for debugging...
SET me=%~nx0
:: aOrig - directory that contains original files...
:: bTemp - temporary working directory...
:: mTemp - temporary directory for mapping.txt...
SET aOrig=C:\aOrig
SET bTemp=C:\norm\bTemp
SET mTemp=C:\norm\mappingTemp
:: scriptBuild - name of .js-script for building a map
:: from original to normalized filenames...
:: scriptMapFi - name of .js-script interpreting
:: mapping file that was build by scriptBuild...
:: mappingFile - path for file that will contain mapping between
:: original filenames and normalized filenames...
SET scriptBuild=C:\norm\backend\build-mapping-to-normalized-filenames.js
SET scriptMapFi=C:\norm\backend\map-filenames.js
SET mappingFile=C:\norm\mappingTemp\mapping.txt
:: Removing files in original folder...
:: Not recursive
:: Be warned - this DEL is working
:: in quiet mode /Q - so it wouldn't ask you politely
:: about del y/n - thus, please make shure that
:: %aOrig% doesn't point to anything
:: important for your personal data or system...
IF EXIST "%aOrig%" DEL /Q "%aOrig%"
IF %ERRORLEVEL% NEQ 0 (
ECHO Script %me% failed on step after-1: [Removing files from original folder]...
ECHO Error: %ERRORLEVEL%
EXIT /B 1
)
:: now we run map-filenames.js ...
:: that //U thing is important...
CSCRIPT //U //NoLogo "%scriptMapFi%" "%bTemp%" "%aOrig%" /NotTraining /MapFrom2To1 /ResultExtension:2 /FuzzyExtensionsFrom < "%mappingFile%"
IF %ERRORLEVEL% NEQ 0 (
ECHO Script %me% failed on step after-2: [Copy files while simultaneously changing their names to normalised form]...
ECHO Error: %ERRORLEVEL%
EXIT /B 1
)
:: Successfully finished
ECHO.
ECHO Script %me% successfully finished!
程序列表 build-mapping-to-normalized-filenames.js :
// - - - - -
// #magicVars #config
// Purpose of basicOptions: organize set of most basic settings
// into a compact object package
var basicOptions = {
// Fist things first -
// "scriptName" will be important for error messages.
// Because when you debug a pipeline of scripts, it is always
// crucial to know - which program in pipeline of 10 utilities
// throws that cryptic stderr message "error: not enough arguments".
// I mean huh? Was that calc-tool.js? Or sort-tool.js? Go figure...
scriptName: WScript.ScriptName.toString(),
// What kind of End Of Line sequence
// (they call it New Line sometimes too)
// you prefer when using outputToolbox?
eolStdOut: '\r\n',
eolStdErr: '\r\n',
// Those are all possible errorCodes that this script
// was designed to fail with.
// You can define other error codes.
// Those SHOULD be integer (not strings).
// Code noErrors = 0 is indication that script finished without errors.
errorCodes: {
noErrors: 0,
badArguments: 1,
badGettingFileList: 2
}
};
// - - - - -
// thisOptions
// Purpose of thisOptions: organize set of hard-coded
// normalisation settings
// into a compact object package
var thisOptions = {
normNameBefore: 'input_',
normNameAfter: ''
}
// - - - - -
// outputToolbox depends on
// basicOptions && WScript
// Purpose of outputToolbox: organize in one group
// functions that are used to
// output chars or messages to stdout or stderr
var outputToolbox = {
// Syntactic sugar for printing exact set of chars to stdout.
print: function( data ) {
// For this script to output UTF characters correctly, program must
// run under /U switch - like
// cscript /U //NoLogo program-name-here.js arg1 arg2
WScript.StdOut.Write( data );
},
// Syntactic sugar for printing one line to stdout.
// terminated with your preferred line-terminator.
printLine: function( data ) {
outputToolbox.print( data );
outputToolbox.print( basicOptions.eolStdOut );
},
// Syntactic sugar for printing exact set of chars to stderr.
printErr: function( data ) {
WScript.StdErr.Write( data );
},
// Syntactic sugar for printing one line to stderr
// terminated with your preferred line-terminator.
printErrLine: function( data ) {
outputToolbox.printErr( data );
outputToolbox.printErr( basicOptions.eolStdErr );
},
// Syntactic sugar for printing one line to stderr
// prepended with meaningful script name (that is useful for debug)
// terminated with your preferred line-terminator.
printErrMessage: function( data ) {
outputToolbox.printErr( basicOptions.scriptName + ' failed' );
outputToolbox.printErr( data );
outputToolbox.printErr( basicOptions.eolStdErr );
},
// Syntactic sugar for printing one line to stderr
// prepended with meaningful script name (that is useful for debug)
// terminated with your preferred line-terminator.
printErrWarning: function( data ) {
outputToolbox.printErr( 'Warning for ' + basicOptions.scriptName + ': ' );
outputToolbox.printErr( data );
outputToolbox.printErr( basicOptions.eolStdErr );
},
// Syntactic sugar for printing Error objects
// catched by throw...catch construction
printErrCatchedErr: function( e ) {
outputToolbox.printErrMessage(
' - catched error details - ' + e.name + ': ' + e.message
);
}
};
// - - - - -
// flowToolbox depends on
// WScript
// Purpose of flowToolbox: organize in one group
// functions that are used to
// control general flow of program
var flowToolbox = {
// Shortcut to kill this script.
// When invoked it stops execution.
die: function ( code ) {
if ( typeof code === 'undefined' ) {
code = 0;
}
WScript.Quit(code);
}
};
// - - - - -
// Here is a cleaner alternative: we pollute only name $b.
// Not so easy to type but later, you can easily find all code calls
// dependant on functions from 'basis'.
// '$b' stands for 'library-b' or more exactly - "library named 'basis'"
var $b = {};
$b.print = outputToolbox.print;
$b.printLine = outputToolbox.printLine;
$b.printErr = outputToolbox.printErr;
$b.printErrLine = outputToolbox.printErrLine;
$b.printErrMessage = outputToolbox.printErrMessage;
$b.printErrWarning = outputToolbox.printErrWarning;
$b.die = flowToolbox.die;
// ^ You can use those as functions now
// - - - - -
// $fileListToolbox
// depends on WScript && ActiveXObject( "Scripting.FileSystemObject" ) && $b
var $fileListToolbox = {
/*
* Input:
* any gibberish that is acceptable
* for your local version of
* Scripting.FileSystemObject
* - like
* getFileListForFolder( 'C:\' );
* getFileListForFolder( 'C:\abc' );
* getFileListForFolder( './' );
* getFileListForFolder( './abc/../abc/..' );
*
* Output:
* aFileList with format:
* empty array
* or
* array of oFileData
* oFileData has format:
* {
* fileNameBase: ...[string],
* fileNameExtension: ...[string],
* parentAbsolutePath: ...[string]
* }
*/
getFileListForFolder: function( folderSpec )
{
var aResult = [];
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
if ( !fso.FolderExists( folderSpec ) ) {
$b.printErrMessage(' folder="' + folderSpec + '" doesn\'t exist');
$b.die( basicOptions.errorCodes.badGettingFileList );
}
var folder = fso.GetFolder( folderSpec );
var files = new Enumerator( folder.files );
for ( ; !files.atEnd(); files.moveNext() ) {
var thisFileItem = files.item();
aResult.push({
fileNameBase: fso.GetBaseName( thisFileItem ),
fileNameExtension: fso.GetExtensionName( thisFileItem ),
parentAbsolutePath: (
fso.GetParentFolderName(
fso.GetAbsolutePathName( thisFileItem )
)
)
});
}
return( aResult );
},
/*
* Purpose:
* Sort files by fileNameBase
*
* Input:
* aFileList
* - format is the same
* as for function $fileListToolbox.getFileListForFolder
*
* Output:
* - format is the same
* as for function $fileListToolbox.getFileListForFolder
*/
sortFileListByFileNameBaseAsc: function( aFileList ) {
var fSort = function( a, b ) {
if ( a.fileNameBase > b.fileNameBase ) {
return 1;
}
if ( a.fileNameBase < b.fileNameBase ) {
return -1;
}
return 0;
}
return aFileList.sort( fSort );
},
/*
* Purpose:
* Tool for displaying contents of fileList
*
* Input:
* aFileList
* - format is the same
* as for function $fileListToolbox.getFileListForFolder
*
* Sideeffects:
* Prints aFileList to the stdout stream
*/
printFileList: function( aFileList, callbackPrintLine ) {
if ( typeof callbackPrintLine === 'undefined' ) {
return;
}
var i;
var L = aFileList.length;
var elem;
for ( i = 0; i < L; i++ ) {
elem = aFileList[i];
callbackPrintLine();
callbackPrintLine( 'i: ' + i );
callbackPrintLine( 'fileNameBase: ' + elem.fileNameBase );
callbackPrintLine( 'fileNameExtension: ' + elem.fileNameExtension );
callbackPrintLine( 'parentAbsolutePath: ' + elem.parentAbsolutePath );
}
}
};
/*
// basic test:
$fileListToolbox.printFileList(
$fileListToolbox.sortFileListByFileNameBaseAsc(
$fileListToolbox.getFileListForFolder( 'bTmp' )
),
$b.printLine
);
*/
// - - - - -
// $mappingEngine
var $mappingEngine = {
// this is a relic from a more complex fileformat...
printHeader: function(
functionPrintChars
) {
},
printBasicFilePair: function(
functionPrintChars,
aFileNameBase, aFileNameExtension,
bFileNameBase, bFileNameExtension
) {
var eol = '\r\n';
if ( aFileNameBase.match( /[\r\n]/ ) !== null ) {
throw new Error(
'error: bad input: aFileNameBase contains end of line symbols'
);
}
if ( bFileNameBase.match( /[\r\n]/ ) !== null ) {
throw new Error(
'error: bad input: bFileNameBase contains end of line symbols'
);
}
if ( aFileNameExtension.match( /[\r\n]/ ) !== null ) {
throw new Error(
'error: bad input: aFileNameExtension contains end of line symbols'
);
}
if ( bFileNameExtension.match( /[\r\n]/ ) !== null ) {
throw new Error(
'error: bad input: bFileNameExtension contains end of line symbols'
);
}
var ar = [
'id:1',
'baseName:' + aFileNameBase,
'extensionName:' + aFileNameExtension,
'id:2',
'baseName:' + bFileNameBase,
'extensionName:' + bFileNameExtension,
':',
''
];
functionPrintChars( ar.join( eol ) );
}
};
/*
//basic test:
$mappingEngine.printHeader( $b.print );
$mappingEngine.printBasicFilePair( $b.print, 'abcdef', 'ext', 'input_001', 'txt' );
$mappingEngine.printBasicFilePair( $b.print, 'ghijkl', 'jpg', 'input_002', 'png' );
*/
// it should print to output:
/*
id:1
baseName:abcdef
extensionName:ext
id:2
baseName:input_001
extensionName:txt
:
id:1
baseName:ghijkl
extensionName:jpg
id:2
baseName:input_002
extensionName:png
:
*/
// - - - - -
function generateFAddLeadingZeroes( minimumLength, startFrom ) {
if ( typeof minimumLength === 'undefined' ) {
minimumLength = 0;
}
if ( typeof startFrom === 'undefined' ) {
startFrom = 0;
}
minimumLength = parseInt( minimumLength ) - 1;
startFrom = parseInt( startFrom );
var fAddLeadingZeroes = function( i, L ) {
i += startFrom;
L += startFrom - 1;
var sResult = i.toString();
if ( minimumLength > 0 ) {
var level = 0;
var tmpL = L;
while (
( tmpL = parseInt( tmpL / 10 ) ) >= 1
) {
++level;
}
level = Math.max( level, minimumLength );
var iLength = sResult.length;
while ( iLength <= level-- ) {
sResult = '0' + sResult;
}
}
return sResult;
}
return fAddLeadingZeroes;
}
// - - - - -
var $thisArguments = {
getArgumentsObj: function() {
return WScript.Arguments;
// mockup for tests in browser
/*
var testArr = [1,2,3,4,5];
var mockupArguments = function( i ) {
return testArr[i];
};
mockupArguments.length = testArr.length;
return mockupArguments;
*/
},
printErrorMessageAndDie: function( meaningfulPart ) {
$b.printErrMessage(
' - problem when ' +
'parsing command-line arguments (options): ' +
''
);
$b.die( basicOptions.errorCodes.badArguments );
},
getArgumentsDummy: function() {
return {
folder: 'C:\abc',
minNOfDigits: 0,
countStart: 1
};
},
print: function( argumentsObject ) {
$b.printErrLine( 'folder: ' + argumentsObject.folder );
$b.printErrLine( 'minNOfDigits: ' + argumentsObject.minNOfDigits );
$b.printErrLine( 'countStart: ' + argumentsObject.countStart );
},
// We don't use any flags it this script...
/*
isThereAFlagInArguments: function( inString ) {
var normalizedEthalon = inString.toString().toLowerCase();
var objArgs = $thisArguments.getArgumentsObj();
var i;
var L = objArgs.length;
for ( i = 2; i < L; i++ ) {
var normalizedArgument = objArgs( i ).toString().toLowerCase();
if ( normalizedEthalon === normalizedArgument ) {
return true;
}
}
return false;
},
*/
getArguments: {
folder: function() {
var rR = '';
var objArgs = $thisArguments.getArgumentsObj();
if ( objArgs.length < 1 ) {
$b.printErrMessage(
' - reason: not enough arguments, ' +
'you MUST to provide at least 1 argument, ' +
'but ammount of arguments you provided ' +
'only: ' + objArgs.length
);
$b.die( basicOptions.errorCodes.badArguments );
}
rR = objArgs(0);
var fso = new ActiveXObject("Scripting.FileSystemObject");
rR = fso.GetAbsolutePathName( rR );
return rR;
},
minNOfDigits: function() {
var rR = '0';
var objArgs = $thisArguments.getArgumentsObj();
if ( objArgs.length >= 2 ) {
rR = objArgs(1);
}
var intRR = parseInt( rR );
// test for NaN
if ( intRR !== intRR ) {
$b.printErrMessage(
' - reason: bad argument 2, ' +
'you MUST to provide non-negative integer, ' +
'but you provided non-integer: ' +
'[' + ( typeof rR ) + ']' +
' "' + rR + '"' +
''
);
$b.die( basicOptions.errorCodes.badArguments );
}
// test for negative
if ( intRR < 0 ) {
$b.printErrMessage(
' - reason: bad argument 2, ' +
'you MUST to provide non-negative integer, ' +
'but you provided negtive integer: ' +
'[' + ( typeof rR ) + ']' +
' "' + rR + '"' +
''
);
$b.die( basicOptions.errorCodes.badArguments );
}
rR = intRR;
return rR;
},
countStart: function() {
var rR = '1';
var objArgs = $thisArguments.getArgumentsObj();
if ( objArgs.length >= 3 ) {
rR = objArgs(2);
}
var intRR = parseInt( rR );
// test for NaN
if ( intRR !== intRR ) {
$b.printErrMessage(
' - reason: bad argument 3, ' +
'you MUST to provide non-negative integer, ' +
'but you provided non-integer: ' +
'[' + ( typeof rR ) + ']' +
' "' + rR + '"' +
''
);
$b.die( basicOptions.errorCodes.badArguments );
}
// test for negative
if ( intRR < 0 ) {
$b.printErrMessage(
' - reason: bad argument 3, ' +
'you MUST to provide non-negative integer, ' +
'but you provided negtive integer: ' +
'[' + ( typeof rR ) + ']' +
' "' + rR + '"' +
''
);
$b.die( basicOptions.errorCodes.badArguments );
}
rR = intRR;
return rR;
}
},
getArgumentsAll: function() {
var $get = $thisArguments.getArguments;
var rR = {
folder: $get.folder(),
minNOfDigits: $get.minNOfDigits(),
countStart: $get.countStart()
};
return rR;
}
};
var $a = $thisArguments;
// - - - - -
function main() {
var $inputOptions = $a.getArgumentsAll();
$b.printErrLine();
$b.printErrLine(
'Arguments for ' + basicOptions.scriptName +
' are interpreted this way:'
);
$a.print( $inputOptions );
$b.printErrLine();
try {
var aFileList = $fileListToolbox.getFileListForFolder( $inputOptions.folder );
} catch (e) {
$b.printErrMessage(
' - problems while trying ' +
'to get list of files from folder: ' +
$inputOptions.folder
);
$b.die( basicOptions.errorCodes.badGettingFileList );
return;
}
aFileList = $fileListToolbox.sortFileListByFileNameBaseAsc( aFileList );
// addLeadingZeroes
// is a function now.
// You can see what arguments it requires
// and output it produces in source for
// generateFAddLeadingZeroes
// function.
var addLeadingZeroes = generateFAddLeadingZeroes(
$inputOptions.minNOfDigits,
$inputOptions.countStart
);
var normNameBefore = thisOptions.normNameBefore;
var normNameAfter = thisOptions.normNameAfter;
var i;
var L = aFileList.length;
for ( i = 0; i < L; i++ ) {
var elem = aFileList[i];
var aBase = elem.fileNameBase;
var aExtension = elem.fileNameExtension;
var count = addLeadingZeroes( i, L );
var bBase = normNameBefore + count + normNameAfter;
var bExtension = aExtension;
$mappingEngine.printBasicFilePair(
$b.print,
aBase, aExtension,
bBase, bExtension
);
}
$b.printErr( 'Script ' + basicOptions.scriptName + ' - finished!' );
};
main();
程序清单 map-filenames.js :
// - - - - -
// #magicVars #config
// Purpose of basicOptions: organize set of most basic settings
// into a compact object package
var basicOptions = {
// Fist things first -
// "scriptName" will be important for error messages.
// Because when you debug a pipeline of scripts, it is always
// crucial to know - which program in pipeline of 10 utilities
// throws that cryptic stderr message "error: not enough arguments".
// I mean huh? Was that calc-tool.js? Or sort-tool.js? Go figure...
scriptName: WScript.ScriptName.toString(),
// What kind of End Of Line sequence
// (they call it New Line sometimes too)
// you prefer when using outputToolbox?
eolStdOut: '\r\n',
eolStdErr: '\r\n',
// Those are all possible errorCodes that this script
// was designed to fail with.
// You can define other error codes.
// Those SHOULD be integer (not strings).
// Code noErrors = 0 is indication that script finished without errors.
errorCodes: {
noErrors: 0,
badArguments: 1,
badCopy: 2,
badParsing: 3,
badGettingFileList: 4
}
};
// - - - - -
// - - - - -
// outputToolbox depends on
// basicOptions && WScript
// Purpose of outputToolbox: organize in one group
// functions that are used to
// output chars or messages to stdout or stderr
var outputToolbox = {
// Syntactic sugar for printing exact set of chars to stdout.
print: function( data ) {
// For this script to output UTF characters correctly, program must
// run under /U switch - like
// cscript /U //NoLogo program-name-here.js arg1 arg2
WScript.StdOut.Write( data );
},
// Syntactic sugar for printing one line to stdout.
// terminated with your preferred line-terminator.
printLine: function( data ) {
outputToolbox.print( data );
outputToolbox.print( basicOptions.eolStdOut );
},
// Syntactic sugar for printing exact set of chars to stderr.
printErr: function( data ) {
WScript.StdErr.Write( data );
},
// Syntactic sugar for printing one line to stderr
// terminated with your preferred line-terminator.
printErrLine: function( data ) {
outputToolbox.printErr( data );
outputToolbox.printErr( basicOptions.eolStdErr );
},
// Syntactic sugar for printing one line to stderr
// prepended with meaningful script name (that is useful for debug)
// terminated with your preferred line-terminator.
printErrMessage: function( data ) {
outputToolbox.printErr( basicOptions.scriptName + ' failed' );
outputToolbox.printErr( data );
outputToolbox.printErr( basicOptions.eolStdErr );
},
// Syntactic sugar for printing one line to stderr
// prepended with meaningful script name (that is useful for debug)
// terminated with your preferred line-terminator.
printErrWarning: function( data ) {
outputToolbox.printErr( 'Warning for ' + basicOptions.scriptName + ': ' );
outputToolbox.printErr( data );
outputToolbox.printErr( basicOptions.eolStdErr );
},
// Syntactic sugar for printing Error objects
// catched by throw...catch construction
printErrCatchedErr: function( e ) {
outputToolbox.printErrMessage(
' - catched error details - ' + e.name + ': ' + e.message
);
}
};
// - - - - -
// flowToolbox depends on
// WScript
// Purpose of flowToolbox: organize in one group
// functions that are used to
// control general flow of program
var flowToolbox = {
// Shortcut to kill this script.
// When invoked it stops execution.
die: function ( code ) {
if ( typeof code === 'undefined' ) {
code = 0;
}
WScript.Quit(code);
}
};
// - - - - -
// Here is a cleaner alternative: we pollute only name $b.
// Not so easy to type but later, you can easily find all code calls
// dependant on functions from 'basis'.
// '$b' stands for 'library-b' or more exactly - "library named 'basis'"
var $b = {};
$b.print = outputToolbox.print;
$b.printLine = outputToolbox.printLine;
$b.printErr = outputToolbox.printErr;
$b.printErrLine = outputToolbox.printErrLine;
$b.printErrMessage = outputToolbox.printErrMessage;
$b.printErrWarning = outputToolbox.printErrWarning;
$b.die = flowToolbox.die;
// ^ You can use those as functions now
// - - - - -
// $fileListToolbox
// depends on WScript && ActiveXObject( "Scripting.FileSystemObject" ) && $b
var $fileListToolbox = {
/*
* Input:
* any gibberish that is acceptable
* for your local version of
* Scripting.FileSystemObject
* - like
* getFileListForFolder( 'C:\' );
* getFileListForFolder( 'C:\abc' );
* getFileListForFolder( './' );
* getFileListForFolder( './abc/../abc/..' );
*
* Output:
* aFileList with format:
* empty array
* or
* array of oFileData
* oFileData has format:
* {
* fileNameBase: ...[string],
* fileNameExtension: ...[string],
* parentAbsolutePath: ...[string]
* }
*/
getFileListForFolder: function( folderSpec )
{
var aResult = [];
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
if ( !fso.FolderExists( folderSpec ) ) {
$b.printErrMessage(' folder="' + folderSpec + '" doesn\'t exist');
$b.die( basicOptions.errorCodes.badGettingFileList );
}
var folder = fso.GetFolder( folderSpec );
var files = new Enumerator( folder.files );
for ( ; !files.atEnd(); files.moveNext() ) {
var thisFileItem = files.item();
aResult.push({
fileNameBase: fso.GetBaseName( thisFileItem ),
fileNameExtension: fso.GetExtensionName( thisFileItem ),
parentAbsolutePath: (
fso.GetParentFolderName(
fso.GetAbsolutePathName( thisFileItem )
)
)
});
}
return( aResult );
},
/*
* Purpose:
* Sort files by fileNameBase
*
* Input:
* aFileList
* - format is the same
* as for function $fileListToolbox.getFileListForFolder
*
* Output:
* - format is the same
* as for function $fileListToolbox.getFileListForFolder
*/
sortFileListByFileNameBaseAsc: function( aFileList ) {
var fSort = function( a, b ) {
if ( a.fileNameBase > b.fileNameBase ) {
return 1;
}
if ( a.fileNameBase < b.fileNameBase ) {
return -1;
}
return 0;
}
return aFileList.sort( fSort );
},
/*
Output:
false
when no results
oFileData
when result found
*/
getAnyFileWithBaseName: function( baseName, aFileList ) {
var i;
var L = aFileList.length;
for ( i = 0; i < L; i++ ) {
var a = aFileList[i];
if ( a.fileNameBase === baseName ) {
return a;
}
}
return false;
},
/*
* Purpose:
* Tool for displaying contents of fileList
*
* Input:
* aFileList
* - format is the same
* as for function $fileListToolbox.getFileListForFolder
*
* Sideeffects:
* Prints aFileList to the stdout stream
*/
printFileList: function( aFileList, callbackPrintLine ) {
if ( typeof callbackPrintLine === 'undefined' ) {
return;
}
var i;
var L = aFileList.length;
var elem;
for ( i = 0; i < L; i++ ) {
elem = aFileList[i];
callbackPrintLine();
callbackPrintLine( 'i: ' + i );
callbackPrintLine( 'fileNameBase: ' + elem.fileNameBase );
callbackPrintLine( 'fileNameExtension: ' + elem.fileNameExtension );
callbackPrintLine( 'parentAbsolutePath: ' + elem.parentAbsolutePath );
}
}
};
/*
// basic test:
$fileListToolbox.printFileList(
$fileListToolbox.sortFileListByFileNameBaseAsc(
$fileListToolbox.getFileListForFolder( 'bTmp' )
),
$b.printLine
);
*/
/*
// test for fuzzy search only by file base name:
$fileListToolbox.printFileList(
$fileListToolbox.sortFileListByFileNameBaseAsc(
$fileListToolbox.getFileListForFolder( 'bTmp' )
),
$b.printLine
);
var fuzzy = $fileListToolbox.getAnyFileWithBaseName(
'input_1',
$fileListToolbox.getFileListForFolder( 'bTmp' )
);
$b.printLine( 'fuzzy.fileNameBase: ' + fuzzy.fileNameBase );
$b.printLine( 'fuzzy.fileNameExtension: ' + fuzzy.fileNameExtension );
*/
// $streamToolbox
var $streamToolbox = {
// when end of line returns '' (aka empty string)
getChar: function() {
if ( WScript.StdIn.AtEndOfStream ) {
return '';
}
return WScript.StdIn.Read(1);
},
// tests show that for Windows 7 it understands terminators
// CRLF or LF-only
// when end of line returns '' (aka empty string)
getLine: function() {
if ( WScript.StdIn.AtEndOfStream ) {
return '';
}
return WScript.StdIn.ReadLine();
}
}
var $s = $streamToolbox;
// it could be used like that:
/*
var piece;
while ( ( piece = $s.getLine() ) !== '' ) {
$b.printLine( 'piece: ' + '>' + piece + '<' );
}
*/
// $thisArguments
var $thisArguments = {
getArgumentsObj: function() {
return WScript.Arguments;
// mockup for tests in browser
/*
var testArr = [1,2,3,4,5];
var mockupArguments = function( i ) {
return testArr[i];
};
mockupArguments.length = testArr.length;
return mockupArguments;
*/
},
printErrorMessageAndDie: function( meaningfulPart ) {
$b.printErrMessage(
' - problem when ' +
'parsing command-line arguments (options): ' +
''
);
$b.die( basicOptions.errorCodes.badArguments );
},
getArgumentsDummy: function() {
return {
direction: 'from-id-2-to-id-1',
resultExtension: 'auto',
folderFrom: 'C:\abc',
folderTo: 'D:\abc',
mode: 'training',
modeForExtensionFrom: 'strict'
};
},
print: function( argumentsObject ) {
$b.printErrLine( 'folderFrom: ' + argumentsObject.folderFrom );
$b.printErrLine( 'folderTo: ' + argumentsObject.folderTo );
$b.printErrLine( 'direction: ' + argumentsObject.direction );
$b.printErrLine( 'resultExtension: ' + argumentsObject.resultExtension );
$b.printErrLine( 'mode: ' + argumentsObject.mode );
$b.printErrLine( 'modeForExtensionFrom: ' + argumentsObject.modeForExtensionFrom );
},
isThereAFlagInArguments: function( inString ) {
var normalizedEthalon = inString.toString().toLowerCase();
var objArgs = $thisArguments.getArgumentsObj();
var i;
var L = objArgs.length;
for ( i = 2; i < L; i++ ) {
var normalizedArgument = objArgs( i ).toString().toLowerCase();
if ( normalizedEthalon === normalizedArgument ) {
return true;
}
}
return false;
},
getArguments: {
folderFrom: function() {
var rR = '';
var objArgs = $thisArguments.getArgumentsObj();
if ( objArgs.length < 2 ) {
$b.printErrMessage(
' - reason: not enough arguments, ' +
'you MUST to provide at least 2 arguments, ' +
'but ammount of arguments you provided ' +
'is only: ' + objArgs.length
);
$b.die( basicOptions.errorCodes.badArguments );
}
rR = objArgs(0);
var fso = new ActiveXObject("Scripting.FileSystemObject");
rR = fso.GetAbsolutePathName( rR );
return rR;
},
folderTo: function() {
var rR = '';
var objArgs = $thisArguments.getArgumentsObj();
if ( objArgs.length < 2 ) {
$b.printErrMessage(
' - reason: not enough arguments, ' +
'you MUST to provide at least 2 arguments, ' +
'but ammount of arguments you provided ' +
'is only: ' + objArgs.length
);
$b.die( basicOptions.errorCodes.badArguments );
}
rR = objArgs(1);
var fso = new ActiveXObject("Scripting.FileSystemObject");
rR = fso.GetAbsolutePathName( rR );
return rR;
},
direction: function() {
var rR = 'from-id-1-to-id-2';
var hasFlag = $thisArguments.isThereAFlagInArguments;
if ( hasFlag( '/MapFrom2To1' ) ) {
rR = 'from-id-2-to-id-1';
}
return rR;
},
resultExtension: function() {
var rR = 'auto';
var hasFlag = $thisArguments.isThereAFlagInArguments;
if ( hasFlag( '/ResultExtension:none' ) ) {
rR = 'none';
}
if ( hasFlag( '/ResultExtension:21' ) ) {
rR = '21';
}
if ( hasFlag( '/ResultExtension:12' ) ) {
rR = '12';
}
if ( hasFlag( '/ResultExtension:2' ) ) {
rR = '2';
}
if ( hasFlag( '/ResultExtension:1' ) ) {
rR = '1';
}
return rR;
},
mode: function() {
var rR = 'training';
var hasFlag = $thisArguments.isThereAFlagInArguments;
if ( hasFlag( '/NotTraining' ) ) {
rR = 'not-training';
}
return rR;
},
modeForExtensionFrom: function() {
var rR = 'strict';
var hasFlag = $thisArguments.isThereAFlagInArguments;
if ( hasFlag( '/FuzzyExtensionsFrom' ) ) {
rR = 'fuzzy';
}
return rR;
}
},
getArgumentsAll: function() {
var $get = $thisArguments.getArguments;
var rR = {
folderFrom: $get.folderFrom(),
folderTo: $get.folderTo(),
direction: $get.direction(),
resultExtension: $get.resultExtension(),
mode: $get.mode(),
modeForExtensionFrom: $get.modeForExtensionFrom()
};
return rR;
}
};
var $a = $thisArguments;
//you can use it like this:
/*
var $inputOptions = $a.getArgumentsAll()
$a.print( $inputOptions );
*/
// $parser
// mockup options for separate testing
/*
var basicOptions = {
errorCodes: {
badParsing: 3
}
};
var $b = {
printErrMessage: function( a ) { console.log(a); },
die: function() { throw new Error(); }
};
*/
var $parser = {};
$parser.generateDummyLineReader = function( textBlock ) {
var lines = textBlock.split( '\r\n' );
var i = -1;
var L = lines.length;
var f = function() {
i++;
if ( i >= L ) {
return '';
}
return lines[i];
}
return f;
}
$parser.generateDummyInputOptions = function() {
return {
direction: '1-to-2',
resultExtension: '12',
folderFrom: 'C:\abc',
folderTo: 'D:\abc'
}
}
$parser.generateDummyMappingLineReader = function() {
return $parser.generateDummyLineReader(
[
'id:1',
'baseName:abcdef',
'extensionName:ext',
'id:2',
'baseName:input_001',
'extensionName:txt',
':',
'id:1',
'baseName:ghijkl',
'extensionName:jpg',
'id:2',
'baseName:input_002',
'extensionName:png',
':'
].join('\r\n')
);
}
/*
Remark:
functionReadNextLine
must be iterator that returns '' (aka empty string)
when stream of lines ends
functionForEachBlockDo must have
those arguments:
- id1Base
- id1Ext
- id2Base
- id2Ext
- $inputOptions
- functionCopyFile
$inputOptions must have properties
(wherever it mentioned in this block of comments):
- direction
- resultExtension
- folderFrom
- folderTo
functionCopyFile must have these arguments
- folderFrom
- fileFrom
- folderTo
- fileTo
*/
$parser.parseLinesAndForEachBlockDo = function(
functionReadNextLine,
functionForEachBlockDo,
$inputOptions,
functionCopyFile
) {
var buffer = null;
var nOfLine = 0;
var states = [
'waitingForId1',
'waitingForId2',
'waitingForBase',
'waitingForExtension',
'waitingForEndOfGroup'
];
function printErrorMessageAndDie( meaningfulPart ) {
$b.printErrMessage(
' - problem when parsing line ' + nOfLine + ': ' +
'[' + typeof line + ']' +
'"' + line + '"' +
meaningfulPart +
''
);
$b.die( basicOptions.errorCodes.badParsing );
}
var state = 'waitingForId1';
var line;
while (
( line = functionReadNextLine() ) !== ''
) {
++nOfLine;
var arOrNull = line.match( /^([a-zA-Z0-9-]*):(.*)$/ );
if (
( arOrNull === null )
) {
printErrorMessageAndDie(
'every line in mapping must be either' +
'"key:value" or ":"'
);
}
var key = arOrNull[1];
var value = arOrNull[2];
if (
( value.match( '\r' ) !== null )
) {
printErrorMessageAndDie(
'if line is ' +
'"key:value" ' +
'then it MUST NOT contain \r (CR) symbols ' +
'exept in terminator'
);
}
if ( state === 'waitingForId1' ) {
if (
( key === 'id' )
&&
( value === '1' )
) {
buffer = {};
buffer.nowGroup = {
id: 1
};
state = 'waitingForBase';
continue;
} else {
printErrorMessageAndDie(
'but this line must be "id:1"'
);
}
}
if ( state === 'waitingForBase' ) {
if (
( key === 'baseName' )
) {
buffer.nowGroup.base = value;
state = 'waitingForExtension';
continue;
} else {
printErrorMessageAndDie(
'but this line must be "baseName:...anything-here..."'
);
}
}
if ( state === 'waitingForExtension' ) {
if (
( key === 'extensionName' )
) {
buffer.nowGroup.ext = value;
if ( buffer.nowGroup.id === 1 ) {
state = 'waitingForId2';
} else {
state = 'waitingForEndOfGroup';
}
continue;
} else {
printErrorMessageAndDie(
'but this line must be "extensionName:...anything-here..."'
);
}
}
if ( state === 'waitingForId2' ) {
if (
( key === 'id' )
&&
( value === '2' )
) {
buffer.group1 = {
base: buffer.nowGroup.base,
ext: buffer.nowGroup.ext
}
buffer.nowGroup = {
id: 2
};
state = 'waitingForBase';
continue;
} else {
printErrorMessageAndDie(
'but this line must be "id:2"'
);
}
}
if ( state === 'waitingForEndOfGroup' ) {
if (
( key === '' )
&&
( value === '' )
) {
buffer.group2 = {
base: buffer.nowGroup.base,
ext: buffer.nowGroup.ext
}
functionForEachBlockDo(
buffer.group1.base,
buffer.group1.ext,
buffer.group2.base,
buffer.group2.ext,
$inputOptions,
functionCopyFile
);
buffer = null;
state = 'waitingForId1';
continue;
} else {
printErrorMessageAndDie(
'but this line must be ":"'
);
}
}
}
if ( buffer !== null ) {
printErrorMessageAndDie(
'dangling state (not all properties group are defined) - ' + state
);
}
}
/*
// test
// can be tested in for browser with only $parser object
$parser.parseLinesAndForEachBlockDo(
$parser.generateDummyMappingLineReader(),
function( a, b, c, d ) {
console.log( 'a:', a, 'b:', b, 'c:', c, 'd:', d );
}
);
*/
/*
Remark:
$inputOptions must have properties:
- direction
- resultExtension
- folderFrom
- folderTo
*/
$parser.reactionOnGoodParsedBlock = function(
id1Base, id1Ext,
id2Base, id2Ext,
$inputOptions,
functionCopyFile
) {
function glueExtensionToFileName(
fileName, extension
) {
fileName = fileName.toString();
extension = extension.toString();
if ( extension.length > 0 ) {
extension = '.' + extension;
}
return fileName + extension;
}
var folderFrom = $inputOptions.folderFrom;
var folderTo = $inputOptions.folderTo;
if ( $inputOptions.modeForExtensionFrom === 'fuzzy' ) {
var tmpFromBase = id1Base;
if ( $inputOptions.direction == 'from-id-2-to-id-1' ) {
tmpFromBase = id2Base;
}
var fuzzyFile = $fileListToolbox.getAnyFileWithBaseName(
tmpFromBase,
$fileListToolbox.getFileListForFolder( folderFrom )
);
if ( fuzzyFile !== false ) {
if ( $inputOptions.direction == 'from-id-2-to-id-1' ) {
id2Ext = fuzzyFile.fileNameExtension;
} else {
id1Ext = fuzzyFile.fileNameExtension;
}
}
}
var fromSet = { base: id1Base, ext: id1Ext };
var toSet = { base: id2Base, ext: id2Ext };
if ( $inputOptions.direction == 'from-id-2-to-id-1' ) {
fromSet = { base: id2Base, ext: id2Ext };
toSet = { base: id1Base, ext: id1Ext };
}
if ( $inputOptions.resultExtension === '21' ) {
toSet.ext = glueExtensionToFileName( id2Ext, id1Ext );
}
if ( $inputOptions.resultExtension === '12' ) {
toSet.ext = glueExtensionToFileName( id1Ext, id2Ext );
}
if ( $inputOptions.resultExtension === '2' ) {
toSet.ext = id2Ext;
}
if ( $inputOptions.resultExtension === '1' ) {
toSet.ext = id1Ext;
}
if ( $inputOptions.resultExtension === 'none' ) {
toSet.ext = '';
}
var fileFrom = glueExtensionToFileName( fromSet.base, fromSet.ext );
var fileTo = glueExtensionToFileName( toSet.base, toSet.ext );
functionCopyFile(
folderFrom, fileFrom,
folderTo, fileTo
);
}
/*
// test
// can be tested in for browser with only $parser object
$parser.reactionOnGoodParsedBlock(
'originalA', 'extA',
'originalB', 'extB',
{
direction: 'from-id-2-to-id-1',
resultExtension: 'auto',
folderFrom: 'C:\abc',
folderTo: 'D:\abc'
},
function(
folderFrom, fileFrom,
folderTo, fileTo
) {
console.log(
'folderFrom:', folderFrom,
'fileFrom:', fileFrom,
'folderTo:', folderTo,
'fileTo:', fileTo
);
}
);
// expected result:
// folderFrom: C:abc fileFrom: originalA.extA folderTo: D:abc fileTo: originalB.extB
*/
var $copyFileToolbox = {
copyFileTraining: function(
folderFrom, fileFrom,
folderTo, fileTo
) {
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
if ( !fso.FolderExists( folderFrom ) ) {
$b.printErrWarning('folderFrom="' + folderFrom + '" doesn\'t exist');
}
if ( !fso.FolderExists( folderTo ) ) {
$b.printErrWarning('folderTo="' + folderTo + '" doesn\'t exist');
}
var pathFrom = fso.BuildPath( folderFrom, fileFrom );
var pathTo = fso.BuildPath( folderTo, fileTo );
if ( !fso.FileExists( pathFrom ) ) {
$b.printErrLine();
$b.printErrWarning('no source file found, script looking for it at pathFrom="' + pathFrom + '" - this file doesn\'t exist');
}
if ( fso.FileExists( pathTo ) ) {
$b.printErrLine();
$b.printErrWarning('be advised - there was found a file at destination for copy command, and in "NotTraining" mode you will overwrite file at pathTo="' + pathTo + '"');
}
$b.printLine( '' );
$b.printLine( 'Training mode: you ask to copy file' );
$b.printLine( 'from: ' + pathFrom );
$b.printLine( '\/' );
$b.printLine( 'to: ' + pathTo );
},
copyFile: function(
folderFrom, fileFrom,
folderTo, fileTo
) {
var fso = new ActiveXObject( "Scripting.FileSystemObject" );
if ( !fso.FolderExists( folderFrom ) ) {
$b.printErrMessage(' folderFrom="' + folderFrom + '" doesn\'t exist');
$b.die( basicOptions.errorCodes.badCopy );
}
if ( !fso.FolderExists( folderTo ) ) {
$b.printErrMessage (' folderTo="' + folderTo + '" doesn\'t exist');
$b.die( basicOptions.errorCodes.badCopy );
}
var pathFrom = fso.BuildPath( folderFrom, fileFrom );
var pathTo = fso.BuildPath( folderTo, fileTo );
if ( !fso.FileExists( pathFrom ) ) {
$b.printErrMessage(' no source file found, script looking for it at pathFrom="' + pathFrom + '" doesn\'t exist');
$b.die( basicOptions.errorCodes.badCopy );
}
fso.copyFile( pathFrom, pathTo, true );
}
}
var $c = $copyFileToolbox;
// you can use it as:
/*
$c.copyFileTraining(
'./', '123.txt',
'b', '345.txt'
);
*/
// you will have a lot of problems testing if filename is unicode
// only way that I found for Windows 7 is to pass filename as stdin
// from some file (perhaps any streaming from file works ok here...)
// and yes, test shows that it works...
function main() {
var $inputOptions = $a.getArgumentsAll();
$b.printErrLine();
$b.printErrLine(
'Arguments for ' + basicOptions.scriptName +
' are interpreted this way:'
);
$a.print( $inputOptions );
$b.printErrLine();
var fCopyFile = $c.copyFileTraining;
if ( $inputOptions.mode === 'not-training' ) {
fCopyFile = $c.copyFile;
}
/*
var fuzzy = $fileListToolbox.getAnyFileWithBaseName(
'input_1',
$fileListToolbox.getFileListForFolder( 'bTmp' )
);
*/
$parser.parseLinesAndForEachBlockDo(
$s.getLine,
$parser.reactionOnGoodParsedBlock,
$inputOptions,
fCopyFile
);
$b.printErr( 'Script ' + basicOptions.scriptName + ' - finished!' );
};
main();