Hat bash einen Hook, der ausgeführt wird, bevor ein Befehl ausgeführt wird?
38544
Gilles
Kann ich in bash vor dem Ausführen eines Befehls eine Funktion ausführen lassen?
Es gibt eine $PROMPT_COMMAND, die ausgeführt wird, bevor eine Eingabeaufforderung angezeigt wird, dh unmittelbar nachdem ein Befehl ausgeführt wurde.
Bash $PROMPT_COMMANDist analog zu zshs precmdFunktion; Was ich also suche, ist ein bash-Äquivalent zu zsh's preexec.
Beispielanwendungen: Setzen Sie Ihren Terminaltitel auf den gerade ausgeführten Befehl. automatisch timevor jedem Befehl hinzufügen .
Die bash-Version 4.4 hat eine "PS0" -Variable, die sich wie "PS1" verhält, aber nach dem Lesen des Befehls aber vor dem Ausführen verwendet wird. Siehe https://www.gnu.org/software/bash/manual/bashref.html#Bash-Variables
glenn jackman vor 7 Jahren
1
Nicht nativ, aber es kann mit der DEBUGFalle gehackt werden . Dieser Code einrichtet preexecund precmdfunktioniert ähnlich wie zsh. Die Befehlszeile wird als einzelnes Argument an übergeben preexec.
Hier ist eine vereinfachte Version des Codes zum Einrichten einer precmdFunktion, die ausgeführt wird, bevor die einzelnen Befehle ausgeführt werden.
preexec () { :; } preexec_invoke_exec () { [ -n "$COMP_LINE" ] && return # do nothing if completing [ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return # don't cause a preexec for $PROMPT_COMMAND local this_command=`HISTTIMEFORMAT= history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//"`; preexec "$this_command" } trap 'preexec_invoke_exec' DEBUG
Dieser Trick geht auf Glyph Lefkowitz zurück ; Danke an bcat für die Suche nach dem ursprünglichen Autor.
Der Vergleich "$ BASH_COMMAND" = "$ PROMPT_COMMAND" funktioniert für mich nicht http://i.imgur.com/blneCdQ.png
laggingreflex vor 10 Jahren
0
Ich habe versucht, diesen Code auf Cygwin zu verwenden. Leider hat es dort ziemlich starke Leistungseffekte - einen einfachen Benchmark-Befehl `time for i in ausführen; wahr machen; done` dauert normalerweise 0,040 Sekunden und 1,400 bis 1,600 Sekunden nach Aktivierung der DEBUG-Falle. Dadurch wird der Trap-Befehl zweimal pro Schleife ausgeführt - und für Cygwin ist die für die Ausführung von sed erforderliche Gabelung mit etwa 0,030 Sekunden für alleinige Gabelung unerschwinglich langsam (Geschwindigkeitsunterschied zwischen `echo` builtin und` / bin / echo`). Vielleicht etwas, das Sie nicht vergessen sollten.
kdb vor 8 Jahren
1
@kdb Cygwin Leistung für Gabel saugt. Meines Wissens ist dies unter Windows unvermeidlich. Wenn Sie Bash-Code unter Windows ausführen müssen, reduzieren Sie das forking.
Gilles vor 8 Jahren
0
@DevNull Dies kann durch Entfernen der Falle sehr leicht umgangen werden. Es gibt keine technische Lösung für Menschen, die tun, was sie dürfen, aber nicht tun sollten. Es gibt teilweise Abhilfemaßnahmen: Geben Sie nicht so vielen Benutzern so viel Zugriff, stellen Sie sicher, dass Ihre Sicherungen auf dem neuesten Stand sind, verwenden Sie die Versionskontrolle anstelle der direkten Dateibearbeitung,… Wenn Sie etwas möchten, das Benutzer nicht einfach deaktivieren können, lassen Sie es Alleine kann sich überhaupt nicht deaktivieren, dann helfen dir Einschränkungen in der Shell nicht: Sie können genauso einfach entfernt werden, wie sie hinzugefügt werden können.
Gilles vor 8 Jahren
0
@Glyph Kann dies so geändert werden, dass der eigentliche Benutzerbefehl nicht ausgeführt wird, wenn er bestimmte Kriterien nicht erfüllt (z. B. die Ausführung eines Befehls blockieren und eine Warnung drucken, wenn er aus einer Wörterbuchdatei unleserliche Sprache enthält)?
DevNull vor 8 Jahren
0
16
cYrus
Sie können den trapBefehl (von help trap) verwenden:
Wenn ein SIGNAL_SPEC DEBUG ist, wird ARG vor jedem einfachen Befehl ausgeführt.
Um beispielsweise den Terminaltitel dynamisch zu ändern, können Sie Folgendes verwenden:
Interessant ... Auf meinem alten Ubuntu-Server sagt "help trap": "Wenn ein SIGNAL_SPEC DEBUG ist, wird ARG ** nach ** jedem einfachen Befehl ausgeführt".
LarsH vor 11 Jahren
1
Ich habe eine Kombination dieser Antwort mit einigen besonderen Dingen in der akzeptierten Antwort verwendet: `trap '[-n" $ COMP_LINE "] && [" $ BASH_COMMAND "! =" $ PROMPT_COMMAND "] && date" +% X "; echo -e "\ e] 0; $ BASH_COMMAND \ 007" "DEBUG". Dadurch wird der Befehl in den Titel eingefügt und die aktuelle Uhrzeit wird direkt vor jedem Befehl gedruckt. Dies geschieht jedoch nicht, wenn `$ PROMPT_COMMAND` ausgeführt wird.
CoreDumpError vor 10 Jahren
1
@CoreDumpError, da Sie den Code überarbeitet haben, sollten Sie alle Bedingungen negieren. Die erste Bedingung lautet: `[-z" $ COMP_LINE "]`.
cYrus vor 10 Jahren
1
@cYrus Danke! Ich kann die bash-Programmierung nicht beinahe genug, um dieses Problem bemerkt zu haben.
CoreDumpError vor 10 Jahren
0
@LarsH: Welche Version hast du? Ich habe BASH_VERSION = "4.3.11 (1) -release" und es heißt "ARG wird * vor * jedem einfachen Befehl ausgeführt."
musiphil vor 10 Jahren
0
@musiphil: Ich erinnere mich nicht. Es war ein Server, den es wahrscheinlich nicht mehr gibt, in einer Organisation, für die ich nicht mehr arbeite.
LarsH vor 10 Jahren
0
10
RCCola
Ich musste dieses genaue Problem kürzlich für ein Nebenprojekt von mir lösen. Ich habe eine ziemlich robuste und robuste Lösung entwickelt, die die preexec- und precmd-Funktionalität von zsh für bash emuliert.
Ursprünglich war sie auf Glyph Lefkowitz 'Lösung basiert, aber ich habe sie verbessert und auf den neuesten Stand gebracht. Gerne helfen oder bei Bedarf eine Funktion hinzufügen.
9
dstromberg
Es ist keine Shell-Funktion, die ausgeführt wird, aber ich habe eine Eingabeaufforderung für $ PS0 eingefügt, die angezeigt wird, bevor jeder Befehl ausgeführt wird. Details hier: http://stromberg.dnsalias.org/~strombrg/PS0-prompt/
$ PS0 ist in bash 4.4 enthalten, obwohl es eine Weile dauern wird, bis die meisten Linux 4.4 enthalten - Sie können 4.4 aber auch selbst bauen, wenn Sie möchten; In diesem Fall sollten Sie es wahrscheinlich unter / usr / local ablegen und / etc / shells und chsh hinzufügen. Dann loggen Sie sich aus und wieder ein, vielleicht ssh'ing zu sich selbst @ localhost oder machen Sie sich zuerst einen Test.
3
francois scheurer
Thank you for the hints! I ended up using this:
#created by francois scheurer #sourced by '~/.bashrc', which is the last runned startup script for bash invocation #for login interactive, login non-interactive and non-login interactive shells. #note that a user can easily avoid calling this file by using options like '--norc'; #he also can unset or overwrite variables like 'PROMPT_COMMAND'. #therefore it is useful for audit but not for security. #prompt & color #http://www.pixelbeat.org/docs/terminal_colours/#256 #http://www.frexx.de/xterm-256-notes/ _backnone="\e[00m" _backblack="\e[40m" _backblue="\e[44m" _frontred_b="\e[01;31m" _frontgreen_b="\e[01;32m" _frontgrey_b="\e[01;37m" _frontgrey="\e[00;37m" _frontblue_b="\e[01;34m" PS1="\[$$\]\u@\h:\[$$\]\w\\$\[$$\] " #'history' options declare -rx HISTFILE="$HOME/.bash_history" chattr +a "$HISTFILE" # set append-only declare -rx HISTSIZE=500000 #nbr of cmds in memory declare -rx HISTFILESIZE=500000 #nbr of cmds on file declare -rx HISTCONTROL="" #does not ignore spaces or duplicates declare -rx HISTIGNORE="" #does not ignore patterns declare -rx HISTCMD #history line number history -r #to reload history from file if a prior HISTSIZE has truncated it if groups | grep -q root; then declare -x TMOUT=3600; fi #timeout for root's sessions #enable forward search (ctrl-s) #http://ruslanspivak.com/2010/11/25/bash-history-incremental-search-forward/ stty -ixon #history substitution ask for a confirmation shopt -s histverify #add timestamps in history - obsoleted with logger/syslog #http://www.thegeekstuff.com/2008/08/15-examples-to-master-linux-command-line-history/#more-130 #declare -rx HISTTIMEFORMAT='%F %T ' #bash audit & traceabilty # # declare -rx AUDIT_LOGINUSER="$(who -mu | awk '')" declare -rx AUDIT_LOGINPID="$(who -mu | awk '')" declare -rx AUDIT_USER="$USER" #defined by pam during su/sudo declare -rx AUDIT_PID="$$" declare -rx AUDIT_TTY="$(who -mu | awk '')" declare -rx AUDIT_SSH="$([ -n "$SSH_CONNECTION" ] && echo "$SSH_CONNECTION" | awk '')" declare -rx AUDIT_STR="[audit $AUDIT_LOGINUSER/$AUDIT_LOGINPID as $AUDIT_USER/$AUDIT_PID on $AUDIT_TTY/$AUDIT_SSH]" declare -rx AUDIT_SYSLOG="1" #to use a local syslogd # #PROMPT_COMMAND solution is working but the syslog message are sent *after* the command execution, #this causes 'su' or 'sudo' commands to appear only after logouts, and 'cd' commands to display wrong working directory #http://jablonskis.org/2011/howto-log-bash-history-to-syslog/ #declare -rx PROMPT_COMMAND='history -a >(tee -a ~/.bash_history | logger -p user.info -t "$AUDIT_STR $PWD")' #avoid subshells here or duplicate execution will occurs! # #another solution is to use 'trap' DEBUG, which is executed *before* the command. #http://superuser.com/questions/175799/does-bash-have-a-hook-that-is-run-before-executing-a-command #http://www.davidpashley.com/articles/xterm-titles-with-bash.html #set -o functrace; trap 'echo -ne "===$BASH_COMMAND===$$\n"' DEBUG set +o functrace #disable trap DEBUG inherited in functions, command substitutions or subshells, normally the default setting already #enable extended pattern matching operators shopt -s extglob #function audit_DEBUG() { # echo -ne "$$" # (history -a >(logger -p user.info -t "$AUDIT_STR $PWD" < <(tee -a ~/.bash_history))) && sync && history -c && history -r # #http://stackoverflow.com/questions/103944/real-time-history-export-amongst-bash-terminal-windows # #'history -c && history -r' force a refresh of the history because 'history -a' was called within a subshell and therefore # #the new history commands that are appent to file will keep their "new" status outside of the subshell, causing their logging # #to re-occur on every function call... # #note that without the subshell, piped bash commands would hang... (it seems that the trap + process substitution interfer with stdin redirection) # #and with the subshell #} ##enable trap DEBUG inherited for all subsequent functions; required to audit commands beginning with the char '(' for a subshell #set -o functrace #=> problem: completion in commands avoid logging them function audit_DEBUG() { #simplier and quicker version! avoid 'sync' and 'history -r' that are time consuming! if [ "$BASH_COMMAND" != "$PROMPT_COMMAND" ] #avoid logging unexecuted commands after Ctrl-C or Empty+Enter then echo -ne "$$" local AUDIT_CMD="$(history 1)" #current history command #remove in last history cmd its line number (if any) and send to syslog if [ -n "$AUDIT_SYSLOG" ] then if ! logger -p user.info -t "$AUDIT_STR $PWD" "$" then echo error "$AUDIT_STR $PWD" "$" fi else echo $( date +%F_%H:%M:%S ) "$AUDIT_STR $PWD" "$" >>/var/log/userlog.info fi fi #echo "===cmd:$BASH_COMMAND/subshell:$BASH_SUBSHELL/fc:$(fc -l -1)/history:$(history 1)/histline:$===" #for debugging } function audit_EXIT() { local AUDIT_STATUS="$?" if [ -n "$AUDIT_SYSLOG" ] then logger -p user.info -t "$AUDIT_STR" "#=== bash session ended. ===" else echo $( date +%F_%H:%M:%S ) "$AUDIT_STR" "#=== bash session ended. ===" >>/var/log/userlog.info fi exit "$AUDIT_STATUS" } #make audit trap functions readonly; disable trap DEBUG inherited (normally the default setting already) declare -fr +t audit_DEBUG declare -fr +t audit_EXIT if [ -n "$AUDIT_SYSLOG" ] then logger -p user.info -t "$AUDIT_STR" "#=== New bash session started. ===" #audit the session openning else echo $( date +%F_%H:%M:%S ) "$AUDIT_STR" "#=== New bash session started. ===" >>/var/log/userlog.info fi #when a bash command is executed it launches first the audit_DEBUG(), #then the trap DEBUG is disabled to avoid a useless rerun of audit_DEBUG() during the execution of pipes-commands; #at the end, when the prompt is displayed, re-enable the trap DEBUG declare -rx PROMPT_COMMAND="trap 'audit_DEBUG; trap DEBUG' DEBUG" declare -rx BASH_COMMAND #current command executed by user or a trap declare -rx SHELLOPT #shell options, like functrace trap audit_EXIT EXIT #audit the session closing
Enjoy!
Ich hatte ein Problem mit Pipe-bash-Befehlen, die hängen bleiben ... Ich fand eine Problemumgehung mithilfe einer Subshell, aber dies führte dazu, dass 'history -a' die Historie nicht außerhalb des Subshell-Bereichs auffrischte ... Schließlich bestand die Lösung darin, eine Funktion zu verwenden Lesen Sie die Historie nach der Ausführung der Subshell erneut. Es funktioniert wie ich wollte. Wie Vaidas auf http://jablonskis.org/2011/howto-log-bash-history-to-syslog/ schrieb, ist die Bereitstellung einfacher als das Patchen der Bash in C (das habe ich auch in der Vergangenheit getan). Es gibt jedoch einen Leistungsabfall, wenn die Verlaufsdatei jedes Mal neu eingelesen wird und ein Datenträger "synchronisiert" wird ...
francois scheurer vor 12 Jahren
0
Möglicherweise möchten Sie diesen Code abschneiden. derzeit ist es fast völlig unlesbar.
l0b0 vor 12 Jahren
5
3
Francois Scheurer
Ich habe eine Methode geschrieben, um alle 'bash'-Befehle / builtins in einer Textdatei oder einem' syslog'-Server zu protokollieren, ohne einen Patch oder ein spezielles ausführbares Werkzeug zu verwenden.
Es ist sehr einfach zu implementieren, da es ein einfaches Shellscript ist, das bei der Initialisierung der 'bash' einmal aufgerufen werden muss.