I have some commands in my Bash history that I want to remove.

I can find them with history | grep "command with password" then remove them with history -d <line-number>

However, when I try to delete them in bulk by piping them to xargs like this I get an error:

history | grep "command with password" | awk '{print $1}' | sort -r | xargs history -d
xargs: history: No such file or directory

I thought that xargs would go through the list of line numbers and send it one by one to the history -d command.

What is causing this error?

NB: I know there are other ways to delete the history, the question is purely to improve my understanding of how xargs works and what I am doing wrong that is causing the error.

  • 1
    Similar, if not duplicate: unix.stackexchange.com/q/57924/117549 – Jeff Schaller Jul 19 '20 at 20:05
  • 2
    Yes, agreed. Similar in that it gives solutions to do what the OP wanted, but the question here is why rather than how so not a duplicate – roaima Jul 19 '20 at 20:21
  • 3
    My (untested) thought was that xargs can't call a shell built in. – Jeff Schaller Jul 19 '20 at 20:24

The error is raised because xargs cannot find a history command. It is a shell builtin, as you can confirm with type history, thus not visible to xargs. Try

echo 1 | xargs history -d; echo $?

The return value is 127. In man xargs, EXIT STATUS section:

0 if it succeeds
123 if any invocation of the command exited with status 1-125
124 if the command exited with status 255
125 if the command is killed by a signal
126 if the command cannot be run
127 if the command is not found
1 if some other error occurred.

Expanding on ilkkachu's comment, in principle you could invoke Bash and call history from the spawned shell.

echo 1 | xargs -I {} bash -c 'echo $HISTFILE; history' bash {}

The above command does not raise an error, after all the builtin history is available to the Bash shell. However, from that same command one also finds that HISTFILE is unset and history outputs nothing: The history is not enabled on a non-interactive shell. Again, you could activate it with the set builtin, export HISTFILE and HISTFILESIZE... But we don't want a headache. Editing .bash_history directly is the straightforward way to go.

  • 1
    Very interesting. I never knew that xargs couldn't call shell builtins! – opticyclic Jul 19 '20 at 20:33
  • 6
    @opticyclic, it could, if it used a shell to start the programs, instead of doing it itself. And you can do it anyway with something like xargs sh -c 'some shell code' sh. It just wouldn't help because that copy of the shell might have a different concept of the history than the main shell where you started the whole pipeline – ilkkachu Jul 19 '20 at 20:49
  • 5
    @opticyclic It is a bit surprising at the first moment, but if you think about it, how would xargs decide on which shell to look for builtins? bash, ksh, dash, zsh? It seems reasonable that it doesn't go as far as that. – Quasímodo Jul 19 '20 at 20:57
  • Both comments totally make sense. Very useful in helping my understanding! – opticyclic Jul 19 '20 at 21:08
  • @opticyclic: xargs isn't a shell-builtin itself so it runs in a separate process. If you wanted to do something like it, you might write a shell loop that used while read, or bash array variables and iterate over them, or some other pure shell construct that can run commands inside the current shell. – Peter Cordes Jul 20 '20 at 4:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.