Я использую задачу md5 grunt для генерации имен файлов MD5. Теперь я хочу переименовать источники в файле HTML с новым именем файла в обратном вызове задачи. Интересно, какой самый простой способ сделать это.
Заменить строку в файле с помощью nodejs
Ответ 1
Вы можете использовать простое регулярное выражение:
var result = fileAsString.replace(/string to be replaced/g, 'replacement');
Итак...
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);
});
});
Ответ 2
Поскольку замена не работала для меня, я создал простую замену пакета в файле npm, чтобы быстро заменить текст в одном или нескольких файлах. Это частично основано на ответе @asgoth.
Изменить (3 октября 2016 г.): теперь пакет поддерживает обещания и глобусы, и инструкции по использованию были обновлены, чтобы отразить это.
Редактирование (16 марта 2018 г.). Пакет в настоящее время содержит более 100 000 загрузок в месяц и был расширен за счет дополнительных функций, а также инструмента CLI.
Установка:
npm install replace-in-file
Требуется модуль
const replace = require('replace-in-file');
Укажите варианты замены
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',
};
Асинхронная замена с обещаниями:
replace(options)
.then(changedFiles => {
console.log('Modified files:', changedFiles.join(', '));
})
.catch(error => {
console.error('Error occurred:', error);
});
Асинхронная замена с обратным вызовом:
replace(options, (error, changedFiles) => {
if (error) {
return console.error('Error occurred:', error);
}
console.log('Modified files:', changedFiles.join(', '));
});
Синхронная замена:
try {
let changedFiles = replace.sync(options);
console.log('Modified files:', changedFiles.join(', '));
}
catch (error) {
console.error('Error occurred:', error);
}
Ответ 3
Возможно, модуль "replace" (www.npmjs.org/package/replace) также сработает для вас. Это не требует, чтобы вы читали, а затем записывали файл.
Адаптировано из документации:
// 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,
});
Ответ 4
Вы также можете использовать функцию "sed", которая является частью ShellJS...
$ npm install [-g] shelljs
require('shelljs/global');
sed('-i', 'search_pattern', 'replace_pattern', file);
Посетите ShellJs.org для получения дополнительных примеров.
Ответ 5
Вы можете обрабатывать файл во время чтения с использованием потоков. Это похоже на использование буферов, но с более удобным 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');
Ответ 6
Я столкнулся с проблемами при замене небольшого заполнителя большой строкой кода.
Я делал:
var replaced = original.replace('PLACEHOLDER', largeStringVar);
Я понял, что проблема связана с шаблонами замены JavaScript, описанными здесь. Поскольку код, который я использовал в качестве заменяющей строки, содержал в нем $
, он помешал вывод.
Мое решение состояло в том, чтобы использовать опцию замены функции, которая НЕ выполняет никакой специальной замены:
var replaced = original.replace('PLACEHOLDER', function() {
return largeStringVar;
});
Ответ 7
ES2017/8 для Node 7.6+ с временным файлом записи для атомной замены.
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
}
Обратите внимание, что только для маленьких файлов, поскольку они будут считаны в памяти.
Ответ 8
На Linux или Mac хранить просто и использовать sed вместе с оболочкой. Внешние библиотеки не требуются. Следующий код работает на Linux.
const shell = require('child_process').execSync
shell('sed -i "s!oldString!newString!g" ./yourFile.js')
Синтаксис sed немного отличается на Mac. Я не могу проверить это прямо сейчас, но я считаю, что вам просто нужно добавить пустую строку после "-i":
const shell = require('child_process').execSync
shell('sed -i "" "s!oldString!newString!g" ./yourFile.js')
"Г" после финала "!" заставляет sed заменить все экземпляры в строке. Удалите его, и будет заменено только первое вхождение в строке.
Ответ 9
Вместо этого я бы использовал дуплексный поток. как описано здесь nodejs doc дуплексные потоки
A Transform stream - это дуплексный поток, в котором вывод вычисляется в каким-то образом от ввода.
Ответ 10
Если развернуть ответ @Sanbor, наиболее эффективный способ сделать это - прочитать исходный файл в виде потока, а затем также направить каждый чанк в новый файл и, наконец, заменить исходный файл новым файлом.
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);
}
})()