有没有办法编写具有以下功能的bash脚本?
- 当我按下某个键或组合键时启动。 (不是那么重要的要求)
- 确定过去 3 小时内访问量最大的 7 个目录。
- 向我提供这 7 个目录的列表,以便我可以使用 tab 和 shift-tab(向后)循环浏览它们。然后按 Enter 键就意味着 cd 到所选目录。
谢谢
答案1
将以下内容添加到您的~/.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
########################################################################
如果自上次查看以来 cd 历史记录机制的自定义历史记录文件变得超过 500 行,第一部分将截断该文件。我们无法使用 Bash 的内置历史记录,因为它不包含时间戳,而您需要时间戳才能获得“过去 3 小时内”的行为。
这两个 Bash 函数完成了我们在下面的 Perl 代码中无法完成的事情,否则 Perl 代码会完成所有繁重的工作。这里唯一棘手的一点是readlink
调用,它将您使用的路径规范化。我们必须这样做,以便在历史记录cd $HOME ; cd ; cd ~ ; cd ../$USER
中产生相同路径的 4 个实例cd
,而不是四个不同的条目。
别名只是函数的方便包装。
现在真正棘手的一点是:
#!/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";
}
将其保存到名为 的文件中cdhistpick
,使其可执行,然后将其放在PATH
.您不会直接执行它。cdh
为此使用别名,因为它通过 传递必要的参数pick_cwd_from_history()
。
它是如何工作的?嗯,给读者做练习? :)
为了满足您的第一个要求(热键),您可以使用适合您选择的操作系统的任何宏录制程序。只需让其为您输入cdh
并按下即可。Enter或者,您可以cdh
自己运行,因为它很容易输入。
Ctrl如果您想要一个更简单但功能较少且可在任何地方使用的替代方案,请养成使用 Bash 的反向增量搜索功能-的习惯R。按该键,然后输入“cd”(不带引号,但尾随空格)以返回到上一个cd
命令。然后每次你点击Ctrl- R,它都会带你回到cd
之前的命令。通过这种方式,您可以cd
在 Bash 历史记录功能的范围内向后浏览您给出的所有命令。
说:
$ echo $HISTFILESIZE
查看 Bash 历史记录将为您存储多少条命令行。您可能需要增加此值以保存 3 小时的命令历史记录。
要在向后浏览命令历史记录后向前搜索,请按Ctrl- S。
如果这在您的系统上不起作用,可能是由于与软件流量控制。您可以使用以下命令修复它:
$ stty stop undef
这可以防止 Ctrl-S 被解释为 XOFF 字符。
这样做的结果是您不能再按 Ctrl-S 来暂时暂停终端输出。就我个人而言,我上次故意使用它是在慢速调制解调器时代。如今,随着快速滚动和大回滚缓冲区的出现,我只是偶然使用此功能,然后必须花一秒钟记住按下Ctrl-Q以使终端不再卡住。 :)
答案2
使用这个脚本。您需要安装sqlite3
才能使用它。它在子 shell 中启动 sqlite3 来更新数据库(以便更改目录与正常情况一样快cd
)。下载这个脚本是source
在你的.bashrc
.使用命令c
更改目录。如果没有给出参数,c
那么 bash 上将显示最近两天访问最多的目录的排序列表。你可以选一个。