Bash komut çalıştırmadan önce çalıştırılan bir kanca var mı?


111

Kısaca, bir komutu çalıştırmadan hemen önce bir işlevin yürütülmesini ayarlayabilir miyim?

Orada $PROMPT_COMMANDsadece bir komut çalıştırdıktan sonra, yani bir istem görüntülemeden önce yürütüldüğü,.

Bash $PROMPT_COMMAND, zsh'nin precmdfonksiyonuna benzer ; bu yüzden aradığım şey zsh'inkine eşdeğer bir bash preexec.

Örnek uygulamalar: terminal başlığınızı yürütülen komuta ayarlayın; timeher komuttan önce otomatik olarak ekle .


3
bash sürüm 4.4, komutu okuduktan sonra fakat çalıştırmadan önce PS0gibi davranan PS1ancak kullanılan bir değişkene sahiptir . Bakınız gnu.org/software/bash/manual/bashref.html#Bash-Variables
glenn jackman

Yanıtlar:


93

Doğal değil, ancak DEBUGtuzağı kullanarak saldırıya uğrayabilir . Bu kod kurulur preexecve precmdzsh'ye benzer şekilde çalışır. Komut satırı tek bir argüman olarak iletilir preexec.

precmdHer komut çalıştırılmadan önce yürütülen bir fonksiyonun ayarlanması için kodun basitleştirilmiş bir versiyonu .

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

Bu numara Glyph Lefkowitz ; Orijinal yazarı bulmak için bcat sayesinde .

Düzenle. Glyph'in hack'in güncellenmiş bir versiyonunu burada bulabilirsiniz: https://github.com/rcaloras/bash-preexec


"$BASH_COMMAND" = "$PROMPT_COMMAND"Karşılaştırma benim için çalışmıyor i.imgur.com/blneCdQ.png
laggingreflex

2
Bu kodu cygwin'de kullanmayı denedim. Ne yazık ki, orada oldukça yoğun performans etkileri var - basit bir kıyaslama komutu çalıştırmak, time for i in {1..10}; do true; donenormalde 0,040 saniye ve DEBUG tuzağını etkinleştirdikten sonra 1,400 ila 1,600 saniye sürer. Tuzak komutunun döngü başına iki kez çalıştırılmasına neden olur - ve Cygwin'de sed yürütmek için gerekli olan çatal, tek başına çatallamak için kabaca 0.030 saniyede (ya da echoyerleşik /bin/echo) arasındaki hız farkını yavaşlatır . Belki akılda tutulması gereken bir şey.
kdb

2
Çatal emmek için kdb Cygwin performansı. Anladığım kadarıyla, bu Windows'ta önlenemez. Windows'ta bash kodunu çalıştırmanız gerekiyorsa, çatallığı kesmeyi deneyin.
Gilles

@DevNull Bu, tuzağı çıkartarak kolayca çözülebilir. İnsanların yapmasına izin verilenleri yapmaları için yapabilecekleri teknik bir çözüm yoktur. Kısmi çareler var: çok fazla kişiye erişim vermeyin, yedeklerinizin güncel olduğundan emin olun, doğrudan dosya manipülasyonu yerine sürüm kontrolünü kullanın,… Kullanıcıların kolayca devre dışı bırakamayacağı bir şey istiyorsanız, izin verin tek başına hiçbir şekilde devre dışı bırakılamaz, daha sonra kabuktaki kısıtlamalar size yardımcı olmaz: eklenebildikleri kadar kolay bir şekilde kaldırılabilirler.
Gilles,

1
Eğer daha komutları varsa PROMPT_COMMANDdeğişken (tarafından sınırlandırılmış örneğin ;) kullanarak, ikinci satırında desen eşleştirme kullanmanız gerekebilir preexec_invoke_execaynen böyle, işlevi: [[ "$PROMPT_COMMAND" =~ "$BASH_COMMAND" ]]. Bunun nedeni BASH_COMMAND, komutların her birini ayrı ayrı temsil etmektir.
jirislav

20

Sen kullanabilirsiniz trapkomutu (dan help trap):

Bir SIGNAL_SPEC DEBUG ise, her basit komuttan önce ARG yürütülür.

Örneğin, terminal başlığını dinamik olarak değiştirmek için şunları kullanabilirsiniz:

trap 'echo -e "\e]0;$BASH_COMMAND\007"' DEBUG

Gönderen bu kaynağın.


1
İlginç ... eski Ubuntu sunucumda help trap"bir SIGNAL_SPEC DEBUG ise, her basit komuttan sonra ARG çalıştırılır" diyor .
LarsH,

1
Ben kabul edilen yanıt özel bazı şeyler bu cevabın bir arada kullanılır: trap '[ -n "$COMP_LINE" ] && [ "$BASH_COMMAND" != "$PROMPT_COMMAND" ] && date "+%X";echo -e "\e]0;$BASH_COMMAND\007"' DEBUG. Bu, komutu başlığın içine koyar ve geçerli zamanı her komuttan hemen önce basar, ancak çalıştırırken bunu yapmaz $PROMPT_COMMAND.
coredumperror

