Làm thế nào để có lịch sử lệnh với đầu ra dấu thời gian đến thiết bị đầu cuối liên tục?


9

Tôi sử dụng một bí danh đơn giản để cho phép "theo dõi" các lệnh trong một hoặc nhiều cửa sổ đầu cuối:

alias trackmi='export PROMPT_COMMAND="history -a; $PROMPT_COMMAND"'

Sau đó, tôi chỉ cần tệp .bash_historytail -f của mình trong một thiết bị đầu cuối khác trên không gian làm việc để nhận phản hồi ngay lập tức. Tôi vừa kích hoạt lịch sử không giới hạn và cập nhật định dạng lịch sử của tôi ( ) trong .bashrc . Tất nhiên lệnh hiển thị dấu thời gian. Nhưng định dạng tệp lịch sử mỗi se là:export HISTTIMEFORMAT="[%F %T] "history

#1401234303
alias
#1401234486
cat ../.bashrc 

Làm cách nào tôi có thể chuyển đổi thời gian Unix và toàn bộ lệnh được hiển thị trên một dòng giống như với historylệnh, bao gồm cả đánh số:

578  [2014-05-27 19:45:03] alias
579  [2014-05-27 19:48:06] cat ../.bashrc 

... Và làm theo điều đó. Hoặc tìm một cách để liên tục xuất đầu ra của historylệnh đến thiết bị đầu cuối?

Câu trả lời:


7

Với GNU awk:

tail -fn+1 ~/.bash_history | awk '
  /^#/{printf "%-4d [%s] ", ++n, strftime("%F %T", substr($0, 2)); next}; 1'

Hoạt động tuyệt vời ban đầu hiển thị nhanh hơn nếu tôi cập nhật giải pháp khác fn+1để so sánh! Cảm ơn!

5

Đây là sản phẩm cuối cùng hoạt động trên một xterm màn hình phân chia từ cơ bản mặc định vỏ đến hoạt động chỉ trong một vài lệnh:

nhập mô tả hình ảnh ở đây

Một cách khó khăn hơn để làm điều này hơn là được thể hiện trong ảnh chụp màn hình có thể trông như thế này:

PS1='$( { date ; fc -l -0 ; } >${TGT_PTY} )'$PS1

Nơi nào ${TGT_PTY}sẽ là bất cứ điều gì bạn nhận được từ ttylệnh khi thực sự chạy một vỏ tương tác trên màn hình nơi bạn muốn đầu ra của mình. Hoặc, thực sự, bạn có thể sử dụng bất kỳ tệp nào có thể ghi được vì về cơ bản nó chỉ là một mục tiêu để chuyển hướng tệp.

Tôi sử dụng cú pháp pty cho thiết bị đầu cuối giả vì tôi cho rằng đó là một loại xterm nào đó, nhưng bạn có thể dễ dàng dành một vt - và lịch sử phát trực tuyến của bạn luôn chỉ là một CTRL-ALT-Fnkết hợp chính. Nếu là tôi, tôi có thể kết hợp hai khái niệm và biến nó thành một phiên screenhoặc tmuxtrên một vt chuyên dụng ... Nhưng tôi lạc đề.

Trên một máy mới khởi động, tôi được chào đón với /bin/loginlời nhắc điển hình trên gettybảng điều khiển Linux điển hình . Tôi nhấn CTRL-ALT-F2để truy cập vào một kmsconbảng điều khiển ít điển hình hơn , hoạt động giống như xtermmột tty. Tôi nhập lệnh ttyvà nhận được phản hồi /dev/pts/0.

Nói chung, xterms ghép một thiết bị đầu cuối thành nhiều thiết bị đầu cuối giả - vì vậy nếu bạn làm điều tương tự trong X11 bằng cách chuyển đổi giữa các tab thiết bị đầu cuối hoặc cửa sổ, bạn cũng có thể nhận được đầu ra /dev/pts/[0-9]*. Nhưng các bảng điều khiển thiết bị đầu cuối ảo được truy cập với CTRL-ALT-Fncác tổ hợp phím là các thiết bị đầu cuối đúng (er) và do đó nhận được /dev/tty[0-9]*chỉ định riêng của chúng .

Đây là lý do tại sao sau khi đăng nhập vào console 2 khi tôi gõ ttyvào dấu nhắc thì phản hồi là /dev/pts/0nhưng khi tôi làm tương tự trên console 1 thì đầu ra là /dev/tty1. Trong mọi trường hợp, trở lại trên console 2 tôi sẽ làm:

bash
PS1='$( { date ; fc -l -0 ; } >/dev/tty1 )'$PS1

