Làm cách nào tôi có thể xóa các bản sao trong .bash_history, giữ trật tự?


60

Tôi thực sự thích sử dụng control+rđể tìm kiếm đệ quy lịch sử lệnh của tôi. Tôi đã tìm thấy một vài lựa chọn tốt mà tôi muốn sử dụng với nó:

# ignore duplicate commands, ignore commands starting with a space
export HISTCONTROL=erasedups:ignorespace

# keep the last 5000 entries
export HISTSIZE=5000

# append to the history instead of overwriting (good for multiple connections)
shopt -s histappend

Vấn đề duy nhất đối với tôi là erasedupschỉ xóa các bản sao liên tiếp - do đó với chuỗi lệnh này:

ls
cd ~
ls

Các lslệnh thực sự sẽ được ghi lại hai lần. Tôi đã nghĩ về việc chạy định kỳ w / cron:

cat .bash_history | sort | uniq > temp.txt
mv temp.txt .bash_history

Điều này sẽ đạt được loại bỏ các bản sao, nhưng tiếc là thứ tự sẽ không được bảo tồn. Nếu tôi không sorttập tin trước, tôi không tin uniqcó thể hoạt động bình thường.

Làm cách nào tôi có thể xóa các bản sao trong .bash_history, giữ trật tự?

Tín dụng thêm:

Có bất kỳ vấn đề với việc ghi đè .bash_historytập tin thông qua một tập lệnh? Ví dụ: nếu bạn xóa tệp nhật ký apache, tôi nghĩ rằng bạn cần gửi tín hiệu nohup / reset killđể nó xóa kết nối của nó với tệp. Nếu đó là trường hợp của .bash_historytệp, có lẽ bằng cách nào đó tôi có thể sử dụng psđể kiểm tra và đảm bảo không có phiên nào được kết nối trước khi tập lệnh lọc được chạy?


3
Hãy thử ignoredupsthay vì erasedupsmột lúc và xem cách nó làm việc cho bạn.
jw013

1
Tôi không nghĩ rằng bash giữ một tay cầm tập tin mở cửa cho các tập tin lịch sử - nó đọc / viết nó khi nó cần phải, vì vậy nó nên (lưu ý - nên - tôi đã không kiểm tra) được an toàn ghi đè lên nó từ nơi khác.
D_Bye

1
Tôi vừa học được một điều mới trong câu đầu tiên của câu hỏi của bạn. Thủ thuật hay!
Ricardo

Tôi không tìm thấy trang man cho tất cả các tùy chọn cho historylệnh. Tôi nên tìm ở đâu?
Jonathan Hartley

Các tùy chọn lịch sử nằm trong phần 'man bash', tìm kiếm phần 'lệnh dựng sẵn shell', sau đó tìm 'history' bên dưới đó.
Jonathan Hartley

Câu trả lời:


36

Sắp xếp lịch sử

Lệnh này hoạt động như thế nào sort|uniq, nhưng giữ cho các dòng ở đúng vị trí

nl|sort -k 2|uniq -f 1|sort -n|cut -f 2

Về cơ bản, chuẩn bị cho mỗi dòng số của nó. Sau khi sort|uniq-ing, tất cả các dòng được sắp xếp lại theo thứ tự ban đầu của chúng (sử dụng trường số dòng) và trường số dòng được xóa khỏi các dòng.

Giải pháp này có một lỗ hổng là không xác định được đại diện nào của một lớp các đường bằng nhau sẽ làm cho nó ở đầu ra và do đó vị trí của nó trong đầu ra cuối cùng không được xác định. Tuy nhiên, nếu chọn đại diện mới nhất, bạn có thể sortnhập dữ liệu bằng khóa thứ hai:

nl|sort -k2 -k 1,1nr|uniq -f1|sort -n|cut -f2

Quản lý .bash_history

Để đọc lại và viết lại lịch sử, bạn có thể sử dụng history -ahistory -wtương ứng.


6
Một phiên bản trang trí sắp xếp không trang trí , được thực hiện với các công cụ shell. Đẹp.
ire_and_curses

Với sort, công -rtắc luôn đảo ngược thứ tự sắp xếp. Nhưng điều này sẽ không mang lại kết quả mà bạn có trong tâm trí. sortliên quan đến hai lần xuất hiện lsgiống hệt nhau với kết quả là, ngay cả khi đảo ngược, thứ tự cuối cùng phụ thuộc vào thuật toán sắp xếp. Nhưng xem cập nhật của tôi cho một ý tưởng khác.
artistoex

