Ich verwende die md5 grunt task , um MD5-Dateinamen zu generieren. Nun möchte ich die Quellen in der HTML-Datei mit dem neuen Dateinamen im Callback der Task umbenennen. Ich frage mich, was der einfachste Weg ist, dies zu tun.
Sie könnten einfache Regex 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 replace für mich nicht funktioniert hat, habe ich ein einfaches npm-Paket erstellt - replace-in-file , um Text in einer oder mehreren Dateien schnell zu ersetzen. Es basiert teilweise auf @ asgoths Antwort.
Edit (3. Oktober 2016): Das Paket unterstützt jetzt Versprechen und Globs. Die Gebrauchsanweisungen wurden entsprechend aktualisiert.
Edit (16. März 2018): Das Paket umfasst inzwischen mehr als 100.000 Downloads pro Monat 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');
Legen Sie Ersatzoptionen fest
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 mit Versprechen:
replace(options)
.then(changedFiles => {
console.log('Modified files:', changedFiles.join(', '));
})
.catch(error => {
console.error('Error occurred:', error);
});
Asynchroner Ersatz mit Callback:
replace(options, (error, changedFiles) => {
if (error) {
return console.error('Error occurred:', error);
}
console.log('Modified files:', changedFiles.join(', '));
});
Synchrone Ersetzung:
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üssen die Datei nicht lesen und dann schreiben.
Angepasst aus der Dokumentation:
// 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,
});
Sie können auch die 'sed'-Funktion verwenden, die zu ShellJS gehört ...
$ npm install [-g] shelljs
require('shelljs/global');
sed('-i', 'search_pattern', 'replace_pattern', file);
Besuchen Sie ShellJs.org für weitere Beispiele.
Sie können die Datei während des Lesens mithilfe von Streams verarbeiten. Es ist wie bei der Verwendung von Puffern, aber 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');
ES2017/8 für Knoten 7.6+ mit einer temporären Schreibdatei für atomaren Austausch.
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
}
Hinweis: Nur für kleine Dateien, da diese in den Speicher gelesen werden.
Ich bin auf Probleme gestoßen, als ich einen kleinen Platzhalter durch eine große Zeichenfolge ersetzt.
Ich habe getan:
var replaced = original.replace('PLACEHOLDER', largeStringVar);
Ich fand heraus, dass es sich bei JavaScript um spezielle Ersatzmuster handelte, die hier beschrieben werden. Da der Code, den ich als ersetzende Zeichenfolge verwendete, einen $
enthielt, war die Ausgabe durcheinander geraten.
Meine Lösung bestand darin, die Funktion zum Ersetzen von Funktionen zu verwenden, die KEINEN speziellen Austausch vornimmt:
var replaced = original.replace('PLACEHOLDER', function() {
return largeStringVar;
});
Unter Linux oder Mac ist keep einfach und wird nur mit der Shell verwendet. 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" einfü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.
Um die Antwort von @ Sanbor zu erweitern, ist es am effizientesten, die Originaldatei als Stream zu lesen und dann jeden Chunk in eine neue Datei zu streamen und die Originaldatei zuletzt durch die neue Datei zu 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 in .__ berechnet wird. etwas von der Eingabe entfernt.