ファイル内の文字列をnodejsで置き換える


166

MD5ファイル名を生成するには、md5 gruntタスクを使用します。次に、HTMLファイルのソースの名前を、タスクのコールバックで新しいファイル名に変更します。これを行う最も簡単な方法は何でしょうか。


1
ファイルの名前を変更し、それらのファイルの参照も検索/置換する、名前変更とファイル内置換の組み合わせがあればよいのですが。
Brain2000

回答:


302

あなたは単純な正規表現を使うことができます:

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);
  });
});

もちろん、ファイルを読んでテキストを置き換えてから、もう一度ファイルを書く必要がありますか、それとももっと簡単な方法がありますか?
AndreasKöberle2013年

多分これを達成するためのノードモジュールがありますが、私はそれを知りません。完全な例を追加しました。
2013年

4
@Zax:ありがとう、私はこの「バグ」がこれほど長く存続できることに驚いています;)
13:20にasgoth

1
ベトナム、中華料理...:申し訳ありませんが私のようなUTF-8のサポート多くの言語を知っているとして
vuhung3990

文字列がテキスト内で複数回出現する場合、最初に見つかった文字列のみが置き換えられます。
eltongonc

79

replaceが機能しなかったため、1つ以上のファイルのテキストをすばやく置き換えるための単純なnpmパッケージreplace-in-fileを作成しました。@asgothの回答に部分的に基づいています。

編集(2016年10月3日):パッケージはpromiseとglobをサポートするようになり、使用法の説明はこれを反映するように更新されました。

編集(2018年3月16日):パッケージは現在、月間ダウンロード数が10万回を超え、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',
};

promiseによる非同期置換:

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
ターンキーモジュールを使用するのは非常に簡単です。非常に大きなフォルダーでasync / awaitとglobを使用してそれを使用したところ、非常に高速でした
Matt Fletcher

ノードjsの文字列制限が256 Mbであるところを読んだので、256 Mbを超えるファイルサイズで作業できますか
Alien128

私はそれができると信じていますが、より大きなファイルのストリーミング置換を実装する作業も進行中です。
Adam Reis

1
いいですね、このSOの回答を読む前に、このパッケージ(CLIツール用)を見つけて使用しました。大好きです
ラッセルチザム

38

おそらく「置換」モジュール(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,
});

パスのファイル拡張子でフィルタリングする方法を知っていますか?パスのようなもの:['path / to / your / file / *。js']->機能しません
Kalamarico

node-globを使用して、globパターンをパスの配列に展開し、それらを反復できます。
RobW

3
これはいいですが、放棄されました。すぐに使用できるソリューションが必要な場合は、維持パッケージについては、stackoverflow.com / a / 31040890/1825390を参照してください。
xavdid

1
node-replaceと呼ばれる保守バージョンもあります。ただし、コードベースを見ると、これもファイル内置換テキストも実際にはファイル内のテキストを置換せず、受け入れられた回答readFile()writeFile()同じように使用します。
c1moore

26

ShellJSの一部である「sed」関数を使用することもできます...

 $ npm install [-g] shelljs


 require('shelljs/global');
 sed('-i', 'search_pattern', 'replace_pattern', file);

その他の例については、ShellJs.orgにアクセスしてください。


これは最もクリーンな解決策のようです:)
Yerken

1
shxnpmスクリプトから実行できます。ShellJs.orgが推奨します。github.com/shelljs/shx
Joshua Robinson

私もこれが好きです。npm-moduleよりもワンライナーの方が優れていますが、コードの
数行

サードパーティの依存関係をインポートすることは、最もクリーンなソリューションではありません。
four43

これはマルチラインを行いません。
曲がりくねった

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');

3
しかし...チャンクがregexpFind文字列を分割するとどうなりますか?その意図は失敗しませんか?
Jaakko Karhu、2017年

それは非常に良い点です。bufferSize置き換える文字列よりも長い文字列を設定し、最後のチャンクを保存して現在のチャンクと連結することで、その問題を回避できるのではないかと思います。
2018年

1
おそらく、ファイルが使用可能なメモリよりも大きい可能性があるため、大きな変数を作成するのではなく、変更されたファイルをファイルシステムに直接書き込むことで、このスニペットも改善する必要があります。
2018年

1

小さなプレースホルダーを大きなコード文字列に置き換えると、問題が発生しました。

やっていた:

var replaced = original.replace('PLACEHOLDER', largeStringVar);

ここで説明されているように、問題はJavaScriptの特別な置換パターンであることがわかりました。置換文字列として使用していたコードにコードが含まれていた$ため、出力がめちゃくちゃになっていた。

私の解決策は、特別な置換を行わない関数置換オプションを使用することでした:

var replaced = original.replace('PLACEHOLDER', function() {
    return largeStringVar;
});

1

ノード7.6以降用のES2017 / 8、アトミック置換用の一時書き込みファイル。

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
}

メモリに読み込まれるため、小さめのファイルのみに注意してください。


必要はありません。nativeutil.promisifyをbluebird使用Promiseしてください
Francisco Mateo 2017

1
@FranciscoMateo真ですが、1つまたは2つを超える関数promisifyAllは依然として非常に便利です。
Matt

1

LinuxまたはMacでは、keepは単純で、シェルでsedを使用するだけです。外部ライブラリは必要ありません。次のコードはLinuxで動作します。

const shell = require('child_process').execSync
shell(`sed -i "s!oldString!newString!g" ./yourFile.js`)

Macのsed構文は少し異なります。今はテストできませんが、 "-i"の後に空の文字列を追加するだけでよいと思います。

const shell = require('child_process').execSync
shell(`sed -i "" "s!oldString!newString!g" ./yourFile.js`)

最後の「!」の後の「g」行上のすべてのインスタンスをsedで置換します。それを削除すると、1行ごとに最初に出現するものだけが置き換えられます。


1

@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);
  }
})()

0

代わりに二重ストリームを使用します。ここに記載されているようにnodejs doc duplexストリーム

変換ストリームは、出力が入力から何らかの方法で計算される二重ストリームです。


0

<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' })

確かに、読み取りテンプレート関数を改善してストリームとして読み取り、行ごとにバイトを構成してより効率的にすることができます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.