Bagaimana cara memiliki riwayat perintah dengan output cap waktu ke terminal terus menerus?

9

Saya menggunakan alias sederhana untuk mengaktifkan "pelacakan" perintah di satu atau banyak jendela terminal:

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

Lalu saya hanya file .bash_historytail -f saya di terminal lain di ruang kerja untuk mendapatkan umpan balik segera. Saya baru saja mengaktifkan riwayat tidak terbatas dan memperbarui format riwayat saya ( ) di .bashrc . Tentu saja perintah menampilkan cap waktu. Tetapi format file histori per se adalah:export HISTTIMEFORMAT="[%F %T] "history

#1401234303
alias
#1401234486
cat ../.bashrc 

Bagaimana saya dapat mengubah waktu Unix dan seluruh perintah ditampilkan pada satu baris seperti halnya historyperintah, termasuk penomoran:

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

... dan ikuti itu. Atau menemukan cara untuk terus menampilkan output dari historyperintah ke terminal?

Komunitas
sumber

Jawaban:

7

Dengan GNU awk:

tail -fn+1 ~/.bash_history | awk '
  /^#/{printf "%-4d [%s] ", ++n, strftime("%F %T", substr($0, 2)); next}; 1'
Stéphane Chazelas
sumber
Bekerja dengan baik dan pada awalnya menampilkan lebih cepat jika saya memperbarui solusi lain dengan fn+1membandingkan! Terima kasih!
5

Inilah produk akhir yang bekerja pada xterm layar terbagi dari dasarnya shell untuk bekerja hanya dalam beberapa perintah:

masukkan deskripsi gambar di sini

Cara yang lebih kasar untuk melakukan ini daripada yang ditunjukkan dalam tangkapan layar mungkin terlihat seperti ini:

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

Di mana ${TGT_PTY}akan menjadi apa pun yang Anda dapatkan dari ttyperintah ketika benar-benar menjalankan shell interaktif di layar di mana Anda ingin output Anda. Atau, sungguh, Anda bisa menggunakan file yang dapat ditulis sama sekali karena pada dasarnya hanya target untuk pengalihan file.

Saya menggunakan sintaks pty untuk pseudo-terminal karena saya menganggap itu adalah xterm dari beberapa jenis, tetapi Anda mungkin dengan mudah mendedikasikan vt - dan sejarah streaming Anda selalu hanya merupakan CTRL-ALT-Fnkombo kunci. Jika itu aku, aku mungkin menggabungkan dua gagasan dan membuatnya menjadi screenatau tmuxsesi pada vt khusus ... Tapi aku ngelantur.

Pada mesin yang baru saja boot saya disambut dengan /bin/loginprompt khas pada gettykonsol Linux yang khas . Saya menekan CTRL-ALT-F2untuk mengakses kmsconkonsol yang kurang umum yang berperilaku lebih seperti xterma tty. Saya memasukkan perintah ttydan menerima sebagai tanggapan /dev/pts/0.

Umumnya xterms multipleks perangkat terminal tunggal menjadi banyak menggunakan pseudo-terminal - jadi jika Anda melakukan hal serupa di X11 dengan beralih di antara tab terminal atau windows Anda kemungkinan akan menerima output /dev/pts/[0-9]*juga. Tetapi konsol terminal virtual yang diakses dengan CTRL-ALT-Fnkombinasi kunci adalah perangkat terminal yang benar (er) dan karenanya menerima /dev/tty[0-9]*peruntukannya sendiri .

Inilah sebabnya mengapa setelah masuk ke konsol 2 ketika saya mengetik ttydi prompt jawabannya adalah /dev/pts/0tetapi ketika saya melakukan hal yang sama pada konsol 1 hasilnya /dev/tty1. Bagaimanapun, kembali pada konsol 2 saya kemudian lakukan:

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

Tidak ada efek yang terlihat. Saya terus mengetik beberapa perintah lagi dan kemudian saya beralih ke konsol 1 dengan menekan CTRL-ALT-F1lagi. Dan di sana saya menemukan entri berulang yang terlihat seperti <date_time>\n<hist#>\t<hist_cmd_string>untuk setiap perintah yang saya ketikkan pada konsol 2.

Pembatasan langsung menulis ke perangkat terminal, opsi lain dapat terlihat seperti:

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

Dan mungkin ...

less +F ${TGT_PTY}

Perintah prompt kasar tidak memenuhi spesifikasi Anda - tidak ada string format untuk datedan tidak ada opsi format fcbaik - tetapi mekanismenya tidak memerlukan banyak: setiap kali prompt Anda membuat perintah riwayat terakhir dan tanggal dan waktu saat ini ditulis untuk yang ${TGT_PTY}file yang Anda tentukan. Sesederhana itu.

Menonton dan mencetak riwayat shell adalah fctujuan utama. Ini adalah built-in shell, meskipun datetidak. Di zsh fcdapat memberikan semua jenis opsi pemformatan mewah, beberapa di antaranya berlaku untuk cap waktu. Dan tentu saja, seperti yang Anda perhatikan di atas, bash's historydapat melakukan hal yang sama.

Untuk kepentingan output yang lebih bersih, Anda dapat menggunakan teknik yang saya jelaskan lebih baik di sini untuk mengatur variabel pelacakan persisten di shell saat ini meskipun harus melacak dan memprosesnya dalam subkulit dalam urutan cepat.

Berikut cara portabel memformat dengan spesifikasi Anda:

_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