Không có tác dụng rõ rệt. Tôi tiếp tục gõ thêm một vài lệnh và sau đó tôi chuyển sang giao diện điều khiển 1 bằng cách nhấn CTRL-ALT-F1lại. Và ở đó tôi tìm thấy các mục lặp đi lặp lại giống như <date_time>\n<hist#>\t<hist_cmd_string>mọi lệnh tôi đã gõ trên console 2.

Tuy nhiên, việc chặn trực tiếp ghi vào thiết bị đầu cuối, một tùy chọn khác có thể trông giống như:

TGT_PTY=
mkfifo ${TGT_PTY:=/tmp/shell.history.pipe}
{   echo 'OPENED ON:'
    date
} >${TGT_PTY}

Và rồi có lẽ ...

less +F ${TGT_PTY}

Lệnh nhắc nhở thô không đáp ứng thông số kỹ thuật của bạn - không có chuỗi định dạng datevà không có tùy chọn định dạng cho fcmột trong hai - nhưng cơ chế của nó không yêu cầu nhiều: mỗi khi dấu nhắc của bạn hiển thị lệnh lịch sử cuối cùng và ngày giờ hiện tại được ghi ra các ${TGT_PTY}tập tin bạn chỉ định. Nó đơn giản như vậy.

Xem và in lịch sử vỏ là fcmục đích chính. Đó là một vỏ được tích hợp sẵn, ngay cả khi datekhông. Trong zsh fccó thể cung cấp tất cả các loại tùy chọn định dạng ưa thích, một số trong đó áp dụng cho tem thời gian. Và tất nhiên, như bạn lưu ý ở trên, chúng bashta historycó thể làm như vậy.

Vì lợi ích của đầu ra sạch hơn, bạn có thể sử dụng một kỹ thuật tôi đã giải thích rõ hơn ở đây để đặt biến theo dõi liên tục trong trình bao hiện tại mặc dù phải theo dõi nó và xử lý nó trong các chuỗi con trong chuỗi nhắc nhở.

Đây là một phương tiện di động để định dạng thông số kỹ thuật của bạn:

_HIST() { [ -z ${_LH#$1} ] ||
    { date "+${1}%t[%F %T]"
      fc -nl -0 
    } >${TGT_PTY}
    printf "(_LH=$1)-$1"
}

: "${_LH=0}"
PS1='${_LH##*[$(($(_HIST \!)))-9]}'$PS1

Tôi triển khai bộ đếm last_history$_LH chỉ theo dõi các bản cập nhật mới nhất để bạn không viết cùng một lệnh lịch sử hai lần - ví dụ chỉ để nhấn enter. Có một chút lộn xộn cần thiết để có được biến tăng trong lớp vỏ hiện tại để nó giữ nguyên giá trị của nó mặc dù hàm được gọi trong một lớp con - một lần nữa, được giải thích tốt hơn trong liên kết .

Đầu ra của nó trông giống như <hist#>\t[%F %T]\t<hist_cmd>\n

Nhưng đó chỉ là phiên bản di động hoàn toàn. Với bashnó có thể được thực hiện với ít hơn và bằng cách chỉ thực hiện các nội dung shell - điều có thể mong muốn khi bạn cho rằng đây là lệnh sẽ chạy mỗi khi bạn nhấn [ENTER]. Đây là hai cách:

_HIST() { [ -z ${_LH#$1} ] || {
        printf "${1}\t[%(%F %T)T]"
        fc -nl -0
    } >${TGT_PTY}
    printf "(_LH=$1)-$1"
}
PROMPT_COMMAND=': ${_LH=0};'$PROMPT_COMMAND
PS1='${_LH##*[$(($(_HIST \!)))-9]}'$PS1

Ngoài ra, sử dụng bashcủa historylệnh, bạn có thể xác định các _HISTchức năng theo cách này:

_HIST() { [ -z ${_LH#$1} ] || 
        HISTTIMEFORMAT="[%F %T]<tab>" \
        history 1 >${TGT_PTY}
    printf "(_LH=$1)-$1"
}

Đầu ra cho một trong hai phương thức cũng giống như: <hist#>\t[%F %T]\t<hist_cmd>\nmặc dù historyphương thức này bao gồm một số khoảng trắng hàng đầu. Tuy nhiên, tôi tin rằng historydấu thời gian của phương thức sẽ chính xác hơn vì tôi không nghĩ rằng họ cần đợi lệnh được tham chiếu hoàn thành trước khi có được dấu của họ.

Bạn có thể tránh theo dõi bất kỳ trạng thái nào trong cả hai trường hợp nếu chỉ bằng cách nào đó bạn lọc luồng với uniq- như bạn có thể làm với mkfifonhư tôi đã đề cập trước đây.

Nhưng thực hiện nó trong dấu nhắc như thế này có nghĩa là nó luôn được cập nhật ngay khi cần chỉ bằng hành động cập nhật dấu nhắc. Thật đơn giản.

Bạn cũng có thể làm một cái gì đó tương tự như những gì bạn đang làm với tailnhưng được đặt

HISTFILE=${TGT_PTY}

Tôi thực sự đang chỉnh sửa nó trong một tab khác ... Xin vui lòng thêm thời gian?
mikeerv

Chà, thực ra tôi sẽ lưu lưu ngay bây giờ, @ illuminÉ - nhưng bạn sẽ thấy nơi tôi rời đi ...
mikeerv

@ illuminÉ - Nhân tiện - và tôi hy vọng tôi đúng về điều này - Tôi giả sử bạn đã nhập lệnh như đã viết ${TGT_PTY}và tất cả? Nếu vậy, nó sẽ giải thích 'chuyển hướng mơ hồ' bởi vì đó sẽ là một biến trống. Bạn cần một tập tin. /dev/pts/[num]trong tất cả khả năng -
mikeerv

Nó không hoạt động! Các màn hình in đã giúp! Tôi ước khối mã đầu tiên và duy nhất của bạn là những gì bạn đặt trong màn hình in và tham chiếu rõ ràng để chọn pts mà bạn muốn đầu ra đi vào và nhập tab vào hàm - không cần gì khác. Sử dụng một biến để mô tả một cái gì đó tôi phải nhập thủ công để thử nó một cách nhanh chóng không phải là một cách làm tốt theo quan điểm của tôi vì bạn phải đào sâu vào văn bản để tìm ra tất cả, điều này làm ảnh hưởng đến giải pháp. Ngoài ra tất cả những điều tốt nhất cho bạn và gia đình bạn.

@ illuminÉ - đừng bận tâm về việc cattôi bị hoang tưởng. Nó hoạt động tốt - thậm chí 12 giờ sau.
mikeerv

4

Vui lòng chơi với định dạng, nhưng điều này (tôi tin) thực hiện những gì bạn đang yêu cầu ... lưu vào một nơi nào đó trong PATH của bạn, thực hiện và tận hưởng:

#!/bin/bash
count=$(  echo "scale=0 ; $(cat ~/.bash_history | wc -l ) / 2" | bc -l )
tail -f ~/.bash_history | awk -v c=$count '{if($1 ~/^#/){gsub(/#/, "", $1);printf "%s\t", c; "date \"+%F %T\" --date @" $1 | getline stamp; printf "[%s]\t",stamp;c++}else{print $0}}'

Tôi chắc chắn rằng nó có thể được tối ưu hóa, nhưng bạn có ý tưởng.

Giải thích ngắn gọn: vì ~ / .bash_history không theo dõi số lượng, trước tiên chúng tôi xác định số lượng mục nhập. Sau đó, một chút phép thuật awk để có được định dạng đúng, và theo dõi số lượng mục.


Điều đó không hoạt động nếu có nhiều mục. Cũng tail -fsẽ đọc 10 dòng ban đầu đã được bao gồm trong của bạn count. Nó giả sử ngày GNU trong môi trường không phải là POSIX (không đặt POSIXLY_CORRECT). Nó chạy một shell và một lệnh ngày trên dấu thời gian.
Stéphane Chazelas

@StephaneChazelas Đối với các mục đa dòng, trên thiết lập của tôi, chúng dường như đăng ký với cả hai giải pháp. Một cái gì đó trong thiết lập của tôi có thể?

1
@ illuminÉ, tink counttính một nửa dòng .bash_historyvà sau đó tăng cho mỗi dòng không bắt đầu #, vì vậy số lượng lịch sử được báo cáo có thể bị sai. Sử dụng count=$(grep -c '^#' ...)có thể sẽ tốt hơn, nhưng trong mọi trường hợp, những số lịch sử đó có thể sẽ không đồng bộ hóa đặc biệt là nếu bạn có nhiều hơn 2 bash chạy cùng một lúc.
Stéphane Chazelas

@StephaneChazelas Trân trọng vì tôi không thể đánh giá cao một trong hai giải pháp Tôi rất biết ơn về lời giải thích! Thật vậy, số lượng là khác nhau, do đó cao hơn nhiều ở đây ... Tôi có thể thấy bạn đã xây dựng giải pháp của riêng mình xung quanh strftime, về cơ bản là những gì historylệnh tận dụng.

1
Cảm ơn bạn đã phản hồi @StephaneChazelas, tôi sẽ xem liệu tôi có thể làm việc xung quanh những người đó không :)
tink
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.