Historia Bash: „ignorowane” i „wymazywane” powodują konflikt ze wspólną historią dla różnych sesji

84

Po pierwsze, nie jest to duplikat istniejących wątków na SE. Przeczytałem te dwa wątki ( 1. , 2. ) o lepszej historii basha, ale żadna z odpowiedzi nie działa - - tak przy okazji, jestem na Fedorze 15.

Dodałem następujące do .bashrcpliku w katalogu użytkownika (/ home / aahan /) i to nie działa. Czy ktoś ma jakiś pomysł?

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

Okej, właśnie tego chcę z historią bash (priorytet):

  • nie przechowuj duplikatów, usuń wszystkie istniejące
  • natychmiast udostępniaj historię wszystkim otwartym terminalom
  • zawsze dołączaj historię, a nie zastępuj ją
  • przechowuj polecenia wieloliniowe jako pojedyncze polecenie (domyślnie wyłączone)
  • jaki jest domyślny rozmiar Historii i rozmiar pliku historii?
to ja
źródło
guz! ktoś? Nic nie działa. Czy może to być problem z Fedorą 15 (z Gnome 3 i działającą na maszynie wirtualnej hosta Windows)?
its_me
Proszę nie „podbijać” postów tutaj. Jeśli nie otrzymają odpowiedzi, musisz zapytać jaśniej, podać odpowiednie fragmenty wskazówek kontekstowych lub coś w tym rodzaju. Jeśli potrzebujesz uwagi do swoich postów, możesz wystawiać nagrody, które pomogą przyciągnąć uwagę większej liczby osób.
Caleb
1
Czy na pewno używasz bash? ( echo $SHELL). Czy ustawienia działają, jeśli uruchomisz je ręcznie z otwartej powłoki? Oczywiście, ponieważ działają one dla tak wielu innych ustawień, są prawidłowe, po prostu źle je wdrażasz. I żadna Fedora15 / Gnome3 / będąca maszyną wirtualną nie ma wiele wspólnego z faktyczną funkcją bash.
Caleb
@Caleb, najpierw przepraszam, że wpadłem na post. Poza tym starałem się, aby wszystko było jasne. Nie, nie wydałem ich w powłoce. Właśnie skopiowałem je do .bashrcpliku. Czy to źle? Czy możesz dodać „odpowiedź” do tego postu za pomocą rzeczywistych poleceń powłoki? (proszę znoszą moją
noobowość
1
Wszystko, co piszesz w skryptach lub .bashrcJEST rzeczywistymi poleceniami powłoki. Skrypty to tylko seria poleceń powłoki. Również edytowanie, które ostatnio usunąłeś, exportbyło złym pomysłem, który należy zachować.
Caleb

Odpowiedzi:

119

To jest naprawdę bardzo interesujące zachowanie i przyznaję, że na początku nie doceniłem tego pytania. Ale najpierw fakty:

1. Co działa

Funkcjonalność można uzyskać na kilka sposobów, ale każdy działa nieco inaczej. Zauważ, że w każdym przypadku, aby historia została „przeniesiona” do innego terminala (zaktualizowana), należy nacisnąć Enterterminal, w którym chce on odzyskać historię.

  • opcja 1:

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

    Ma to dwie wady:

    1. Podczas logowania (otwieranie terminala) ostatnie polecenie z pliku historii jest odczytywane dwukrotnie do bufora historii bieżącego terminala;
    2. Bufory różnych terminali nie są zsynchronizowane z plikiem historii.
  • Opcja 2:

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

    (Tak, nie ma potrzeby shopt -s histappendi tak, musi być history -c w środku PROMPT_COMMAND) Ta wersja ma również dwie ważne wady:

    1. Plik historii musi zostać zainicjowany. Musi zawierać co najmniej jedną niepustą linię (może być dowolna).
    2. historyKomenda może dać fałszywe wyjścia - patrz poniżej.

[Edytuj] „A zwycięzcą jest ...”

  • opcja 3:

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

    To jest tak daleko, jak to możliwe. Jest to jedyna opcja, aby erasedupshistoria i wspólna historia działały jednocześnie. To prawdopodobnie ostateczne rozwiązanie wszystkich twoich problemów, Aahan.


2. Dlaczego opcja 2 wydaje się nie działać (lub: co tak naprawdę nie działa zgodnie z oczekiwaniami)?

Jak wspomniałem, każde z powyższych rozwiązań działa inaczej. Ale najbardziej myląca interpretacja działania ustawień wynika z analizy wyników historypolecenia . W wielu przypadkach polecenie może dawać fałszywe dane wyjściowe. Dlaczego? Ponieważ jest wykonywany przed sekwencją innych historypoleceń zawartych w PROMPT_COMMAND! Jednak podczas korzystania z drugiej lub trzeciej opcji można monitorować zmiany .bash_historytreści (używając watch -n1 "tail -n20 .bash_history"na przykład) i zobaczyć, jaka jest prawdziwa historia.

3. Dlaczego opcja 3 jest tak skomplikowana?

Wszystko zależy od sposobu erasedupsdziałania. Jak stwierdza instrukcja bash, „(...) erasedupspowoduje usunięcie wszystkich poprzednich wierszy pasujących do bieżącego wiersza z listy historii przed zapisaniem tego wiersza” . Więc to jest naprawdę to, co chciał OP (a nie tylko, jak wcześniej sądzono, nie mieć duplikaty występujące w sekwencji ) . Oto dlaczego każde z history -.poleceń musi albo nie może znajdować się w PROMPT_COMMAND:

  • history -n musi być tam przed history -wodczytem .bash_historypoleceń zapisanych z dowolnego innego terminala,

  • history -w ma być tam, aby zapisać historię do pliku i usunąć duplikaty,

  • history -a nie należy go tam umieszczać zamiast history -w, ponieważ nie powoduje to usunięcia duplikatów,

  • history -cjest również potrzebny, ponieważ zapobiega usuwaniu bufora historii po każdym poleceniu,

  • i wreszcie history -rjest potrzebny do przywrócenia bufora historii z pliku, a tym samym do udostępnienia historii między sesjami terminalowymi.

rozcietrzewiacz
źródło
2
+1 za „Ale najbardziej myląca interpretacja działania ustawień wynika z analizy wyników polecenia historii”. Myślę, że to jest sedno problemu PO. Doskonałe odliczenie.
jasonwryan
2
W końcu zrozumiałem twój punkt widzenia. Przez „duplikaty” miałeś na myśli coś innego niż ja. Skupiłem się tylko na sekwencjach tego samego polecenia . Zaktualizowałem moją odpowiedź - patrz „opcja 3”. Również w twoim przypadku, aby przetestować działanie historii, powinieneś faktycznie użyć watch "tail -n 20 .bash_history"zamiast tail -f .bash_history.
rozcietrzewiacz
2
Opcja 3 właśnie wymazała wszystkie moje 100 000 linii historii :( (wydanie bash 3.2.25 (1))
Felipe Alvarez
2
history -cjest również potrzebny, ponieważ zapobiega usuwaniu do pamięci bufora historii po każdym poleceniu. Dlaczego takie śmieci występują?
Piotr Dobrogost
2
Twoje rozwiązanie 3 faktycznie nie działa. Jest bardzo wadliwy i zależy od kolejności, w której użytkownicy trafiają do różnych terminali. Często powoduje to utratę poleceń, szczególnie przy przełączaniu między terminalami. Źródło: wypróbowałem.
6cef
8

W poleceniu polecenia używasz -cprzełącznika. Od man bash:

-c   Wyczyść listę historii, usuwając wszystkie wpisy

Aby udostępnić swoją historię wszystkim otwartym terminalom, możesz użyć -n:

-n Wczytaj   wiersze historii, które nie zostały jeszcze odczytane z pliku historii do bieżącej listy historii. Są to linie dołączane do pliku historii od początku bieżącej sesji bash.

Rozmiar domyślny znajduje się również w instrukcji:

HISTSIZE Liczba poleceń do zapamiętania w historii poleceń (patrz HISTORIA poniżej). Wartość domyślna to 500.

Aby zapisać polecenia wieloliniowe:

Cmdhist opcja powłoki, jeśli jest włączona, powoduje, że powłoka próbować zapisać każdy wiersz polecenia wielowierszowego w tej samej pozycji historii, dodając średników gdzie niezbędne dla zachowania poprawności składniowej. Lithist opcja powłoka powoduje, że powłoka będzie zachowywać polecenia z osadzonymi znakami nowej linii zamiast średników.

Ponadto nie powinieneś poprzedzać poleceń HIST * export- są to zmienne tylko bash, a nie zmienne środowiskowe: HISTCONTROL=ignoredups:erasedupsjest wystarczające.

jasonwryan
źródło
Czy to export PROMPT_COMMAND="history -a; history -n; history -r; $PROMPT_COMMAND"prawda? Czy wiesz także, jak mogę sprawić, by plik historii przechowywał polecenia wieloliniowe jako jedno polecenie (które jest domyślnie wyłączone)?
its_me
1
Tak. Zgodnie z cytowaną powyżej stroną podręcznika shopt -s cmdhistzapisuje się wiele wierszy.
jasonwryan
okej, próbowałem ich wszystkich. Wygląda na HISTCONTROL=ignoredups:erasedupsto, że nie działa. Próbowałem też mieć to w .bashrcpliku (wśród funkcji niestandardowych). Masz pojęcie, co może być nie tak? Używam Fedory 15 na maszynie wirtualnej - na hoście Windows 7.
its_me
Upewnij się, że w innych plikach startowych, takich jak itp., Nie ma nic /etc/bashrc, co .bash_profileby je przesłaniało.
jasonwryan
Nie widzę żadnych problemów z .bash_profileplikiem. Jeśli chodzi o treść, /etc/bashrcktórą tu umieściłem, proszę spojrzeć - pastebin.com/Uae6sE6s
its_me
8

Właśnie to wymyśliłem i jak dotąd jestem z tego zadowolony…

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" 

UWAGI:

  • Tak, to skomplikowane ... ale usuwa wszystkie duplikaty, a jednocześnie zachowuje chronologię w każdym terminalu!
  • Mój HISTIGNOREignoruje wszystkie polecenia, które nie mają argumentów. Może to nie być pożądane przez niektórych ludzi i może zostać pominięte.
użytkownik41176
źródło
1

Użyj tego zamiast:

HISTCONTROL=ignoreboth
verndog
źródło
0

To nie działa, ponieważ zapominasz o:

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

Ale wydaje się, że history -njest po prostu błędny, gdy export HISTCONTROL=ignoreboth:erasedupsdziała.

Pozwala eksperymentować:

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

Tutaj włączamy usuwanie duplikatów, przełączamy historię na niestandardowy plik, usuwamy historię. Po zakończeniu wszystkich poleceń mamy pusty plik historii i jedno polecenie w bieżącej historii.

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

Otwórz drugi terminal i uruchom te sześć poleceń. Po tym:

$ 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"

Teraz twoja historia ma dwa polecenia, a plik historii ma:

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

Powrót do pierwszego terminala:

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

Huh ... żadne z echopoleceń nie zostało odczytane. Przełącz ponownie na drugi terminal i:

$ echo "Z"
$ history -w

Teraz plik historii to:

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

Przełącz ponownie na pierwszy terminal:

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

Możesz zobaczyć, że echo "Z"polecenie zostało scalone history -n.

Innym błędem jest to, że polecenia są odczytywane z historii według numeru polecenia, a nie według czasu polecenia, tak myślę. Oczekuję, że echow historii pojawiły się inne polecenia

Eugen Konkov
źródło
0

skasowane nie usuwa przycinania (jak chomp w niektórych językach) początkowych i końcowych spacji. To jest błąd. Usunięte dane również nie usuwają wszystkich poprzednich wpisów.

Sajith Nallithodi
źródło