2015-05-15 310 views
3

最近我想在Mac中嘗試Z shell。但是我想繼續保存命令歷史到〜/ .persistent_history,這正是我在Bash中所做的(ref)。保存Zsh的歷史記錄〜/ .persistent_history

然而,在裁判鏈接腳本在zsh下不能正常工作:

log_bash_persistent_history() 
{ 
    [[ 
    $(history 1) =~ ^\ *[0-9]+\ +([^\ ]+\ [^\ ]+)\ +(.*)$ 
    ]] 
    local date_part="${BASH_REMATCH[1]}" 
    local command_part="${BASH_REMATCH[2]}" 
    if [ "$command_part" != "$PERSISTENT_HISTORY_LAST" ] 
    then 
    echo $date_part "|" "$command_part" >> ~/.persistent_history 
    export PERSISTENT_HISTORY_LAST="$command_part" 
    fi 
} 
run_on_prompt_command() 
{ 
    log_bash_persistent_history 
} 
PROMPT_COMMAND="run_on_prompt_command" 

是否有任何人誰可以幫助我得到它的工作?非常感謝!

+1

[這](http://superuser.com/questions/735660/whats-the-zsh-equivalent-of-bashs-prompt-command)應與替換'PROMPT_COMMAND'幫助。替換[[''用法應該可以用'grep -o'或'cut'或類似的方法來實現,但是取決於zsh中'history'的確切輸出。 –

+0

@EtanReisner非常感謝!對於'PROMPT_COMMAND',鏈接應該是有幫助的。對於''[''部分,我剛剛用'history'命令發現,bash會在最後一行給出最新的一個(在這種情況下,它是'history')。但是在Zsh下,'history'命令不會返回最新的命令,它會在最後一行返回'history'之前使用的命令。有任何想法嗎? :-) – astroboylrx

+0

那麼,我沒有理由重新發明輪子。只需設置'HISTFILE'並將'HISTSIZE'和'SAVEHIST'設置爲一個非常大的尺寸(我的尺寸是100,000),並且我沒有理由讓它們變大,因爲我在iTerm2中記錄了所有終端會話 - 這就是所有命令+輸出,在提示中時間縮短到秒)。默認的歷史記錄格式具有關聯的POSIX時間戳,由於您沒有tzinfo,所以它比您的更準確。 – 4ae1e1

回答

2

經過這麼多谷歌搜索,我終於找到了做到這一點的方式。 首先,在〜/ .zshrc,添加下列選項歷史操作:

setopt append_history # append rather then overwrite 
setopt extended_history # save timestamp 
setopt inc_append_history # add history immediately after typing a command 

總之,這三個選項會立即記錄下每一個input_time +命令到〜/ .zsh_history。 然後,把這個功能到〜/ .zshrc:

precmd() { # This is a function that will be executed before every prompt 
    local date_part="$(tail -1 ~/.zsh_history | cut -c 3-12)" 
    local fmt_date="$(date -d @${date_part} +'%Y-%m-%d %H:%M:%S')" 
    # For older version of command "date", comment the last line and uncomment the next line 
    #local fmt_date="$(date -j -f '%s' ${date_part} +'%Y-%m-%d %H:%M:%S')" 
    local command_part="$(tail -1 ~/.zsh_history | cut -c 16-)" 
    if [ "$command_part" != "$PERSISTENT_HISTORY_LAST" ] 
    then 
     echo "${fmt_date} | ${command_part}" >> ~/.persistent_history 
     export PERSISTENT_HISTORY_LAST="$command_part" 
    fi 
} 

由於我同時使用bash和zsh的,所以我希望有一個文件,該文件可以保存他們所有的歷史命令。在這種情況下,我可以使用「grep」輕鬆搜索它們。

+0

多線命令失敗。以下工作:'local date_part =「$(gawk'substr($ 0,0,1)==」:「{print;}'〜/ .zsh_history | tail -1 | cut -c 3-12)」' –

1

原來的答案大多是好的,但要處理,也包含字符多行命令「:」比如這個作品:

