Adicione o seguinte ao seu arquivo ~/.bashrc
:
#### cd history mechanism ##############################################
export CDHISTFILE=~/.cdhistory
if [ -e "$CDHISTFILE" ]
then
cdht='mktemp'
tail -500 "$CDHISTFILE" > $cdht
mv "$cdht" "$CDHISTFILE"
fi
function keep_cd_history() {
if [ -z "$1" ] ; then d="$HOME" ; else d="$1" ; fi
cdhcan='readlink -f "$d"'
if 'cd' "$d"
then
echo -e 'date +%s'"\t"$cdhcan >> $CDHISTFILE
fi
}
function pick_cwd_from_history() {
f=~/.cdhistgo
cdhistpick "$f"
if [ -r "$f" ] ; then cd "'head -1 $f'" ; fi
}
alias cd=keep_cd_history
alias cdh=pick_cwd_from_history
########################################################################
A primeira seção trunca o arquivo de histórico personalizado do mecanismo de histórico de cd se ele ultrapassou 500 linhas desde a última vez que o visualizamos. Não podemos usar o histórico integrado do Bash porque ele não inclui os timestamps, necessários para obter o comportamento "nas últimas três horas".
As duas funções de Bash fazem coisas que não podemos fazer no código Perl abaixo, que de outra forma faz todo o trabalho pesado. O único truque aqui é a chamada readlink
, que canaliza os caminhos que você usa. Temos que fazer isso para que cd $HOME ; cd ; cd ~ ; cd ../$USER
resulte em 4 instâncias do mesmo caminho no histórico cd
, e não em quatro entradas diferentes.
Os alias são apenas invólucros de conveniência para as funções.
Agora, o problema realmente complicado:
#!/usr/bin/perl -w
use strict;
use List::Util qw(min);
#### Configurables #####################################################
# Number of seconds back in time to look for candidate directories
my $history_seconds_threshold = 3 * 60 * 60;
# Ignore directories we have gone to less than this many times
my $access_count_threshold = 1;
# Number of directory options to give in pick list
my $max_choices = 7;
#### DO NOT OPEN. NO USER-SERVICEABLE PARTS INSIDE. ####################
# Get file name our caller wants the cd choice to be sent to
die "usage: $0 <choice_file>\n" unless $#ARGV == 0;
my $cdhg_file = $ARGV[0];
unlink $cdhg_file; # don't care if it fails
# Build summary stats from history file to find recent most-accessed
my $oldest_interesting = time - $history_seconds_threshold;
my %stats;
open my $cdh, '<', "$ENV{HOME}/.cdhistory" or die "No cd history yet!\n";
while (<$cdh>) {
chomp;
my ($secs, $dir) = split /\t/;
next unless $secs and $secs >= $oldest_interesting;
++$stats{$dir};
}
# Assemble directory pick list
my @counts = sort values %stats;
$access_count_threshold = $counts[$max_choices - 1] - 1
if @counts > $max_choices;
my @dirs = grep { $stats{$_} > $access_count_threshold } keys %stats;
$max_choices = min($max_choices, scalar @dirs);
# Show pick list, and save response to the file pick_cwd_from_history()
# expects. Why a file? The shell must call chdir(2), not us, because
# if we do, we change only our CWD. Can't use stdio; already in use.
my $choice;
if ($max_choices > 1) {
for (my $i = 0; $i < $max_choices; ++$i) {
print $i + 1, '. ', $dirs[$i], "\n";
}
print "\nYour choice, O splendid one? [1-$max_choices]: ";
$choice = <STDIN>;
chomp $choice;
exit 0 unless $choice =~ /^[0-9]+$/ && $choice <= $max_choices;
}
elsif ($max_choices == 1) {
print "Would you like to go to $dirs[0]? [y/n]: ";
$choice = 1 if uc(<STDIN>) =~ /^Y/;
}
else {
die "Not enough cd history to give choices!\n";
}
if ($choice) {
open my $cdhg, '>', $cdhg_file or
die "Can't write to $cdhg_file: $!\n";
print $cdhg $dirs[$choice - 1], "\n";
}
Salve isso em um arquivo chamado cdhistpick
, torne-o executável e coloque-o em algum lugar no seu PATH
. Você não vai executá-lo diretamente. Use o cdh
alias para isso, pois ele passa em um argumento necessário via pick_cwd_from_history()
.
Como isso funciona? Ummmm, exercício para o leitor? :)
Para obter sua primeira exigência, a tecla de atalho, você pode usar qualquer programa de gravação de macro que desejar para o sistema operacional escolhido. Apenas digite cdh
e pressione Enter para você. Ou você pode executar cdh
, já que é fácil digitar.
Se você quer uma alternativa mais simples, mas menos funcional, que funcionará em qualquer lugar, adquira o hábito de usar o recurso de busca incremental reversa do Bash, Ctrl - R . Pressione isso, então digite "cd" (sem as aspas, mas com o espaço à direita) para ser levado de volta ao comando anterior cd
. Então, cada vez que você apertar Ctrl - R , ele retornará ao comando cd
antes disso. Dessa forma, você pode retroceder através de todos os comandos cd
que você deu, dentro dos limites do recurso de histórico do Bash.
Diga:
$ echo $HISTFILESIZE
para ver quantas linhas de comando o histórico do Bash armazenará para você. Pode ser necessário aumentar isso para manter 3 horas de histórico de comandos.
Para procurar no seu histórico de comandos depois de recuar, pressione Ctrl - S .
Se isso não funcionar em seu sistema, provavelmente é devido a um conflito com o controle de fluxo de software . Você pode consertá-lo com este comando:
$ stty stop undef
Isso impede que o Ctrl-S seja interpretado como o caractere XOFF.
A conseqüência disso é que você não pode mais pressionar Ctrl-S para pausar temporariamente a saída do terminal. Pessoalmente, a última vez que usei isso de propósito foi nos tempos dos modems lentos. Nos dias de hoje com rolagem rápida e grandes buffers de rolagem, eu só uso esse recurso por acidente, então tenho que passar uma segunda lembrança para pressionar Ctrl - Q para obter o terminal não preso. :)