1
Trong trường hợp, bạn không muốn sửa đổi .bash_history, bạn có thể đặt phần sau vào .bashrc: alias history = 'history | sắp xếp -k2 -k 1,1nr | uniq -f 1 | sắp xếp -n '
Nathan

Là gì nlvào lúc bắt đầu của mỗi dòng mã? Có nên không history?
AL

1
@AL nl thêm số dòng. Toàn bộ lệnh giải quyết vấn đề chung: loại bỏ trùng lặp trong khi duy trì trật tự. Đầu vào được đọc từ stdin.
artistoex 5/2/2015

48

Vì vậy, tôi đã tìm kiếm điều tương tự chính xác sau khi bị làm phiền bởi các bản sao và thấy rằng nếu tôi chỉnh sửa ~ / .bash_profile (Mac) của mình bằng:

export HISTCONTROL=ignoreboth:erasedups

Nó thực hiện chính xác những gì bạn muốn, nó chỉ giữ lệnh mới nhất. ignoreboththực sự giống như làm ignorespace:ignoredupsvà điều đó cùng với erasedupsviệc hoàn thành công việc.

Ít nhất trên thiết bị đầu cuối Mac của tôi với bash công việc này hoàn hảo. Tìm thấy nó ở đây trên Askubfox.com .


10
đây phải là câu trả lời đúng
MitchBroadhead 3/03/2016

đã thử nghiệm trên Max OS X Yosemite và trên Ubuntu 14_04
Ricardo

1
đồng ý với @MitchBroadhead. điều này giải quyết vấn đề trong chính bash, mà không cần cron-job bên ngoài. đã thử nghiệm nó trên Ubuntu 17.04 và 16.04 LTS
Georg Jung

hoạt động trên OpenBSD. Nó chỉ loại bỏ các dups của bất kỳ lệnh nào mà nó đang nối vào tệp lịch sử, điều này rất tốt cho tôi. Nó có tác dụng thú vị là rút ngắn tệp lịch sử khi tôi nhập các lệnh đã tồn tại dưới dạng trùng lặp trước đó. Bây giờ tôi có thể làm cho tập tin lịch sử của tôi tối đa ngắn hơn.
WeakPulum

1
Điều này chỉ bỏ qua các lệnh trùng lặp, liên tiếp. Nếu bạn luân phiên lặp đi lặp lại giữa hai lệnh đã cho, lịch sử bash của bạn sẽ lấp đầy với các bản sao
Dylanthepiguy

16

Tìm thấy giải pháp này trong tự nhiên và đã thử nghiệm:

awk '!x[$0]++'

Lần đầu tiên nhìn thấy giá trị cụ thể của một dòng ($ 0), giá trị của x [$ 0] bằng không.
Giá trị của 0 được đảo ngược với !và trở thành một.
Một câu lệnh đánh giá một nguyên nhân gây ra hành động mặc định, đó là in.

Do đó, lần đầu tiên một cụ thể $0được nhìn thấy, nó được in.

Mỗi lần tiếp theo (lặp lại) giá trị của x[$0]đã được tăng lên,
giá trị bị phủ định của nó là 0 và một câu lệnh ước tính bằng 0 không được in.

Để giữ giá trị lặp lại cuối cùng, đảo ngược lịch sử và sử dụng cùng một awk:

awk '!x[$0]++' ~/.bash_history                 # keep the first value repeated.

tac ~/.bash_history | awk '!x[$0]++' | tac     # keep the last.

Ồ Điều đó chỉ làm việc. Nhưng nó loại bỏ tất cả trừ sự xuất hiện đầu tiên tôi đoán. Tôi đã đảo ngược thứ tự của các dòng bằng Sublime Text trước khi chạy nó. Bây giờ tôi sẽ đảo ngược nó một lần nữa để có được một lịch sử sạch sẽ chỉ với sự xuất hiện cuối cùng của tất cả các bản sao bị bỏ lại. Cảm ơn bạn.
trss

Kiểm tra câu trả lời của tôi!
Ali Shakiba

Câu trả lời rõ ràng và tổng quát (không giới hạn trong trường hợp sử dụng lịch sử) mà không khởi chạy quy trình phụ bazilion ;-)
JepZ

9

Mở rộng câu trả lời của Clayton:

tac $HISTFILE | awk '!x[$0]++' | tac | sponge $HISTFILE

