Bash histori: "ignoredups" dan "erasedups" mengatur konflik dengan riwayat umum di seluruh sesi


85

Pertama-tama, ini bukan duplikat utas yang ada di SE. Saya telah membaca dua utas ini ( 1 , 2 ) tentang sejarah bash yang lebih baik, tetapi tidak ada jawaban yang berhasil - - Saya menggunakan Fedora 15.

Saya menambahkan yang berikut ke .bashrcfile di direktori pengguna (/ home / aahan /), dan tidak berfungsi. Adakah yang punya petunjuk?

HISTCONTROL=ignoredups:erasedups  # no duplicate entries
HISTSIZE=1000                     # custom history size
HISTFILESIZE=100000                 # custom history file size
shopt -s histappend                      # append to history, don't overwrite it
PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"  # Save and reload the history after each command finishes

Oke inilah yang saya inginkan dengan bash history (prioritas):

  • jangan menyimpan duplikat, hapus yang sudah ada
  • segera bagikan riwayat dengan semua terminal terbuka
  • selalu menambahkan riwayat, bukan menimpanya
  • menyimpan perintah multi-line sebagai satu perintah (yang tidak aktif secara default)
  • apa ukuran Riwayat standar dan ukuran file riwayat?

menabrak! siapa saja? Tidak ada yang berhasil. Mungkinkah ini masalah dengan Fedora 15 (dengan Gnome 3 dan berjalan pada mesin virtual host Windows)?
its_me

Tolong jangan "menabrak" posting di sini. Jika mereka tidak dijawab, Anda perlu bertanya lebih jelas, sertakan petunjuk konteks yang tepat, atau sesuatu. Jika Anda membutuhkan perhatian pada posting Anda, Anda bisa mengeluarkan hadiah yang membantu lebih banyak orang membayar mereka lebih banyak perhatian.
Caleb

1
Apakah Anda yakin menggunakan bash? ( echo $SHELL). Apakah pengaturan bekerja jika Anda menjalankannya secara manual dari shell terbuka Anda? Jelas karena mereka bekerja untuk banyak orang lain pengaturannya benar, Anda hanya salah mengimplementasikannya. Dan tidak ada Fedora15 / Gnome3 / menjadi mesin virtual tidak ada hubungannya dengan fungsi sebenarnya bash.
Caleb

@ Caleb, pertama maaf tentang menabrak posting. Juga, saya mencoba yang terbaik untuk menjadi sangat jelas. Tidak, saya belum mengeluarkannya di shell. Saya hanya menyalin-menempelkannya ke .bashrcfile. Apakah itu salah? Bisakah Anda menambahkan "jawaban" ke posting ini dengan perintah shell yang sebenarnya? (tolong tahan dengan noob-ity saya.)
its_me

1
Semua yang Anda tulis dalam skrip atau .bashrcADALAH perintah shell aktual. Skrip hanyalah serangkaian perintah shell. Juga edit yang baru-baru ini Anda buat menghapus exportbit adalah ide yang buruk, yang harus disimpan.
Caleb

Jawaban:


120

Ini sebenarnya adalah perilaku yang sangat menarik dan saya akui saya telah terlalu meremehkan pertanyaan di awal. Tapi pertama-tama faktanya:

1. Apa yang berhasil

Fungsionalitas dapat dicapai dalam beberapa cara, meskipun masing-masing bekerja sedikit berbeda. Perhatikan bahwa, dalam setiap kasus, untuk memiliki sejarah "ditransfer" ke terminal lain (diperbarui), kita harus menekan Enterdi terminal, di mana ia ingin mengambil sejarah.

  • Pilihan 1:

    shopt -s histappend
    HISTCONTROL=ignoredups
    PROMPT_COMMAND="history -a; history -n; $PROMPT_COMMAND"

    Ini memiliki dua kelemahan:

    1. Saat masuk (membuka terminal), perintah terakhir dari file histori dibaca dua kali ke buffer sejarah terminal saat ini;
    2. Buffer terminal yang berbeda tidak tetap sinkron dengan file histori.
  • pilihan 2:

    HISTCONTROL=ignoredups
    PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"

    (Ya, tidak perlu shopt -s histappenddan ya, harus history -c di tengah-tengah PROMPT_COMMAND) Versi ini juga memiliki dua kelemahan penting:

    1. File riwayat harus diinisialisasi. Itu harus mengandung setidaknya satu baris non-kosong (bisa apa saja).
    2. The historyperintah dapat memberikan output yang salah - lihat di bawah.

[Sunting] "Dan pemenangnya adalah ..."

  • opsi 3:

    HISTCONTROL=ignoredups:erasedups
    shopt -s histappend
    PROMPT_COMMAND="history -n; history -w; history -c; history -r; $PROMPT_COMMAND"

    Ini sejauh yang didapat. Ini adalah satu - satunya pilihan untuk memiliki keduanya erasedupsdan riwayat umum yang bekerja secara bersamaan. Ini mungkin solusi terakhir untuk semua masalahmu, Aahan.


