Сохранение текущего каталога в bash истории
Я хочу сохранить текущий каталог, где каждая команда была выпущена вместе с командой в истории. Чтобы не испортить вещи, я думал о добавлении текущего каталога в качестве комментария в конце строки. Пример может помочь:
$ cd /usr/local/wherever
$ grep timmy accounts.txt
Я хотел бы bash сохранить последнюю команду как:
grep timmy accounts.txt # /usr/local/wherever
Идея состоит в том, что таким образом я мог сразу увидеть, где я дал команду.
Ответы
Ответ 1
Однолинейная версия
Вот однострочная версия. Это оригинал. Я также опубликовал краткую версию функции и длинную версию функции с несколькими добавленными функциями. Мне нравятся версии функций, потому что они не будут поглощать другие переменные в вашей среде, и они намного более читабельны, чем однострочные. В этом посте есть некоторая информация о том, как все они работают, которые могут не дублироваться в других.
Добавьте следующее в ваш файл ~/.bashrc
:
export PROMPT_COMMAND='hpwd=$(history 1); hpwd="${hpwd# *[0-9]* }"; if [[ ${hpwd%% *} == "cd" ]]; then cwd=$OLDPWD; else cwd=$PWD; fi; hpwd="${hpwd% ### *} ### $cwd"; history -s "$hpwd"'
Это делает запись в истории, которая выглядит следующим образом:
rm subdir/file ### /some/dir
Я использую ###
в качестве разделителя комментариев, чтобы отделить его от комментариев, которые может вводить пользователь, и уменьшить вероятность коллизий при удалении старых комментариев к путям, которые в противном случае накапливались бы при нажатии клавиши ввода в пустой командной строке. К сожалению, побочным эффектом является то, что команда типа echo " ### "
искажается, хотя это должно быть довольно редко.
Некоторые люди считают неприятным тот факт, что я повторно использую одно и то же имя переменной. Обычно я бы не стал, но здесь я пытаюсь свести к минимуму след. Это легко изменилось в любом случае.
Это вслепую предполагает, что вы не используете HISTTIMEFORMAT
или модифицируете историю каким-либо другим способом. Было бы легко добавить команду date
к комментарию вместо функции HISTTIMEFORMAT
. Однако, если вам нужно использовать его по какой-то причине, он все еще работает в подоболочке, так как автоматически отключается:
$ htf="%Y-%m-%d %R " # save it for re-use
$ (HISTTIMEFORMAT=$htf; history 20)|grep 11:25
Есть пара очень маленьких проблем с этим. Один из них - если вы используете команду history
, например, так:
$ history 3
echo "hello world" ### /home/dennis
ls -l /tmp/file ### /home/dennis
history 3
Результат не будет отображать комментарий к самой команде history
, даже если вы увидите его, если вы нажмете стрелку вверх или введете другую команду history
.
Другое заключается в том, что команды со встроенными символами новой строки оставляют некомментированную копию в истории в дополнение к закомментированной копии.
Могут быть другие проблемы, которые обнаруживаются. Дайте мне знать, если найдете.
Как это устроено
Bash выполняет команду, содержащуюся в переменной PROMPT_COMMAND
каждый раз, когда выдается первичное приглашение PS1
. Этот маленький скрипт использует эту возможность, чтобы захватить последнюю команду в истории, добавить комментарий и сохранить ее обратно.
Здесь это разделено с комментариями:
hpwd=$(history 1) # grab the most recent command
hpwd="${hpwd# *[0-9]* }" # strip off the history line number
if [[ ${hpwd%% *} == "cd" ]] # if it a cd command, we want the old directory
then # so the comment matches other commands "where *were* you when this was done?"
cwd=$OLDPWD
else
cwd=$PWD
fi
hpwd="${hpwd% ### *} ### $cwd" # strip off the old ### comment if there was one so they
# don't accumulate, then build the comment
history -s "$hpwd" # replace the most recent command with itself plus the comment
Ответ 2
hcmnt - версия с длинными функциями
Вот длинная версия в виде функции. Это монстр, но он добавляет несколько полезных функций. Я также разместил однострочный (оригинал) и более короткую функцию. Мне нравятся версии функций, потому что они не будут clobber других переменных в вашей среде и
они гораздо читабельнее, чем однострочный. Для получения дополнительной информации о том, как это работает, и некоторых ограничений, прочитайте запись для однострочного и итогового номеров в приведенной ниже функции. Я опубликовал каждую версию в своем собственном ответе, чтобы сделать вещи более организованными.
Чтобы использовать этот, сохраните его в файле с именем hcmnt
в таком месте, как /usr/local/bin
(вы можете chmod +x
его, если хотите), затем введите его в свой ~/.bashrc
следующим образом:
source /usr/local/bin/hcmnt
export hcmntextra='date "+%Y%m%d %R"'
export PROMPT_COMMAND='hcmnt'
Не редактируйте файл функции, где установлены PROMPT_COMMAND
или hcmntextra
. Оставьте их такими, чтобы они оставались как значения по умолчанию. Добавьте их в свой .bashrc
, как показано выше, и отредактируйте их там, чтобы установить параметры для hcmnt
или изменить или отключить hcmntextra
. В отличие от короткой функции, с этим вы должны установить переменную hcmntextra
и использовать параметр -e
, чтобы эта функция работала.
В комментариях к функции вы можете добавить несколько параметров, которые документированы (с несколькими примерами). Одна заметная особенность - иметь запись истории с добавленным комментарием, зарегистрированным в файле, и оставить фактическую историю нетронутой. Чтобы использовать эту функцию, просто
добавьте опцию -l
filename следующим образом:
export PROMPT_COMMAND="hcmnt -l ~/histlog"
Вы можете использовать любую комбинацию параметров, за исключением того, что -n
и -t
являются взаимоисключающими.
#!/bin/bash
hcmnt() {
# adds comments to bash history entries (or logs them)
# by Dennis Williamson - 2009-06-05 - updated 2009-06-19
# http://stackoverflow.com/questions/945288/saving-current-directory-to-bash-history
# (thanks to Lajos Nagy for the idea)
# the comments can include the directory
# that was current when the command was issued
# plus optionally, the date or other information
# set the bash variable PROMPT_COMMAND to the name
# of this function and include these options:
# -e - add the output of an extra command contained in the hcmntextra variable
# -i - add ip address of terminal that you are logged in *from*
# if you're using screen, the screen number is shown
# if you're directly logged in, the tty number or X display number is shown
# -l - log the entry rather than replacing it in the history
# -n - don't add the directory
# -t - add the from and to directories for cd commands
# -y - add the terminal device (tty)
# text or a variable
# Example result for PROMPT_COMMAND='hcmnt -et $LOGNAME'
# when hcmntextra='date "+%Y%m%d %R"'
# cd /usr/bin ### mike 20090605 14:34 /home/mike -> /usr/bin
# Example for PROMPT_COMMAND='hcmnt'
# cd /usr/bin ### /home/mike
# Example for detailed logging:
# when hcmntextra='date "+%Y%m%d %R"'
# and PROMPT_COMMAND='hcmnt -eityl ~/.hcmnt.log [email protected]$HOSTNAME'
# $ tail -1 ~/.hcmnt.log
# cd /var/log ### [email protected] /dev/pts/3 192.168.1.1 20090617 16:12 /etc -> /var/log
# INSTALLATION: source this file in your .bashrc
# will not work if HISTTIMEFORMAT is used - use hcmntextra instead
export HISTTIMEFORMAT=
# HISTTIMEFORMAT still works in a subshell, however, since it gets unset automatically:
# $ htf="%Y-%m-%d %R " # save it for re-use
# $ (HISTTIMEFORMAT=$htf; history 20)|grep 11:25
local script=$FUNCNAME
local hcmnt=
local cwd=
local extra=
local text=
local logfile=
local options=":eil:nty"
local option=
OPTIND=1
local usage="Usage: $script [-e] [-i] [-l logfile] [-n|-t] [-y] [text]"
local newline=$'\n' # used in workaround for bash history newline bug
local histline= # used in workaround for bash history newline bug
local ExtraOpt=
local LogOpt=
local NoneOpt=
local ToOpt=
local tty=
local ip=
# *** process options to set flags ***
while getopts $options option
do
case $option in
e ) ExtraOpt=1;; # include hcmntextra
i ) ip="$(who --ips -m)" # include the terminal ip address
ip=($ip)
ip="${ip[4]}"
if [[ -z $ip ]]
then
ip=$(tty)
fi;;
l ) LogOpt=1 # log the entry
logfile=$OPTARG;;
n ) if [[ $ToOpt ]]
then
echo "$script: can't include both -n and -t."
echo $usage
return 1
else
NoneOpt=1 # don't include path
fi;;
t ) if [[ $NoneOpt ]]
then
echo "$script: can't include both -n and -t."
echo $usage
return 1
else
ToOpt=1 # cd shows "from -> to"
fi;;
y ) tty=$(tty);;
: ) echo "$script: missing filename: -$OPTARG."
echo $usage
return 1;;
* ) echo "$script: invalid option: -$OPTARG."
echo $usage
return 1;;
esac
done
text=([email protected]) # arguments after the options are saved to add to the comment
text="${text[*]:$OPTIND - 1:${#text[*]}}"
# *** process the history entry ***
hcmnt=$(history 1) # grab the most recent command
# save history line number for workaround for bash history newline bug
histline="${hcmnt% *}"
hcmnt="${hcmnt# *[0-9]* }" # strip off the history line number
if [[ -z $NoneOpt ]] # are we adding the directory?
then
if [[ ${hcmnt%% *} == "cd" ]] # if it a cd command, we want the old directory
then # so the comment matches other commands "where *were* you when this was done?"
if [[ $ToOpt ]]
then
cwd="$OLDPWD -> $PWD" # show "from -> to" for cd
else
cwd=$OLDPWD # just show "from"
fi
else
cwd=$PWD # it not a cd, so just show where we are
fi
fi
if [[ $ExtraOpt && $hcmntextra ]] # do we want a little something extra?
then
extra=$(eval "$hcmntextra")
fi
# strip off the old ### comment if there was one so they don't accumulate
# then build the string (if text or extra aren't empty, add them plus a space)
hcmnt="${hcmnt% ### *} ### ${text:+$text }${tty:+$tty }${ip:+$ip }${extra:+$extra }$cwd"
if [[ $LogOpt ]]
then
# save the entry in a logfile
echo "$hcmnt" >> $logfile || echo "$script: file error." ; return 1
else
# workaround for bash history newline bug
if [[ $hcmnt != ${hcmnt/$newline/} ]] # if there a newline in the command
then
history -d $histline # then delete the current command so it not duplicated
fi
# replace the history entry
history -s "$hcmnt"
fi
} # END FUNCTION hcmnt
# set a default (must use -e option to include it)
export hcmntextra='date "+%Y%m%d %R"' # you must be really careful to get the quoting right
# start using it
export PROMPT_COMMAND='hcmnt'
update 2009-06-19: добавлены опции, полезные для ведения журнала (ip и tty), обходной путь для проблемы с дублирующейся записью, удаление посторонних нулевых присвоений
Ответ 3
Вы можете установить Advanced Shell History, инструмент с открытым исходным кодом, который записывает историю bash
или zsh
в базу данных sqlite. Это записывает такие вещи, как текущий рабочий каталог, код выхода команды, время запуска и остановки команды, время начала и окончания сеанса, tty и т.д.
Если вы хотите запросить базу данных истории, вы можете написать свои собственные SQL-запросы, сохранить их и сделать их доступными в комплекте ash_query
. Есть несколько полезных предварительно упакованных запросов, но, поскольку я хорошо знаю SQL, я обычно просто открываю базу данных и запрашиваю интерактивно, когда мне нужно что-то искать.
Один запрос, который я нахожу очень полезным, однако, смотрит на историю текущего рабочего каталога. Это помогает мне вспомнить, где я остановился, когда я над чем-то работал.
[email protected]:~$ ash_query -q CWD
session
when what
1
2014-08-27 17:13:07 ls -la
2014-08-27 17:13:09 cd .ash
2014-08-27 17:16:27 ls
2014-08-27 17:16:33 rm -rf advanced-shell-history/
2014-08-27 17:16:35 ls
2014-08-27 17:16:37 less postinstall.sh
2014-08-27 17:16:57 sudo reboot -n
И та же история, использующая текущий рабочий каталог (и что-то ниже):
[email protected]:~$ ash_query -q RCWD
session
where
when what
1
/home/vagrant/advanced-shell-history
2014-08-27 17:11:34 nano ~/.bashrc
2014-08-27 17:12:54 source /usr/lib/advanced_shell_history/bash
2014-08-27 17:12:57 source /usr/lib/advanced_shell_history/bash
2014-08-27 17:13:05 cd
/home/vagrant
2014-08-27 17:13:07 ls -la
2014-08-27 17:13:09 cd .ash
/home/vagrant/.ash
2014-08-27 17:13:10 ls
2014-08-27 17:13:11 ls -l
2014-08-27 17:13:16 sqlite3 history.db
2014-08-27 17:13:43 ash_query
2014-08-27 17:13:50 ash_query -Q
2014-08-27 17:13:56 ash_query -q DEMO
2014-08-27 17:14:39 ash_query -q ME
2014-08-27 17:16:26 cd
/home/vagrant
2014-08-27 17:16:27 ls
2014-08-27 17:16:33 rm -rf advanced-shell-history/
2014-08-27 17:16:35 ls
2014-08-27 17:16:37 less postinstall.sh
2014-08-27 17:16:57 sudo reboot -n
FWIW - Я являюсь автором и сторонником проекта.
Ответ 4
hcmnts - короткая функциональная версия
Вот короткая версия в виде функции. Я также разместил однострочный (оригинал) и более длинную функцию с несколькими добавленными функциями. Мне нравятся версии функций, потому что они не будут clobber других переменных в вашей среде, и они гораздо читабельнее, чем однострочный. Прочтите запись для однострочного изображения для получения дополнительной информации о том, как это работает и какие ограничения. Я опубликовал каждую версию в своем собственном ответе, чтобы сделать вещи более организованными.
Чтобы использовать этот, сохраните его в файле с именем hcmnts
в таком месте, как /usr/local/bin
(вы можете chmod +x
его, если хотите), затем введите его в свой ~/.bashrc
следующим образом:
source /usr/local/bin/hcmnts
Прокомментируйте строку, которая устанавливает hcmntextra
, если вам не нужны дата и время (или вы можете изменить ее формат или использовать другую команду помимо date
).
Это все, что нужно.
#!/bin/bash
hcmnts() {
# adds comments to bash history entries
# the *S*hort version of hcmnt (which has many more features)
# by Dennis Williamson
# http://stackoverflow.com/questions/945288/saving-current-directory-to-bash-history
# (thanks to Lajos Nagy for the idea)
# INSTALLATION: source this file in your .bashrc
# will not work if HISTTIMEFORMAT is used - use hcmntextra instead
export HISTTIMEFORMAT=
# HISTTIMEFORMAT still works in a subshell, however, since it gets unset automatically:
# $ htf="%Y-%m-%d %R " # save it for re-use
# $ (HISTTIMEFORMAT=$htf; history 20)|grep 11:25
local hcmnt
local cwd
local extra
hcmnt=$(history 1)
hcmnt="${hcmnt# *[0-9]* }"
if [[ ${hcmnt%% *} == "cd" ]]
then
cwd=$OLDPWD
else
cwd=$PWD
fi
extra=$(eval "$hcmntextra")
hcmnt="${hcmnt% ### *}"
hcmnt="$hcmnt ### ${extra:+$extra }$cwd"
history -s "$hcmnt"
}
export hcmntextra='date +"%Y%m%d %R"'
export PROMPT_COMMAND='hcmnts'
Ответ 5
Для тех, кто хочет этого в zsh, я изменен. Реализация Jeet Sukumaran и percol позволяют интерактивный поиск по ключевым словам и извлечение либо команды, либо путь, в котором он был выполнен. Также можно отфильтровать повторяющиеся команды и скрыть поля (дата, команда, путь)
Ответ 6
Джентльмен, это работает лучше. Единственное, что я не могу понять, - это сделать script НЕ log to syslog при входе в систему и записать последнюю команду в историю. Но пока работает как прелесть.
#!/bin/bash
trackerbash() {
# adds comments to bash history entries
# by Dennis Williamson
# http://stackoverflow.com/questions/945288/saving-current-directory-to-bash-history
# (thanks to Lajos Nagy for the idea)
#Supper Enhanced by QXT
# INSTALLATION: source this file in your .bashrc
export HISTTIMEFORMAT=
# export HISTTIMEFORMAT='%F %T '
local hcmnt
local cwd
local extra
local thistty
local whoiam
local sudouser
local shelldate
local TRACKIP
local TRACKHOST
thistty=`/usr/bin/tty|/bin/cut -f3-4 -d/`
whoiam=`/usr/bin/whoami`
sudouser=`last |grep $thistty |head -1 | awk '{ print $1 }' |cut -c 1-10`
hcmnt=$(history 1)
hcmnt="${hcmnt# *[0-9]* }"
cwd=`pwd`
hcmnt="${hcmnt% ### *}"
hcmnt=" $hcmnt ${extra:+$extra }"
shelldate=`date +"%Y %b %d %R:%S"`
TRACKHOST=`whoami | sed -r "s/.*\((.*)\).*/\\1/"`
TRACKIP=`last |grep $thistty |head -1 | awk '{ print $3 }'`
logger -p local1.notice -t bashtracker -i -- "$sudouser ${USER}: $thistty: $TRACKIP: $shelldate: $cwd : $hcmnt"
history -w
}
export PROMPT_COMMAND='trackerbash'
Ответ 7
Вот один вкладыш того, что я использую. Вставить его сюда, потому что он намного проще, и у меня нет проблем с историей сеансов, я просто хочу иметь историю с рабочим каталогом.
Также однострочник выше слишком сильно портит ваш пользовательский интерфейс.
export PROMPT_COMMAND='if [ "$(id -u)" -ne 0 ]; then echo "$(date "+%Y-%m-%d.%H:%M:%S") $(pwd) $(history 1)" >> ~/.bash.log; fi'
Так как мой домашний каталог, как правило, представляет собой перекрестную штуковину, это побочный эффект - история всего, что я когда-либо делал. При желании добавьте $(hostname)
к команде echo, приведенной выше... в зависимости от вашей рабочей среды.
Grep более чем хорош даже при 100 тыс. Записей. Нет необходимости sqlite войти. Просто не вводите пароли в командной строке, и все хорошо. Пароли в любом случае 90 технологий!
Также для поиска я склонен делать это:
function hh() {
grep "$1" ~/.bash.log
}
Ответ 8
Полное раскрытие: я автор FOSS-инструмента
shournal - журнал (file-) для вашей оболочки:
Используя интеграцию bash, рабочий каталог команды также хранится в shournal sqlite-database и может быть получен через
shournal --query -cmdcwd "$PWD"
Запросы для рабочих подкаталогов можно выполнить с помощью
shournal --query -cmdcwd -like "$PWD/%"