History command works in a terminal, but doesn't when written as a bash script
Looking at your history only makes sense in an interactive shell. Make that command a function instead of a standalone script. In your ~/.bashrc, put
popular_history() {
history | sort -k2 | uniq -c --skip-fields=1 | sort -r -g | head
}
Why history command is default disabled for bash scripts
In General
- For an overwhelming majority of scripts, reading dotfiles for history would be pure startup-time overhead, with that content never used at runtime. This would make all shell scripts take longer to start up, with no compensating benefit.
- If noninteractive scripts did support history expansion, this would make their behavior dependent on prior interactive actions, thus harder to predict and different between invocations. This is, in particular, a compelling reason not to have
set -H
on by default in noninteractive script invocation. History is specified in the User Portability Utilities section of the POSIX sh standard, which is focused around support for interactive scripting. Quoting from the standard, with emphasis added:
When the sh utility is being used interactively, it shall maintain a list of commands previously entered from the terminal in the file named by the
HISTFILE
environment variable. The type, size, and internal format of this file are unspecified. Multiplesh
processes can share access to the file for a user, if file access permissions allow this; see the description of theHISTFILE
environment variable.
Finally, some context:
history -c
is needed in an interactive script because that interactive shell will be writing a new copy of HISTFILE
when it exits; if you didn't use a special command, it would potentially write back content that you instead want to clear. In a noninteractive shell, it won't be writing any history anyhow, so you might as well simply delete or truncate the history file.
history -c doesn't work when called inside a script?
Don't try to clear history -- even though that's the most obvious way that passing a password on the command line exposes it, that action is giving a false sense of security: Passwords given on the command line are trivial to capture via other processes running on the same machine (even under untrusted accounts!) even without history involved at all.
Moreover, as you note, a shell can only modify its own in-memory state, not the in-memory state of the separate process that started it (which may not even be the same shell, or a shell at all!).
Instead, modify your Python program's calling convention to read the password direct from the TTY (as SSH does), or from the environment. For the latter, usage might look like:
# assumes you renamed aesencrypt.py to aesencrypt, ran chmod +x, and gave a valid shebang
password="somePassword" aesencrypt outFile
...and you would want to modify your Python script to do something like:
#!/usr/bin/env python
import os, sys
filename = sys.argv[1]
password = os.environ['password']
# ...put the rest of your logic here.
When using the terminal history, can I force confirmation when executing dangerous commands from my history?
Use Functions as Command Wrappers
The shell doesn't directly support this feature, in part because a command is stored in history before it is executed. However, you can largely fake it by wrapping your "dangerous" commands in functions that take precedence over the real commands. For example:
rm () {
local regex='[Yy]'
if history | sed '$d' | pcregrep -q "^\s+\d+\s+${FUNCNAME} $*"; then
read -p 'Are you sure? '
[[ "$REPLY" =~ $regex ]] || return 1
fi
command "$FUNCNAME" "$@"
}
The secret sauce is the sed command that strips out the current command that's just been stored in history. Without that, grep would always find the command in the history.
Caveats
- Note the requirement for a grep compiled with PCRE support. You will need to adjust the regular expression if you don't have pcregrep or egrep compiled with PCRE support.
- You might also have to adjust quoting or use eval if word splitting doesn't happen the way you think it should, but it worked fine for me in casual testing. Your mileage may vary.
Bash one liner works but script does not
The line echo "$(( i++))) $item"
has one closing parentheses in excess.
echo "$(( i++ )) $item"
If you try to use history
in a script, it will fail.
Try running this script:
#!/bin/bash
history
It will print nothing because there is no history stored (for this instance of the shell). To read history you need to provide the file with the stored history, call the builtin history
to read -r
and finally you can list the history from memory:
#!/bin/bash
HISTFILE="$HOME/.bash_history"
history -r
history
That doesn't mean that commands will be written to the file, that's controlled by a different option.
#!/bin/bash
read -p 'Search history for? ' string
i=0
OLDIFS=$IFS
IFS=$'\n'
HISTFILE="$HOME/.bash_history"
history -r
IFS=$'\n' read -d '' -a arr <<<"$(history | grep "$string" | cut -c8-)"
for item in ${arr[@]}
do echo "$(( i++ )) $item"
done
Diffrence between bash script.sh and ./script.sh
History is disabled by BASH in non-interactive shells by-default. If you want to enable it however, you can do so like this:
#!/bin/bash
echo $HISTFILE # will be empty in non-iteractive shell
HISTFILE=~/.bash_history # set it again
set -o history
# the command will work now
history
The reason this is done is to avoid cluttering the history by any commands being run by any shell scripts.
Adding hashbang (meaning the file is to be interpreted as a script by the program specified in your hashbang) to your script when being run via ./env.sh
invokes your script using the binary /bin/bash
i.e. run via bash
, thus again printing no history.
Related Topics
G++ Searches /Lib/../Lib/, Then /Lib/
Splitting Gzipped Logfiles Without Storing the Ungzipped Splits on Disk
Jboss as 7.1.1 Ejb 3:Ejb Pool Error
Backing Up (And Restoring) a Plone Instance
Copy Files from Windows to Windows Subsystem for Linux (Wsl)
How to Capture Raw Hid Input on Linux
How to Get Perf to Find Symbols in My Program
Using Bash Script to Find Line Number of String in File
How to Change(Hide) the Nginx Server Signature
Bash Output Stream Write to a File
Identifying Which Linux System Library Contains a Function
Suppress Notice of Forked Command Being Killed
Emulating Slurm on Ubuntu 16.04
Magic Numbers of the Linux Reboot() System Call