2. Mengapa opsi 2 tampaknya tidak berfungsi (atau: apa yang sebenarnya tidak berfungsi seperti yang diharapkan)?

Seperti yang saya sebutkan, masing-masing solusi di atas bekerja secara berbeda. Tetapi interpretasi yang paling menyesatkan tentang bagaimana pengaturan bekerja berasal dari menganalisis output dari historyperintah . Dalam banyak kasus, perintah dapat memberikan output yang salah . Mengapa? Karena itu dieksekusi sebelum urutan historyperintah lain yang terkandung dalam PROMPT_COMMAND! Namun, ketika menggunakan opsi kedua atau ketiga, seseorang dapat memantau perubahan .bash_historykonten (menggunakan watch -n1 "tail -n20 .bash_history"misalnya) dan melihat apa sejarah sebenarnya.

3. Mengapa opsi 3 begitu rumit?

Itu semua terletak pada cara erasedupskerjanya. Seperti yang dinyatakan oleh manual bash, "(...) erasedupsmenyebabkan semua baris sebelumnya yang cocok dengan baris saat ini dihapus dari daftar riwayat sebelum baris itu disimpan" . Jadi ini benar-benar yang diinginkan OP (dan bukan hanya, seperti yang saya pikirkan sebelumnya, agar tidak ada duplikat yang muncul secara berurutan ) . Inilah mengapa masing-masing history -.perintah harus atau tidak bisa di PROMPT_COMMAND:

  • history -n memiliki untuk berada di sana sebelum history -wmembaca dari .bash_historyperintah disimpan dari setiap terminal lain,

  • history -w memiliki untuk berada di sana untuk menyelamatkan sejarah untuk mengajukan dan menghapus duplikat,

  • history -a tidak boleh ditempatkan di sana, bukan history -w, karena itu tidak memicu menghapus duplikat,

  • history -cjuga diperlukan karena mencegah membuang buffer sejarah setelah setiap perintah,

  • dan akhirnya, history -ryang diperlukan untuk mengembalikan buffer sejarah dari file, sehingga akhirnya membuat sejarah bersama seluruh sesi terminal.


2
+1 untuk "Tetapi interpretasi yang paling menyesatkan tentang bagaimana pengaturan bekerja berasal dari menganalisis output dari perintah sejarah." Saya pikir ini adalah inti dari masalah OP. Deduksi yang sangat baik.
jasonwryan

2
Saya akhirnya melihat maksud Anda. Dengan 'duplikat' Anda berarti sesuatu yang lain dari saya. Saya hanya fokus pada urutan dari perintah yang sama . Diperbarui jawaban saya - lihat "opsi 3". Juga, untuk kasus Anda, untuk menguji bagaimana sejarah bekerja, Anda harus benar - benar menggunakannya watch "tail -n 20 .bash_history"daripada tail -f .bash_history.
rozcietrzewiacz