tacđảo ngược tệp, đảm bảo bạn đã cài đặt moreutilsđể bạn có spongesẵn, nếu không hãy sử dụng tệp tạm thời.


1
Đối với những người trên Mac, hãy sử dụng brew install coreutilsvà lưu ý rằng tất cả các tiện ích GNU đều có gtrước để tránh nhầm lẫn với các lệnh Mac tích hợp BSD (ví dụ: gsed là GNU trong khi sed là BSD). Vì vậy, sử dụng gtac.
tralston

Tôi cần lịch sử -c và lịch sử -r để có được nó để sử dụng lịch sử
drescherjm

4

Chúng sẽ giữ các dòng trùng lặp cuối cùng:

ruby -i -e 'puts readlines.reverse.uniq.reverse' ~/.bash_history
tac ~/.bash_history | awk '!a[$0]++' | tac > t; mv t ~/.bash_history

Nói rõ hơn, tôi có hiểu đúng rằng bạn đã chỉ ra hai giải pháp (lộng lẫy) ở đây không và người dùng chỉ cần thực hiện một trong số chúng? Hoặc là một viên ruby, hay một Bash?
Jonathan Hartley

3

Đây là một bài viết cũ, nhưng là một vấn đề vĩnh viễn cho người dùng muốn mở nhiều thiết bị đầu cuối và có lịch sử được đồng bộ hóa giữa các cửa sổ, nhưng không bị trùng lặp.

Giải pháp của tôi trong .bashrc:

shopt -s histappend
export HISTCONTROL=ignoreboth:erasedups
export PROMPT_COMMAND="history -n; history -w; history -c; history -r"
tac "$HISTFILE" | awk '!x[$0]++' > /tmp/tmpfile  &&
                tac /tmp/tmpfile > "$HISTFILE"
rm /tmp/tmpfile
  • tùy chọn histappend thêm lịch sử của bộ đệm vào cuối tệp lịch sử ($ HISTFILE)
  • ignboth và erasingup ngăn các mục trùng lặp được lưu trong $ HISTFILE
  • Lệnh prompt cập nhật bộ đệm lịch sử
    • history -n đọc tất cả các dòng từ $ HISTFILE có thể đã xảy ra ở một thiết bị đầu cuối khác kể từ lần trả hàng cuối cùng
    • history -w ghi bộ đệm được cập nhật vào $ HISTFILE
    • history -c lau bộ đệm để không xảy ra sự trùng lặp
    • history -r đọc lại $ HISTFILE, thêm vào bộ đệm trống
  • tập lệnh awk lưu trữ lần xuất hiện đầu tiên của mỗi dòng mà nó gặp. tacđảo ngược nó, và sau đó đảo ngược nó để có thể được lưu với các lệnh gần đây nhất vẫn còn gần đây nhất trong lịch sử
  • tập tin / tmp

Mỗi khi bạn mở một shell mới, lịch sử sẽ bị xóa tất cả các bản sao và mỗi lần bạn nhấn Enterphím trong một cửa sổ shell / terminal khác nhau, nó sẽ cập nhật lịch sử này từ tệp.



Nếu "ignboth và erasingup ngăn không cho bản sao được lưu", thì tại sao bạn cũng cần thực hiện lệnh "awk" để xóa bản sao khỏi tệp? Có phải vì "ignboth và erasingup" chỉ ngăn chặn các bản sao liên tiếp được lưu lại? Xin lỗi để được mô phạm, tôi chỉ đang cố gắng để hiểu.
Jonathan Hartley

1
tẩy xóa chỉ xóa các bản sao liên tiếp. Và bạn đã đúng rằng lệnh awk sao chép lệnh erasingupes làm cho nó trở nên thừa.
smilefrog

Cảm ơn bạn, điều đó làm cho tôi rõ ràng những gì đang xảy ra.
Jonathan Hartley

0

Để đơn phương ghi lại mọi lệnh mới là khó khăn. Đầu tiên bạn cần thêm ~/.profilehoặc tương tự:

HISTCONTROL=erasedups
PROMPT_COMMAND='history -w'

Sau đó, bạn cần thêm vào ~/.bash_logout:

history -a
history -w

Bạn có thể giúp tôi hiểu tại sao, khi đăng xuất, bạn cần nối thêm lịch sử chưa được ghi vào tệp lịch sử trước khi viết lại toàn bộ tệp lịch sử không? Bạn có thể chỉ viết toàn bộ tập tin mà không có 'phụ lục' không?
Jonathan Hartley
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.