How to determine the OS path separator in JavaScript? - javascript

How can I tell in JavaScript what path separator is used in the OS where the script is running?

Use path module in node.js returns the platform-specific file separator.
example
path.sep // on *nix evaluates to a string equal to "/"
Edit: As per Sebas's comment below, to use this, you need to add this at the top of your js file:
const path = require('path')

Afair you can always use / as a path separator, even on Windows.
Quote from http://bytes.com/forum/thread23123.html:
So, the situation can be summed up
rather simply:
All DOS services since DOS 2.0 and all Windows APIs accept either forward
slash or backslash. Always have.
None of the standard command shells (CMD or COMMAND) will accept forward
slashes. Even the "cd ./tmp" example
given in a previous post fails.

The Correct Answer
Yes all OS's accept CD ../ or CD ..\ or CD .. regardless of how you pass in separators. But what about reading a path back. How would you know if its say, a 'windows' path, with ' ' and \ allowed.
The Obvious 'Duh!' Question
What happens when you depend on, for example, the installation directory %PROGRAM_FILES% (x86)\Notepad++. Take the following example.
var fs = require('fs'); // file system module
var targetDir = 'C:\Program Files (x86)\Notepad++'; // target installer dir
// read all files in the directory
fs.readdir(targetDir, function(err, files) {
if(!err){
for(var i = 0; i < files.length; ++i){
var currFile = files[i];
console.log(currFile);
// ex output: 'C:\Program Files (x86)\Notepad++\notepad++.exe'
// attempt to print the parent directory of currFile
var fileDir = getDir(currFile);
console.log(fileDir);
// output is empty string, ''...what!?
}
}
});
function getDir(filePath){
if(filePath !== '' && filePath != null){
// this will fail on Windows, and work on Others
return filePath.substring(0, filePath.lastIndexOf('/') + 1);
}
}
What happened!?
targetDir is being set to a substring between the indices 0, and 0 (indexOf('/') is -1 in C:\Program Files\Notepad\Notepad++.exe), resulting in the empty string.
The Solution...
This includes code from the following post: How do I determine the current operating system with Node.js
myGlobals = { isWin: false, isOsX:false, isNix:false };
Server side detection of OS.
// this var could likely a global or available to all parts of your app
if(/^win/.test(process.platform)) { myGlobals.isWin=true; }
else if(process.platform === 'darwin'){ myGlobals.isOsX=true; }
else if(process.platform === 'linux') { myGlobals.isNix=true; }
Browser side detection of OS
var appVer = navigator.appVersion;
if (appVer.indexOf("Win")!=-1) myGlobals.isWin = true;
else if (appVer.indexOf("Mac")!=-1) myGlobals.isOsX = true;
else if (appVer.indexOf("X11")!=-1) myGlobals.isNix = true;
else if (appVer.indexOf("Linux")!=-1) myGlobals.isNix = true;
Helper Function to get the separator
function getPathSeparator(){
if(myGlobals.isWin){
return '\\';
}
else if(myGlobals.isOsx || myGlobals.isNix){
return '/';
}
// default to *nix system.
return '/';
}
// modifying our getDir method from above...
Helper function to get the parent directory (cross platform)
function getDir(filePath){
if(filePath !== '' && filePath != null){
// this will fail on Windows, and work on Others
return filePath.substring(0, filePath.lastIndexOf(getPathSeparator()) + 1);
}
}
getDir() must be intelligent enough to know which its looking for.
You can get even really slick and check for both if the user is inputting a path via command line, etc.
// in the body of getDir() ...
var sepIndex = filePath.lastIndexOf('/');
if(sepIndex == -1){
sepIndex = filePath.lastIndexOf('\\');
}
// include the trailing separator
return filePath.substring(0, sepIndex+1);
You can also use 'path' module and path.sep as stated above, if you want to load a module to do this simple of a task. Personally, i think it sufficient to just check the information from the process that is already available to you.
var path = require('path');
var fileSep = path.sep; // returns '\\' on windows, '/' on *nix
And Thats All Folks!

As already answered here, you can find the OS specific path separator with path.sep to manually construct your path. But you can also let path.join do the job, which is my preferred solution when dealing with path constructions:
Example:
const path = require('path');
const directory = 'logs';
const file = 'data.json';
const path1 = `${directory}${path.sep}${file}`;
const path2 = path.join(directory, file);
console.log(path1); // Shows "logs\data.json" on Windows
console.log(path2); // Also shows "logs\data.json" on Windows

