Ich benutze die md5-Grunzaufgabe , um MD5-Dateinamen zu generieren. Jetzt möchte ich die Quellen in der HTML-Datei mit dem neuen Dateinamen im Rückruf der Aufgabe umbenennen. Ich frage mich, was der einfachste Weg ist, dies zu tun.
Ich benutze die md5-Grunzaufgabe , um MD5-Dateinamen zu generieren. Jetzt möchte ich die Quellen in der HTML-Datei mit dem neuen Dateinamen im Rückruf der Aufgabe umbenennen. Ich frage mich, was der einfachste Weg ist, dies zu tun.
Antworten:
Sie könnten einen einfachen regulären Ausdruck verwenden:
var result = fileAsString.replace(/string to be replaced/g, 'replacement');
So...
var fs = require('fs')
fs.readFile(someFile, 'utf8', function (err,data) {
if (err) {
return console.log(err);
}
var result = data.replace(/string to be replaced/g, 'replacement');
fs.writeFile(someFile, result, 'utf8', function (err) {
if (err) return console.log(err);
});
});
Da das Ersetzen bei mir nicht funktioniert hat, habe ich ein einfaches npm-Paket zum Ersetzen in der Datei erstellt , um Text in einer oder mehreren Dateien schnell zu ersetzen. Es basiert teilweise auf der Antwort von @ asgoth.
Bearbeiten (3. Oktober 2016) : Das Paket unterstützt jetzt Versprechen und Globs, und die Gebrauchsanweisungen wurden aktualisiert, um dies widerzuspiegeln.
Bearbeiten (16. März 2018) : Das Paket hat jetzt über 100.000 monatliche Downloads gesammelt und wurde um zusätzliche Funktionen sowie ein CLI-Tool erweitert.
Installieren:
npm install replace-in-file
Modul erforderlich
const replace = require('replace-in-file');
Geben Sie Ersatzoptionen an
const options = {
//Single file
files: 'path/to/file',
//Multiple files
files: [
'path/to/file',
'path/to/other/file',
],
//Glob(s)
files: [
'path/to/files/*.html',
'another/**/*.path',
],
//Replacement to make (string or regex)
from: /Find me/g,
to: 'Replacement',
};
Asynchroner Ersatz durch Versprechen:
replace(options)
.then(changedFiles => {
console.log('Modified files:', changedFiles.join(', '));
})
.catch(error => {
console.error('Error occurred:', error);
});
Asynchroner Ersatz durch Rückruf:
replace(options, (error, changedFiles) => {
if (error) {
return console.error('Error occurred:', error);
}
console.log('Modified files:', changedFiles.join(', '));
});
Synchroner Austausch:
try {
let changedFiles = replace.sync(options);
console.log('Modified files:', changedFiles.join(', '));
}
catch (error) {
console.error('Error occurred:', error);
}
Vielleicht würde das "Ersetzen" -Modul ( www.npmjs.org/package/replace ) auch für Sie funktionieren. Sie müssten die Datei nicht lesen und dann schreiben.
Aus der Dokumentation übernommen:
// install:
npm install replace
// require:
var replace = require("replace");
// use:
replace({
regex: "string to be replaced",
replacement: "replacement string",
paths: ['path/to/your/file'],
recursive: true,
silent: true,
});
readFile()
und writeFile()
akzeptieren genau die akzeptierte Antwort.
Sie können auch die 'sed'-Funktion verwenden, die Teil von ShellJS ist ...
$ npm install [-g] shelljs
require('shelljs/global');
sed('-i', 'search_pattern', 'replace_pattern', file);
Besuchen Sie ShellJs.org für weitere Beispiele.
shx
Mit ShellJs.org können Sie npm-Skripte ausführen. github.com/shelljs/shx
Sie können die Datei beim Lesen mithilfe von Streams verarbeiten. Es ist wie bei der Verwendung von Puffern, jedoch mit einer bequemeren API.
var fs = require('fs');
function searchReplaceFile(regexpFind, replace, cssFileName) {
var file = fs.createReadStream(cssFileName, 'utf8');
var newCss = '';
file.on('data', function (chunk) {
newCss += chunk.toString().replace(regexpFind, replace);
});
file.on('end', function () {
fs.writeFile(cssFileName, newCss, function(err) {
if (err) {
return console.log(err);
} else {
console.log('Updated!');
}
});
});
searchReplaceFile(/foo/g, 'bar', 'file.txt');
bufferSize
Sie dieses Problem vermeiden könnten, indem Sie eine längere Zeichenfolge als die Zeichenfolge festlegen, die Sie ersetzen, den letzten Block speichern und mit der aktuellen verketten.
Beim Ersetzen eines kleinen Platzhalters durch eine große Codefolge sind Probleme aufgetreten.
Ich habe getan:
var replaced = original.replace('PLACEHOLDER', largeStringVar);
Ich fand heraus, dass das Problem die hier beschriebenen speziellen Ersatzmuster von JavaScript waren . Da der Code, den ich als Ersetzungszeichenfolge verwendete, einige $
enthielt, hat er die Ausgabe durcheinander gebracht.
Meine Lösung bestand darin, die Option zum Ersetzen von Funktionen zu verwenden, die KEINEN speziellen Austausch durchführt:
var replaced = original.replace('PLACEHOLDER', function() {
return largeStringVar;
});
ES2017 / 8 für Knoten 7.6+ mit einer temporären Schreibdatei für den atomaren Ersatz.
const Promise = require('bluebird')
const fs = Promise.promisifyAll(require('fs'))
async function replaceRegexInFile(file, search, replace){
let contents = await fs.readFileAsync(file, 'utf8')
let replaced_contents = contents.replace(search, replace)
let tmpfile = `${file}.jstmpreplace`
await fs.writeFileAsync(tmpfile, replaced_contents, 'utf8')
await fs.renameAsync(tmpfile, file)
return true
}
Beachten Sie, nur für kleinere Dateien, da diese in den Speicher eingelesen werden.
Unter Linux oder Mac ist keep einfach und verwendet sed nur mit der Shell. Keine externen Bibliotheken erforderlich. Der folgende Code funktioniert unter Linux.
const shell = require('child_process').execSync
shell(`sed -i "s!oldString!newString!g" ./yourFile.js`)
Die sed-Syntax ist auf dem Mac etwas anders. Ich kann es momentan nicht testen, aber ich glaube, Sie müssen nur eine leere Zeichenfolge nach dem "-i" hinzufügen:
const shell = require('child_process').execSync
shell(`sed -i "" "s!oldString!newString!g" ./yourFile.js`)
Das "g" nach dem Finale "!" Lässt sed alle Instanzen in einer Zeile ersetzen. Entfernen Sie es, und nur das erste Vorkommen pro Zeile wird ersetzt.
Wenn Sie die Antwort von @ Sanbor erweitern, können Sie dies am effizientesten tun, indem Sie die Originaldatei als Stream lesen und dann jeden Block in eine neue Datei streamen und zuletzt die Originaldatei durch die neue Datei ersetzen.
async function findAndReplaceFile(regexFindPattern, replaceValue, originalFile) {
const updatedFile = `${originalFile}.updated`;
return new Promise((resolve, reject) => {
const readStream = fs.createReadStream(originalFile, { encoding: 'utf8', autoClose: true });
const writeStream = fs.createWriteStream(updatedFile, { encoding: 'utf8', autoClose: true });
// For each chunk, do the find & replace, and write it to the new file stream
readStream.on('data', (chunk) => {
chunk = chunk.toString().replace(regexFindPattern, replaceValue);
writeStream.write(chunk);
});
// Once we've finished reading the original file...
readStream.on('end', () => {
writeStream.end(); // emits 'finish' event, executes below statement
});
// Replace the original file with the updated file
writeStream.on('finish', async () => {
try {
await _renameFile(originalFile, updatedFile);
resolve();
} catch (error) {
reject(`Error: Error renaming ${originalFile} to ${updatedFile} => ${error.message}`);
}
});
readStream.on('error', (error) => reject(`Error: Error reading ${originalFile} => ${error.message}`));
writeStream.on('error', (error) => reject(`Error: Error writing to ${updatedFile} => ${error.message}`));
});
}
async function _renameFile(oldPath, newPath) {
return new Promise((resolve, reject) => {
fs.rename(oldPath, newPath, (error) => {
if (error) {
reject(error);
} else {
resolve();
}
});
});
}
// Testing it...
(async () => {
try {
await findAndReplaceFile(/"some regex"/g, "someReplaceValue", "someFilePath");
} catch(error) {
console.log(error);
}
})()
Ich würde stattdessen einen Duplex-Stream verwenden. wie hier dokumentiert nodejs doc Duplex-Streams
Ein Transformationsstrom ist ein Duplexstrom, bei dem die Ausgabe auf irgendeine Weise aus der Eingabe berechnet wird.
<p>Please click in the following {{link}} to verify the account</p>
function renderHTML(templatePath: string, object) {
const template = fileSystem.readFileSync(path.join(Application.staticDirectory, templatePath + '.html'), 'utf8');
return template.match(/\{{(.*?)\}}/ig).reduce((acc, binding) => {
const property = binding.substring(2, binding.length - 2);
return `${acc}${template.replace(/\{{(.*?)\}}/, object[property])}`;
}, '');
}
renderHTML(templateName, { link: 'SomeLink' })
Natürlich können Sie die Lesevorlagenfunktion verbessern, um sie als Stream zu lesen, und die Bytes zeilenweise zusammensetzen, um sie effizienter zu gestalten