Saya menerapkan penghitung last_history$_LH yang hanya melacak pembaruan terbaru sehingga Anda tidak menulis perintah sejarah yang sama dua kali - misalnya hanya untuk menekan enter. Ada sedikit perselisihan yang diperlukan untuk mendapatkan variabel yang bertambah dalam shell saat ini sehingga tetap mempertahankan nilainya meskipun fungsinya disebut dalam subkulit - yang, sekali lagi, lebih baik dijelaskan dalam tautan .

Outputnya seperti <hist#>\t[%F %T]\t<hist_cmd>\n

Tapi itu hanya versi yang sepenuhnya portabel. Dengan bashitu dapat dilakukan dengan lebih sedikit dan dengan menerapkan shell builtins saja - yang mungkin diinginkan ketika Anda menganggap bahwa ini adalah perintah yang akan berjalan setiap kali Anda menekan [ENTER]. Berikut ini dua cara:

_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

Atau, menggunakan bash's historyperintah, Anda dapat menentukan _HISTfungsi dengan cara ini:

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

Output untuk kedua metode juga terlihat seperti: <hist#>\t[%F %T]\t<hist_cmd>\nmeskipun historymetode ini menyertakan beberapa spasi putih terkemuka. Namun, saya percaya historycap waktu metode akan lebih akurat karena saya pikir mereka tidak perlu menunggu perintah yang dirujuk untuk menyelesaikan sebelum mendapatkan cap mereka.

Anda dapat menghindari pelacakan keadaan apa pun dalam kedua kasus jika hanya Anda yang entah bagaimana menyaring aliran uniq- seperti yang mungkin Anda lakukan dengan yang mkfifosaya sebutkan sebelumnya.

Tetapi melakukannya di prompt seperti ini berarti selalu diperbarui hanya jika perlu hanya dengan tindakan memperbarui prompt. Itu mudah.

Anda mungkin juga melakukan sesuatu yang mirip dengan apa yang Anda lakukan tailtetapi lebih pada pengaturan

HISTFILE=${TGT_PTY}
mikeserv
sumber
Saya sebenarnya mengeditnya di tab lain ... Tolong, lebih banyak waktu lagi?
mikeserv
Sebenarnya saya akan menekan save sekarang juga, @ illuminÉ - tetapi Anda akan melihat di mana saya tinggalkan ...
mikeserv
@ illuminÉ - Ngomong-ngomong - dan saya harap saya benar tentang ini - saya berasumsi Anda memasukkan perintah seperti yang tertulis ${TGT_PTY}dan semuanya? Jika demikian, itu akan menjelaskan 'pengarahan ambigu' karena itu akan menjadi variabel kosong. Anda membutuhkan file. /dev/pts/[num]dalam semua kemungkinan -
mikeserv
Itu berhasil! Printscreen membantu! Saya berharap blok kode Anda yang pertama dan satu-satunya adalah apa yang Anda masukkan ke dalam printscreen dan referensi yang jelas untuk memilih poin yang Anda inginkan untuk output dan memasukkan tab ke dalam fungsi - tidak ada lagi yang diperlukan. Menggunakan variabel untuk menggambarkan sesuatu yang harus saya masukkan secara manual untuk mencobanya dengan cepat bukan praktik yang baik menurut saya karena Anda harus menggali ke dalam teks untuk mencari tahu semuanya, yang mana solusinya solusi. Juga semua yang terbaik untuk Anda dan keluarga Anda.
@ illuminÉ - lupakan tentang catsaya paranoid. Ini berfungsi dengan baik - bahkan 12 jam kemudian.
mikeserv
4

Jangan ragu untuk bermain dengan pemformatan, tetapi ini (saya percaya) melakukan apa yang Anda minta ... simpan ke suatu tempat di PATH Anda, jadikan dieksekusi dan nikmati:

#!/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}}'

Saya yakin itu bisa dioptimalkan, tetapi Anda mendapatkan idenya.

Penjelasan singkat: karena ~ / .bash_history tidak melacak hitungan, pertama-tama kita menentukan jumlah entri. Kemudian sedikit sihir awk untuk mendapatkan pemformatan yang benar, dan melacak jumlah entri.

Tink
sumber
Itu tidak berfungsi jika ada entri multi-baris. Juga tail -fakan membaca 10 baris awalnya yang sudah termasuk dalam Anda count. Ini mengasumsikan tanggal GNU dalam lingkungan non-POSIX (POSIXLY_CORRECT tidak disetel). Ini menjalankan satu shell dan satu perintah tanggal per cap waktu.
Stéphane Chazelas
@StephaneChazelas Untuk entri multi-baris, pada pengaturan saya mereka tampaknya mendaftar dengan kedua solusi. Sesuatu dalam pengaturan saya mungkin?
1
@ illuminÉ, tink countmenghitung separuh baris .bash_history, dan kemudian peningkatan untuk setiap baris yang tidak dimulai #, sehingga penghitungan riwayat yang dilaporkan cenderung salah. Menggunakan count=$(grep -c '^#' ...)mungkin akan lebih baik, tetapi dalam hal apa pun, angka-angka sejarah itu cenderung berakhir tidak sinkron terutama jika Anda memiliki lebih dari 2 bash berjalan pada saat yang sama.
Stéphane Chazelas
@StephaneChazelas Hormat saya karena saya tidak dapat sepenuhnya menghargai solusi mana pun. Saya sangat berterima kasih atas penjelasannya! Memang hitungannya berbeda, konsekuensinya jauh lebih tinggi di sini ... Saya bisa melihat Anda membangun solusi Anda sendiri di sekitar strftime, yang pada dasarnya adalah apa yang historymemanfaatkan perintah.
1
Terima kasih atas umpan balik @StephaneChazelas, saya akan melihat apakah saya dapat mengatasi itu :)
tink