Search code examples
node.jsbashmacosterminalhistory

How to manipulate terminal's history from node js


I am building a cli tool in node.js (for mac, bash), which lets you interactively construct a command and run it. for example, you can run "node myTool", and then it asks you some questions and at the end runs a command. The thing is, I want the user to be able to run the final command again by pressing the "up" key. currently, when the user presses "up" he gets: "node myTool", but I want instead to give him the actual command that was running. I was able to do it in bash, by manipulating the history like this:

echo 'final command' >> $HISTFILE &&  history -r $HISTFILE

this actually works, and when I press "up" I see "final command", but It doesn't work from node.js by using execSync:

execSync(`echo 'final command' >> $HISTFILE &&  history -r $HISTFILE`);

It successfully appends "final command" to the history, but for obvious reasons, "history -r" doesn't refresh the terminal. is there a way to actually refresh the terminal's history for the user to achieve the expected result??


Solution

  • You're running history -r in a new shell process, not the user's original interactive shell. Each shell process keeps its own history, and reloading the child shell's history has no effect on the parent shell's history.

    In general, there's no direct way to modify the state of the parent shell. Programs that are intended to do this work by outputting shell commands. The user makes use of them by getting the output with a command substitution, then using eval to execute it (an example of this is tset).

    So you could change your node.js script to

    fs.writeFileSync(process.env.HISTFILE, "final command\n", "a");
    process.stdout.write(`history -r "$HISTFILE"`);
    

    Then you could use it as

    eval "$(node scriptname.js)"
    

    But in this case the script can't do any "normal" output to stdout, since anything it prints will be executed by eval rather than displayed on the terminal.