1
Kod refactored beri @CoreDumpError, tüm koşulları inkâr gerekir: Birincisi dolayısıyla haline gelir: [ -z "$COMP_LINE" ].
cYrus 17:14

@cYrus Teşekkürler! Bu sorunu fark edecek kadar az bash programlaması bilmiyorum.
coredumperror

@LarsH: Hangi sürüme sahipsiniz? BASH_VERSION = "4.3.11 (1) - yeniden yayınla" ve "ARG her basit komuttan önce çalıştırılıyor" yazıyor .
musiphil

12

Yürütülen bir kabuk işlevi değil, ancak $PS0her komut çalıştırılmadan önce görüntülenen bir bilgi dizesine katkıda bulundum . Detaylar burada: http://stromberg.dnsalias.org/~strombrg/PS0-prompt/

$PS0dahildir bashen linux'lar 4.4 dahil etmek için bir süre alacağım olsa 4.4 - Bu arada isterseniz 4.4 kendinizi inşa edebilirsiniz; bu durumda, muhtemelen altına koymak gerekir /usr/local, eklemek /etc/shellsve chshbuna. Sonra oturumunuzu kapatın ve tekrar oturum açın, belki de sshlocalhost @ in ya da suilk önce kendinize bir test olarak katılabilirsiniz.


11

Kısa bir süre önce bu kesin sorunu bir yan projem için çözmek zorunda kaldım. Zsh'ın prexec ve basd için prrm işlevselliğini taklit eden oldukça sağlam ve esnek bir çözüm yaptım.

https://github.com/rcaloras/bash-preexec

Başlangıçta Glyph Lefkowitz'in çözümüne dayanıyordu, ama ben bunu geliştirdim ve bugüne kadar getirdim. Gerekirse bir özellik eklemek veya yardımcı olmaktan memnuniyet duyarız.


3

İpuçları için teşekkürler! Bunu kullanarak sona erdi:

#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="\[${_backblue}${_frontgreen_b}\]\u@\h:\[${_backblack}${_frontblue_b}\]\w\\$\[${_backnone}${_frontgreen_b}\] "

#'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 '{print $1}')"
declare -rx AUDIT_LOGINPID="$(who -mu | awk '{print $6}')"
declare -rx AUDIT_USER="$USER" #defined by pam during su/sudo
declare -rx AUDIT_PID="$$"
declare -rx AUDIT_TTY="$(who -mu | awk '{print $2}')"
declare -rx AUDIT_SSH="$([ -n "$SSH_CONNECTION" ] && echo "$SSH_CONNECTION" | awk '{print $1":"$2"->"$3":"$4}')"
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===${_backvoid}${_frontgrey}\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 "${_backnone}${_frontgrey}"
#  (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 "${_backnone}${_frontgrey}"
        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" "${AUDIT_CMD##*( )?(+([0-9])[^0-9])*( )}"
            then
                echo error "$AUDIT_STR $PWD" "${AUDIT_CMD##*( )?(+([0-9])[^0-9])*( )}"
            fi
        else
            echo $( date +%F_%H:%M:%S ) "$AUDIT_STR $PWD" "${AUDIT_CMD##*( )?(+([0-9])[^0-9])*( )}" >>/var/log/userlog.info
        fi
    fi
    #echo "===cmd:$BASH_COMMAND/subshell:$BASH_SUBSHELL/fc:$(fc -l -1)/history:$(history 1)/histline:${AUDIT_CMD%%+([^ 0-9])*}===" #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

Keyfini çıkarın!


Askıya alınmış olan bash komutları ile ilgili bir sorunum vardı ... Bir subshell kullanarak bir geçici çözüm buldum, ancak bu 'history -a'nın subshell kapsamı dışındaki geçmişi yenilemesine neden oldu ... Sonunda çözüm bir fonksiyon kullanmaktı. Bu, alt kabuk yürütme işleminden sonra geçmişi yeniden okudu. İstediğim gibi çalışıyor. Vaidas'ın jablonskis.org/2011/howto-log-bash-history-to-syslog'da yazdığı gibi dağıtmak , C'deki bash'i yamalamaktan daha kolaydır (bunu ben de geçmişte yaptım). ama her zaman geçmiş dosyasını tekrar okurken ve bir disk senkronizasyonu yaparken bazı performans düşüşleri var ...
francois scheurer

5
Bu kodu kırpmak isteyebilirsiniz; şu anda neredeyse tamamen okunamıyor.
l0b0

3

Bir yama veya özel bir çalıştırılabilir araç kullanmadan tüm 'bash' komutlarını / yerleşiklerini bir metin dosyasına veya 'syslog' sunucusuna kaydetmek için bir yöntem yazdım.

Dağıtılması çok kolaydır, çünkü 'bash' başlangıcında bir kez çağrılması gereken basit bir kabuk komut dosyasıdır.

Buradaki metoda bakınız .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.