Just use "/", it works on all OS's as far as I know.

Related

javascript read .ini file

I tried searching . How do i do it? I'm create html and i want to read .ini file by javascript on the client Not in the server.
I copy code from javascript parser for a string which contains .ini data
error Uncaught ReferenceError: require is not defined var fs = require('fs')
function parseINIString() {
var fs = require('fs')
var data = fs.readFileSync('C:\\test.ini', 'utf8');
var regex = {
section: /^\s*\[\s*([^\]]*)\s*\]\s*$/,
param: /^\s*([\w\.\-\_]+)\s*=\s*(.*?)\s*$/,
comment: /^\s*;.*$/
};
var value = {};
var lines = data.split(/\r\n|\r|\n/);
var section = null;
alert(lines);
for (x = 0; x < lines.length; x++) {
if (regex.comment.test(lines[x])) {
return;
} else if (regex.param.test(lines[x])) {
var match = lines[x].match(regex.param);
if (section) {
value[section][match[1]] = match[2];
} else {
value[match[1]] = match[2];
}
} else if (regex.section.test(lines[x])) {
var match = lines[x].match(regex.section);
value[match[1]] = {};
section = match[1];
} else if (lines.length == 0 && section) {//changed line to lines to fix bug.
section = null;
};
}
return value;
}
Let's say the javascript running in a browser is so called 'client script'. There are lots of limitation while writing client script, one of them is that it's not allowed to visit the user file on disk. This is to prevent any injected hacker script from reading private data. And the explicit error you see is about the new key word 'require' which is well known as 'commonjs' module which is introduced by Nodejs usually. The 'fs' is one of the internal module of Nodejs as well.
So if you still consist using client script to get the job done, you have to rewrite the script, not 'require' the 'fs' module. And use the file reader to get the content of a file object, which is generated by a file input usually.
A detailed introduction about how to read local files.

How to get a value in a callback function out of the callback

