node.jslinuxbashshellexec

Why do nodejs exec/spawn not show any output for bash 'history' command?


I am running this on Ubuntu and have tried many variations of exec/spawn functions (and their sync counterparts) and none of them can show me an output for bash 'history' command. One scenario is following:

const { spawnSync} = require('child_process');
const child = spawnSync('history', { shell: "/bin/bash" });

console.log('error: ', child.error);
console.log('stdout: ', child.stdout.toString());
console.log('stderr: ', child.stderr);

It does not show any errors and output is empty. I think this question has more to do with 'specialty' or category of the history command than nodejs's function since they work fine for normal commands like ls, pwd, whoami, etc work fine. I have looked at my .bash_history file and its filled with history so that's not the issue.

Another problem that might be similar is ll command also fails even though I have set bash as shell. But for ll, it does return an error:

/bin/bash: ll: command not found

Just to be sure, I tried running ll command in bash it worked just fine. What am I missing here?

edit: I have done some more testing it seems more like a bash thing than a node thing. When I simply write the history command, bash prints results but when I do bash -c history, it does not show any output but also no error.

image of bash output.


Solution

  • The bash(1) man page (see excerpt below) states, that the command history is available when the -o history option is set. The option is on by default, if bash is in interactive mode. The command line option -i turns interactive mode on. The commands are read from the file designated by the HISTFILE environment variable.

    When bash is called with a command via option -c, neither the shell option history is on, nor is the environment variable HISTFILE set:

    $ bash -c 'set -o | grep history; shopt -p | grep histappend; echo "HISTFILE: ${HISTFILE-!!not set!!}"'
    history         off
    histappend      off
    HISTFILE: !!not set!!
    

    When bash is called with the option -i, both the shell option history as well as the environment variable HISTFILE are set:

    $ bash -i -c 'set -o | grep history; shopt -p | grep histappend; echo "HISTFILE: ${HISTFILE-!!not set!!}"'
    history         on
    histappend      off
    HISTFILE: /home/user/.bash_history
    

    Since interactive mode tries to open a terminal - issuing error messages, if that fails - it is better to handle the history setup explicitely:

    $bash -c 'unset HISTFILESIZE; HISTFILE="${HOME}/.bash_history"; set -o history; history 5'
      496  ls -l
      497  ls -l
      498  ls -l
      499  ls -l
      500  ls -l
    

    Note, that the command in interactive mode does not display the history as requested:

    $ bash -i -c 'set -o | grep history; echo "HISTFILE: ${HISTFILE-!!not set!!}"; history 5'
    history         on
    HISTFILE: /home/user/.bash_history
    

    It is still necessary to turn the shell option history on explicitely:

    $ bash -i -c 'set -o history; history 5'
      496  ls -l
      497  ls -l
      498  ls -l
      499  ls -l
      500  ls -l
    

    Excerpt from bash man page:

    OPTIONS

    -c
    If the -c option is present, then commands are read from the first non-option argument command_string.

    -i
    If the -i option is present, the shell is interactive.

    [-+]O [ shopt_option ]
    shopt_option is one of the shell options accepted by the shopt builtin [...]. If shopt_option is present, -O sets the value of that option; +O unsets it. If shopt_option is not supplied, the names and values of the shell options accepted by shopt are printed on the standard output. If the invocation option is +O, the output is displayed in a format that may be reused as input.

    HISTORY

    When the -o history option to the set builtin is enabled, the shell provides access to the command history, the list of commands previously typed.

    On startup, the history is initialized from the file named by the variable HISTFILE (default ~/.bash_history). The file named by the value of HISTFILE is truncated, if necessary, to contain no more than the number of lines specified by the value of HISTFILESIZE. If HISTFILESIZE is unset, or set to null, a non-numeric value, or a numeric value less than zero, the history file is not truncated.

    SHELL BUILTIN COMMANDS

    set -o option-name
    The option-name can be one of the following:

    history
    Enable command history, as described above under HISTORY. This option is on by default in interactive shells.

    If -o is supplied with no option-name, the values of the current options are printed. If +o is supplied with no option-name, a series of set commands to recreate the current option settings is displayed on the standard output.