local line_num_last=$(grep -ane '^:' ~/.zsh_history | tail -1 | cut -d':' -f1 | tr -d '\n') 
local date_part="$(gawk "NR == $line_num_last {print;}" ~/.zsh_history | cut -c 3-12)" 
local fmt_date="$(date -d @${date_part} +'%Y-%m-%d %H:%M:%S')" 
local command_part="$(gawk "NR >= $line_num_last {print;}" ~/.zsh_history | sed -re '1s/.{15}//')" 
+0

涼!我沒有想過多行命令。這非常有幫助。非常感謝! :-) – astroboylrx

+0

我添加另一行來使多行命令存儲爲單行命令:'local one_line_command = $ {(Q)command_part // \\ $'\ n'/}'。現在看起來非常棒。 :-) – astroboylrx

0

不能發表評論,但(這超出了一個簡單的更正),所以我會添加這個答案。

This correctionthe accepted answer完全不是那麼回事的時候,例如,最後一個命令了相當多的時間來執行 - 你會得到流浪數字和你的命令;,像這樣:

2017-07-22 19:02:42 | 3;micro ~/.zshrc && . ~/.zshrc 

這可以通過在command_part與稍長gawk,這也避免了我們一個管道更換sed -re '1s/.{15}//'固定:

local command_part="$(gawk " 
    NR == $line_num_last { 
    pivot = match(\$0, \";\"); 
    print substr(\$0, pivot+1); 
    } 
    NR > $line_num_last { 
    print; 
    }" ~/.zsh_history)" 

它也有問題時deali使用多行命令,其中一行以:開頭。這可以(大部分)通過用grep -anE '^: [0-9]{10}:[0-9]*?;' ~/.zsh_history代替line_num_last中的grep -ane '^:' ~/.zsh_history來解決 - 我說主要是因爲一個命令可能包含一個匹配該表達式的字符串。再說了,

% naughty "multiline 
> command 
> ::123;but a command I'm not 
> " 

這將導致~/.persistent_history一個慘敗紀錄。

爲了解決這個問題,我們需要反過來,檢查以前的redord是否有結束\(可能還有其他的條件,但我不是這樣的歷史格式熟悉卻又),如果是這樣嘗試之前的比賽。

_get_line_num_last() { 
    local attempts=0 
    local line=0 
    while true; do 
    # Greps the last two lines that can be considered history records 
    local lines="$(grep -anE '^: [0-9]{10}:[0-9]*?;' ~/.zsh_history | \ 
       tail -n $((2 + attempts)) | head -2)" 
    local previous_line="$(echo "$lines" | head -1)" 
    # Gets the line number of the line being tested 
    local line_attempt=$(echo "$lines" | tail -1 | cut -d':' -f1 | tr -d '\n') 
    # If the previous (possible) history records ends with `\`, then the 
    # _current_ one is part of a multiline command; try again. 
    # Probably. Unless it was in turn in the middle of a multi-line 
    # command. And that's why the last line should be saved. 
    if [[ $line_attempt -ne $HISTORY_LAST_LINE ]] && \ 
     [[ $previous_line == *"\\" ]] && [[ $attempts -eq 0 ]]; 
    then 
     ((attempts+=1)) 
    else 
     line=$line_attempt 
     break 
    fi 
    done 
    echo "$line" 
} 
precmd() { 
    local line_num_last="$(_get_line_num_last)" 
    local date_part="$(gawk "NR == $line_num_last {print;}" ~/.zsh_history | cut -c 3-12)" 
    local fmt_date="$(date -d @${date_part} +'%Y-%m-%d %H:%M:%S')" 
    # I use awk itself to split the _first_ line only at the first `;` 
    local command_part="$(gawk " 
    NR == $line_num_last { 
     pivot = match(\$0, \";\"); 
     print substr(\$0, pivot+1); 
    } 
    NR > $line_num_last { 
     print; 
    }" ~/.zsh_history)" 
    if [ "$command_part" != "$PERSISTENT_HISTORY_LAST" ] 
    then 
    echo "${fmt_date} | ${command_part}" >> ~/.persistent_history 
    export PERSISTENT_HISTORY_LAST="$command_part" 
    export HISTORY_LAST_LINE=$((1 + $(wc -l < ~/.zsh_history))) 
    fi 
}