2
Opsi 3 baru saja menghapus semua 100.000 baris sejarah saya :( (bash 3.2.25 (1) -release)
Felipe Alvarez

2
history -cjuga diperlukan karena mencegah membuang buffer sejarah setelah setiap perintah. Mengapa pemotongan ini terjadi?
Piotr Dobrogost

2
Solusi Anda 3 sebenarnya tidak berfungsi. Ini sangat buggy dan tergantung pada urutan pengguna menekan enter di terminal yang berbeda. Ini akan sering menghasilkan perintah yang hilang, terutama ketika berpindah-pindah antar terminal. Sumber: mencobanya.
6cef

8

Dalam perintah prompt Anda, Anda menggunakan -csakelar. Dari man bash:

-c   Bersihkan daftar riwayat dengan menghapus semua entri

Untuk membagikan riwayat Anda dengan semua terminal terbuka, Anda dapat menggunakan -n:

-n   Baca baris riwayat yang belum dibaca dari file riwayat ke dalam daftar riwayat saat ini. Ini adalah baris yang ditambahkan ke file histori sejak awal sesi bash saat ini.

Ukuran standar juga ada di manual:

HISTSIZE Jumlah perintah yang harus diingat dalam histori perintah (lihat SEJARAH di bawah). Nilai standarnya adalah 500.

Untuk menyimpan perintah multi-line:

The cmdhist pilihan shell, jika diaktifkan, menyebabkan shell untuk mencoba untuk menyimpan setiap baris perintah multi-baris dalam entri sejarah yang sama, menambahkan titik koma di mana diperlukan untuk mempertahankan kebenaran sintaksis. The lithist pilihan shell menyebabkan shell untuk menyimpan perintah dengan baris baru tertanam bukan titik koma.

Selain itu, Anda tidak boleh mengawali perintah HIST * dengan export- itu adalah variabel bash-only bukan variabel lingkungan: HISTCONTROL=ignoredups:erasedupssudah cukup.


Jadi, ini export PROMPT_COMMAND="history -a; history -n; history -r; $PROMPT_COMMAND"benar? Juga, apakah Anda tahu bagaimana saya bisa membuat file riwayat menyimpan perintah multi-line sebagai satu perintah (yang tidak aktif secara default) ??
its_me

1
Iya. Sesuai halaman manual yang dikutip di atas, shopt -s cmdhistakan menghemat banyak baris.
jasonwryan

oke, saya coba semuanya. Sepertinya HISTCONTROL=ignoredups:erasedupstidak berfungsi. Saya juga mencoba hanya dengan itu di .bashrcfile (antara fungsi kustom). Adakah yang tahu apa yang salah? Saya menggunakan Fedora 15 pada mesin virtual - - host Windows 7.
its_me

Pastikan tidak ada apa pun di file startup lain, seperti /etc/bashrc, .bash_profiledll yang menimpanya.
jasonwryan

Saya tidak melihat ada masalah dengan .bash_profilefile tersebut. Adapun konten di /etc/bashrcsaya taruh di sini, silakan lihat - pastebin.com/Uae6sE6s
its_me

8

Inilah yang saya buat dan saya senang dengan itu sejauh ini ...

alias hfix='history -n && history | sort -k2 -k1nr | uniq -f1 | sort -n | cut -c8- > ~/.tmp$$ && history -c && history -r ~/.tmp$$ && history -w && rm ~/.tmp$$'  
HISTCONTROL=ignorespace  
shopt -s histappend  
shopt -s extglob  
HISTSIZE=1000  
HISTFILESIZE=2000  
export HISTIGNORE="!(+(*\ *))"  
PROMPT_COMMAND="hfix; $PROMPT_COMMAND" 

CATATAN:

  • Ya, itu rumit ... tetapi, ia menghapus semua duplikat dan tetap mempertahankan kronologi di dalam setiap terminal!
  • Saya HISTIGNOREmengabaikan semua perintah yang tidak memiliki argumen. Ini mungkin tidak diinginkan oleh beberapa orang dan dapat ditinggalkan.

1

Gunakan ini sebagai gantinya:

HISTCONTROL=ignoreboth

0

Ini tidak berfungsi, karena Anda lupa tentang:

 -n   read all history lines not already read from the history file
      and append them to the history list

Tapi sepertinya history -nhanya buggy saat export HISTCONTROL=ignoreboth:erasedupsberlaku.

Mari kita bereksperimen:

$ PROMPT_COMMAND=
$ export HISTCONTROL=ignoreboth:erasedups
$ export HISTFILE=~/.bash_myhistory
$ HISTIGNORE='history:history -w'
$ history -c
$ history -w

Di sini kita menghidupkan dups menghapus, beralih histori ke file kustom, menghapus histori. Setelah semua perintah selesai, kami memiliki file riwayat kosong dan satu perintah di riwayat saat ini.

$ history
$ cat ~/.bash_myhistory
$ history
$ 1  [2019-06-17 14:57:19] cat ~/.bash_myhistory

Buka terminal kedua dan jalankan enam perintah itu juga. Setelah itu:

$ echo "X"
$ echo "Y"
$ history -w
$ history
  1  [2019-06-17 15:00:21] echo "X"
  2  [2019-06-17 15:00:23] echo "Y"

Sekarang riwayat Anda saat ini memiliki dua perintah dan file riwayat memiliki:

#1560772821
echo "X"
#1560772823
echo "Y"

Kembali ke terminal pertama:

$ history -n
$ history
1  [2019-06-17 14:57:19] cat ~/.bash_myhistory 
2  [2019-06-17 15:03:12] history -n

Hah ... tidak ada echoperintah yang dibaca. Beralih lagi ke terminal kedua dan:

$ echo "Z"
$ history -w

Sekarang file histori adalah:

#1560772821
echo "X"
#1560772823
echo "Y"
#1560773057
echo "Z"

Beralih ke terminal pertama lagi:

$ history -n
$ history
  1  [2019-06-17 14:57:19] cat ~/.bash_myhistory 
  2  [2019-06-17 15:03:12] history -n
echo "Z"

Anda dapat melihat bahwa echo "Z"perintah digabungkan ke history -n.

Bug lain adalah karena perintah dibaca dari sejarah dengan nomor perintah dan bukan oleh waktu perintah, saya pikir. Saya berharap echoperintah lain muncul di histori


0

erasedups tidak memangkas (seperti chomp dalam beberapa bahasa) ruang utama dan tambahan. Ini adalah bug. Juga terhapus tidak menghapus semua entri sebelumnya.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.