I am writing a Webpack loader for Pug files. It loads all the dependent images referenced in the Pug file, uses file-loader to copy them to the appropriate directory, and replaces require() calls in the Pug file with the URLs of the resultant file.
I am trying to add functionality to allow for data URLs to be interpolated into the Pug file in lieu of file URLs. I am using this.loadModule() to execute file-loader now.
This function takes a callback as an argument. One of the args passed to this callback contains the result of url-loader, the data URL. So, I need this to write into the Pug file that should be output.
The problem is, the entire loader finishes running before that callback ever runs. So, anything I do with the data in there doesn't end up in the final file. The overall structure of my program looks like this:
module.exports = function pugDepLoader(inputFile) {
/* Setting up various constants and variables. */
/* A while() loop that continually tests inputFile with a
regex to find require() statements.
It creates a copy of inputFile called outputFile,
which it iterates over again and again,
replacing require() statements with file paths
one at a time.
The call to this.loadModule() is within this while() loop. */
/* End of while() loop */
/* Return outputFile to Webpack, which is now a
string with the Pug file, but with the
require() statements replaced with
file paths.
This is meant to be fed to file-loader to
get written out to disc. */
}
That return statement at the end will get called repeatedly before this.loadModule()'s callback ever runs once, as I have discovered with debugging statements. I need to be able to get that data URL that is provided within the callback into outputFile before I return it, so that it will end up in the file that is ultimately written to disc.
Entire source code follows:
module.exports = function pugDepLoader(inputFile) {
const fs = require('fs');
const path = require('path');
const loaderUtils = require('loader-utils');
var options = loaderUtils.getOptions(this);
//This option is required. It specifies
//the path to the root of the Pug file's
//dependencies relative to the location
//of the Pug files.
var contextualRoot;
if(!options || !options.hasOwnProperty('contextualRoot')) {
throw new Error('You must specify a contextual root');
}
else {
contextualRoot = options.contextualRoot;
//Ensure there is a trailing slash.
if(contextualRoot[contextualRoot.length-1] !== '/') {
contextualRoot = contextualRoot + '/';
}
}
//Determines whether paths should begin with
//a leading slash. Useful for Express.js
//compatibility.
if(!options.hasOwnProperty('absolute')) {
options.absolute = false;
}
//Set up regex to search for require() statements
const reqRE = new RegExp(/require\(/, 'g');
//outputFile will be returned containing the
//appropriately processed Pug
var outputFile = inputFile.slice();
//We need to execute reqRE once to kick things
//off, and we need to save it to a variable
//because we need information from it.
let regexResult = reqRE.exec(inputFile);
//regexResult will be null when there
//are no more matches to be found in
//the file.
while(regexResult != null) {
//pathStartIndex is the beginning of
//the path to the required file.
//We add 1 to skip the opening
//single quote.
let pathStartIndex = reqRE.lastIndex+1;
//We require the path to be wrapped in
//single quotes, so that we can easily
//be certain about where the require
//statement ends.
if(inputFile[reqRE.lastIndex] !== "'") {
console.log('FATAL ERROR: File path must be wrapped in single quotes!');
break;
}
//inputPath will hold the actual file path itself.
let inputPath = inputFile.slice(pathStartIndex, inputFile.indexOf("'", pathStartIndex));
//pathArray is used to split the
//path so we can easily extract
//the file name and path
//separately.
let pathArray = inputPath.split('/');
//Just the file name, with extension.
let fileName = pathArray.pop();
//outputPath will define what path should be
//written into the output Pug file.
//The user may optionally specify a
//custom output path.
let outputPath;
if(options.outputPath) {
outputPath = options.outputPath;
}
else {
outputPath = pathArray.join('/');
}
//Ensure a trailing slash.
if(outputPath[outputPath.length-1] !== '/') {
outputPath = (outputPath + '/');
}
//reqStart holds the index of the letter
//"r" in require(), so that we can remove
//the require() call and replace it
//with the file path.
let reqStart = inputFile.indexOf('require', regexResult.index);
//reqStmt is the require() statement in
//full. This will be used with replace()
//to replace the require() call with a
//file path in the output file.
let reqStmt = inputFile.slice(reqStart, inputFile.indexOf('"', pathStartIndex));
//The final file path, with file name.
//This will be written into the output
//Pug file in place of the require()
//calls.
let filePath = outputPath + fileName;
if(options.absolute && filePath[0] !== '/') {
if(filePath[0] === '.' && filePath[1] === '/') {
filePath = filePath.slice(1);
}
else {
filePath = '/' + filePath;
}
}
else if(!options.absolute && filePath[0] === '/') {
filePath = filePath.slice(1);
}
this.loadModule(contextualRoot + inputPath, (err, res, srcmap, module) => {
if(err) {
console.log(err);
}
if(new RegExp(/data:image/).test(res)) {
filePath = res.slice(res.indexOf('data:image'), res.lastIndexOf('"'));
}
});
//This takes care of require() calls
//inside of url() CSS functions,
//such as are used to declare
//background images inline.
//If the word "require" is
//preceeded by a single quote,
//it is within a url() function,
//and so we add appropriate closure
//for that function to the end of
//the path.
if(inputFile[reqStart-1] === "'") {
filePath = filePath + "');";
}
//Write the output.
outputFile = outputFile.replace(reqStmt, filePath);
//Run the next iteration of the regex search.
regexResult = reqRE.exec(inputFile);
}
//Return output as a string to Webpack.
return outputFile;
}

Simple JavaScript code does not work with nodejs

I am an experienced JavaScript programmer, but am just starting to learn node.js.
Using node, I want to read the contents of a directory, and print out files of only a specific extension. Both the directory and file-extension will be given by command-line arguments.
But, I also want to push myself and explore JavaScript programming concepts as I solve these puzzles, so I wanted to create a File object to store information about a file, and use that to solve the problem.
Becaause of this, my approach is overly-complex, and I know that there are simpler ways of doing this, but I just want an answer which solves my current problem:
Why does node.js throw the following error
this.baseName = /.+(?=\.[^.]+)/.exec(file)[0];
^
TypeError: Cannot read property '0' of null
in this code:
function File(file){
if (file instanceof File) return file;
if (!(this instanceof File)) return new File(file);
if (typeof file !== "string") throw new Error("Useful error." + typeof(file));
this.fullName = file;
/*vvvvvvvvvvvv Important Bit vvvvvvvvvvvvvv*/
this.baseName = /.+(?=\.[^.]+)/.exec(file)[0];
this.extension = /[^.]+(?=$)/.exec(file)[0];
}
File.prototype = {
isOfType: function(ext){
return ext === this.extension;
}
}
var fs = require('fs');
var argv = process.argv;
fs.readdir(argv[2], function(err, list){
var res = list.filter(function(element, index, array){
var file = new File(element);
return file.isOfType(argv[3]);
});
console.log(res);
});
but, in a Chrome js console, it runs fine (with simulated process and fs objects of course).
To me (inexperienced me) it looks like node could be making several mistakes:
Not handling the regex properly (I've done tests and this seems likely)
Using square brackets to find key '0' within object, instead of index 0 within array.
Or I could be making several mistakes:
Not understanding fs.readdir and its necessary callback.
Not understanding possible differences between constructors in JavaScript and Node
Please help, I'd like an answer that solves or explains my current problem, not one that works around it.
Thanks.
Node has a built in way to check for valid files
function File(file){
if (file instanceof File) return file;
if (!(this instanceof File)) return new File(file);
if (typeof file !== "string") throw new Error("Useful error." + typeof(file));
var stat = fs.statSync(file);
if ( stat && stat.isFile() ) {
this.fullName = file;
/*vvvvvvvvvvvv Important Bit vvvvvvvvvvvvvv*/
this.baseName = /.+(?=\.[^.]+)/.exec(file)[0];
this.extension = /[^.]+(?=$)/.exec(file)[0];
}
}
Node also has a built in way to get extensions and path names
var path = require('path');
var basename = path.basename(file);
var extension = path.extname(file);
Why does node.js throw the following error
this.baseName = /.+(?=\.[^.]+)/.exec(file)[0];
^
TypeError: Cannot read property '0' of null
Because file doesn't match the regular expression, and so exec returns null; then you're doing null[0], which throws the exception. That would happen for a name like a or a., for instance, or the . and .. pseudo-directories.
Use the NodeJS debugger or just console.log file` immediately prior, an you'll see the value that's causing trouble.
It's because any file names which don't match your regular expression, such as many directory names (which are included in your list variable), will then return null here:
/.+(?=\.[^.]+)/.exec(file)
so you are trying to do
null[0]
which will not work.
I solved this by simply checking for a period within the file name:
var file = (element.indexOf(".") > -1) ? new File(element) : false;
return (file) ? file.isOfType(argv[3]) : false;
within the filter, so final code resembles:
fs.readdir(argv[2], function(err, list){
var res = list.filter(function(element, index, array){
var file = (element.indexOf(".") > -1) ? new File(element) : false;
return (file) ? file.isOfType(argv[3]) : false;
});
for (var i = 0; i < res.length; console.log(res[i++]));
});

Batch File > Javascript > WinSCP > Check if file exists

I have a batch file that will launch a .js file which, via WinSCP, checks if a file exists and returns to the batch file if it does or not.
The problem IS: It always returns not found, and I cannot figure out why. I am unsure how to use a wildcard in this scenario.
The batch file looks like this:
cscript /nologo file.js
if errorlevel 1 goto notfound
exit
:notfound
(another script to copy a file over)
Only one file can exist on the server at once. So every ten min, this batch file will run, check if there is a file, if not, copy one over.
The file.js:
// Configuration
// Remote file search for
var FILEPATH = "../filepath/TSS*";
// Session to connect to
var SESSION = "mysession#someplace.come";
// Path to winscp.com
var WINSCP = "c:\\program files (x86)\\winscp\\winscp.com";
var filesys = WScript.CreateObject("Scripting.FileSystemObject");
var shell = WScript.CreateObject("WScript.Shell");
var logfilepath = filesys.GetSpecialFolder(2) + "\\" + filesys.GetTempName() + ".xml";
var p = FILEPATH.lastIndexOf('/');
var path = FILEPATH.substring(0, p);
var filename = FILEPATH.substring(p + 1);
var exec;
// run winscp to check for file existence
exec = shell.Exec("\"" + WINSCP + "\" /log=\"" + logfilepath + "\"");
exec.StdIn.Write(
"option batch abort\n" +
"open \"" + SESSION + "\"\n" +
"ls \"" + path + "\"\n" +
"exit\n");
// wait until the script finishes
while (exec.Status == 0)
{
WScript.Sleep(100);
WScript.Echo(exec.StdOut.ReadAll());
}
if (exec.ExitCode != 0)
{
WScript.Echo("Error checking for file existence");
WScript.Quit(1);
}
// look for log file
var logfile = filesys.GetFile(logfilepath);
if (logfile == null)
{
WScript.Echo("Cannot find log file");
WScript.Quit(1);
}
// parse XML log file
var doc = new ActiveXObject("MSXML2.DOMDocument");
doc.async = false;
doc.load(logfilepath);
doc.setProperty("SelectionNamespaces",
"xmlns:w='http://winscp.net/schema/session/1.0'");
var nodes = doc.selectNodes("//w:file/w:filename[#value='" + filename + "']");
if (nodes.length > 0)
{
WScript.Echo("File found");
// signalize file existence to calling process;
// you can also continue with processing (e.g. downloading the file)
// directly from the script here
WScript.Quit(0);
}
else
{
WScript.Echo("File not found");
WScript.Quit(1);
}
On line 4 it says:
var FILEPATH = "../filepath/TSS*";
That star is what is giving me issues, i think. I need to look for a file which STARTS WITH TSS, but will have a time stamp tacked on the end. So i need to just use a wildcard after TSS.
So what i need help with is: Making this process return true if any file exists with TSS*
Any help would be much appreciated.
EDIT:
var nodes = doc.selectNodes("//w:file/w:filename[starts-with(#value, 'TSS')]");
This code seems to not work. If this code worked, it seems like it would solve all my problems.
You need to correct xpath expression in var nodes... line.
Try something like this:
doc.setProperty("SelectionLanguage", "XPath"); //added in edit
var nodes = doc.selectNodes("//w:file/w:filename[starts-with(#value, '" + filename + "')]");
and delete asterisk from FILEPATH.
Note: first line is required in order to use XPath as the query language, not default (and old) XSLPattern which doesn't support methods such as starts-with or contains.
SelectionLanguage Property (MDSN).
You can use the stat command. You can even inline the WinSCP script into the batch file:
#echo off
set REMOTE_PATH=/home/user/test.txt
winscp.com /command ^
"option batch abort" ^
"open mysession" ^
"stat %REMOTE_PATH%" ^
"exit"
if errorlevel 1 goto error
echo File %REMOTE_PATH% exists
rem Do something
exit 0
:error
echo Error or file %REMOTE_PATH% not exists
exit 1
An alternative is using the Session.FileExists from WinSCP .NET assembly.
For further details, see the WinSCP article Checking file existence.

How should I resolve a path passed to a node module?

I am working on creating my first Node.js module, and am having a difficult time trying to decide the best way to handle this. What is the recommended method for receiving paths to files to my module as input by the user? How can I then resolve where that is? Where the user called my module and the location of my running code are in two different places.
Let's take this a step further. Let's say a user has a JSON config file with an array of these paths (in config/settings.json):
{
"paths": [
"../path1",
"path2",
"/path/to/path3",
"~/path4"
]
}
Now obviously these paths are relative to the config file, not any executing code. This feels wrong, as now my method must accept the __dirname, the path of the config file relative to __dirname, etc.
var mymodule = require('mine');
mymodule(__dirname, './config/settings.json');
Any files I read from the config must then be based on the location of this config file. This feels weird. Suggestions?
From FileUtils source:
var updateFileProperties = function (file, path){
file._path = null;
file._usablePath = null;
file._isAbsolute = false;
if (path === undefined || path === null) return;
path = PATH.normalize (path);
var index = path.indexOf (":") + 1;
var windowsRoot = path.substring (0, index);
path = path.substring (index);
//https://github.com/joyent/node/issues/3066
if (path[0] === "/" && path[1] === "/"){
path = path.replace (/[\/]/g, "\\");
path = path.substring (0, path.length - 1);
}
file._isAbsolute = path[0] === SLASH;
file._path = windowsRoot + path;
file._usablePath = file._isAbsolute ? file._path : (windowsRoot + PATH.join (file._relative, path));
};
var File = function (path){
var main = process.mainModule.filename;
var cwd = main.substring (0, main.lastIndexOf (SLASH));
var relative = PATH.relative (process.cwd (), cwd);
this._relative = relative;
//...omitted
updateFileProperties (this, path);
};
This piece of code resolves the relative path problem.
_usablePath contains the real path. The "." path will be the directory where the main file is located. No matter how is called, it will point to the expected directory.
You can test the functions printing what the new File (<path>).getPath () returns.
I would avoid complexity and go for absolute paths only. Most users do __dirname + '../relativepath' anyways. ( at least that's what I do ).

Categories