DirB,Bash 目录书签

作者:Ira Chayut

想象一下,浏览网页时,每次访问网页都必须输入完整的统一资源标识符 (URI) 路径——这很痛苦。然而,自从 1993 年 Mosaic 浏览器添加了浏览器书签以来,它们使返回常用站点变得轻而易举(请参阅 en.wikipedia.org/wiki/Internet_bookmark)。无论您称它们为“书签”、“收藏夹”、“热点列表”还是“Internet 快捷方式”,它们都是出色的省时工具。

作为消费产品软件的开发人员,我经常同时在多个目录树中工作。我经常在每个活动的开发产品的源代码目录、包含供应商文档的目录和我的桌面(我保存所有活动的但尚未归档的工作)之间跳转。我过去为每个活动目录打开一个单独的 xterm 窗口,但是在各个窗口之间使用鼠标以及跟踪哪个窗口包含哪个目录既繁琐又容易出错。

如果存在命令行书签,它们将通过几次击键将我传送到经常访问的目录。当然,Linux 更改目录命令 (cd) 带有一个内置快捷方式:转到您的主目录的快捷方式。要回家,我只需要输入cd不带参数的命令。这甚至比点击我的红宝石拖鞋更容易(这与流行的脚本语言无关,而是对绿野仙踪的牵强附会的引用)。但是,便利性到此为止。

我创建了 Directory Bookmarks (DirB,发音为“derby”),将书签的概念扩展到命令行,并在目录之间快速移动。DirB 实现为一组 Bash shell 函数,由几个简单的命令组成

  • s— 保存目录书签。

  • g— 转到书签或命名目录。

  • p— 将书签/目录推送到目录堆栈。

  • r— 删除已保存的书签。

  • d— 显示书签目录路径。

  • sl— 打印目录书签列表。

这些命令可以与常用的 Bash 命令一起使用cd, pushdpopd.

正如您将看到的,DirB 意味着更少的击键和更高的工作效率。现在,我(几乎)从不不带它就出门。

如果 DirB 的函数名称与您已使用的命令或别名冲突,请在 .bashDirB 文件中将冲突函数的名称更改为您适用的名称。

安装

要安装 DirB,请从 www.DirB.info/bashDirB 下载源文件 .bashDirB,并将其另存为 ~/.bashDirB 到您的主目录。然后,编辑您的 ~/.bashrc 文件,并在文件中包含以下内容

source ~/.bashDirB

现在,每个新的 Bash 会话都将拥有 DirB 的强大功能。如果您在 ~/.bashrc 文件中使用 DirB 命令,请放置source行,放在使用 DirB 命令的上方。我发现将其放在文件顶部附近对我有用。

安装 DirB 后,打开一个新的 xterm 窗口,并按照本文的其余部分进行操作。

DirB 附带一个小的奖励。在同时在多个窗口中工作时,我发现让每个 xterm 窗口在其标题栏中显示当前目录的名称非常方便。为了实现这一点,.bashDirB 文件设置了主要的 Bash shell 提示符 $PS1 以输出转义序列。然后,此字符串将作为命令行提示符的一部分输出,并且 X11 窗口软件将通过更新 xterm 窗口的标题栏来响应转义序列。如果您未使用 X11,或者不需要此行为,请编辑 ~/.bashDirB 并在PS1=文件第 18 行的前面插入井号 (#),以注释掉该功能。

保存和使用书签

桌面是我最常见的目的地之一。我通过转到我的桌面,然后输入s命令

% cd ~/Desktop
% s d

(请注意,%表示 shell 的命令行提示符,而不是作为命令的一部分键入。)上面的第二行创建了一个名为 d 的新书签。

无论我在哪里,我现在都可以使用g命令

% cd /tmp   # go somewhere
% pwd
/tmp
% g d       # go to the desktop
% pwd
/home/Desktop
转到指定目录

现在可以使用以下命令移动到目录cdg。如果有一种方法可以同时用于书签和目录路径,是否会更简单?当然会。因此,DirB 的g命令已扩展为能够完全替换cd完全

% pwd
/home/Desktop
% g /tmp
% pwd
/tmp

g命令的行为与cd命令相同,如果参数的第一个字符是句点 (.),或者如果参数不是已保存书签的名称。第一个字符是句点的特殊情况允许您移动到与先前保存的书签同名的当前子目录

% cd /tmp
% mkdir d
% g ./d
% pwd
/tmp/d

如果您使用命令g d而不是g ./d在上面,DirB 会将您带到您的桌面,因为已经存在名为 d 的桌面书签。

如果g的参数是目录的相对或绝对路径,并且没有同名的书签,则您将被带到指定的路径

% cd /tmp
% mkdir subdir
% g subdir
% pwd
/tmp/subdir

cd命令一样,如果您输入不带参数的g命令,您将转到您的主目录

% cd /tmp
% g
% pwd
/home
与亲戚一起旅行

我工作的大多数源代码目录都使用相同的布局组织。从应用程序的源代码目录中,我经常需要引用我的标准库中的头文件。这些头文件位于文件系统中向上两个目录和向下两个目录:../../stdlib/inc。

DirB 可以保存相对书签或任何指定路径的书签。无需位于要添加书签的目录中。可以使用较长版本的s命令来指定书签的路径

% g projA
% pwd
/home/projectA/source/application/main
% s stdh ../../stdlib/inc
% g stdh
% pwd
/home/projectA/source/stdlib/inc

一旦使用s命令创建了相对书签,就可以从相对路径存在的任何位置轻松进行相对移动

% g projB
% pwd
/home/projectB/source/application/main
% g stdh
% pwd
/home/projectB/source/application/main

此较长版本的s命令设置完整的路径目录书签,而无需先更改为目标目录

% g projA
% pwd
/home/projectA/source/application/main
% s t /tmp
% pwd
/home/projectA/source/application/main
% d t
/tmp

请注意,当前工作目录未被s命令更改,并且书签设置为s命令的参数,而不是当前目录。书签可以稍后使用,与更简单的书签相同

% g t
% pwd
/tmp
操作目录堆栈

由于g命令扩展了 Bash 的内置cd命令,DirB 具有p命令来扩展 shell 的pushd命令,并且还替换了 shell 的popd命令最常见的用法。

在其最常用的形式中,p命令更改为新目录,同时将当前目录记住在堆栈上。然后打印目录堆栈的状态

% g
% pwd
/home
% p /tmp
/tmp
~

波浪号 (~) 是 Bash 主目录的快捷方式。目标也可以很容易地是书签

% p d
~/Desktop
/tmp
~

目录堆栈列表以每行一个目录的方式完成,而不是默认的列表样式pushd,其中所有目录都跨行打印。这是一种个人偏好,通过丢弃调用的输出pushd命令,然后运行dirs -p命令来实现。

除了书签目标和目标破折号 (-),p命令的工作方式与 Bash 的pushd命令完全相同。实际上,所有实际工作都是在幕后由pushd完成的。因此,正常的pushd行为以及增强的书签功能都是有效的(并且有用)

% p directory   # adds dir to top of dir stack
% p bookmark    # adds bookmark to dir stack
% p             # swaps top two stack entries
% p +n          # rotate nth entry from top to top
% p -n          # rotate nth entry from bottom to top

要旋转目录堆栈,以便底部目录移动到堆栈顶部作为当前目录,请使用p -0。除了替换pushd之外,p命令还可以替换 shell 的popd命令的最简单形式

% g
% pwd
/home
% p /tmp
/tmp
~
% p -
~

如果需要popd命令的全部功能,则标准popd命令(以及pushdcd)仍然可用,并且可以与 DirB 命令一起使用。

要获取当前目录堆栈的列表,shell 的dirs命令的工作方式与 DirB 之前相同。

列出已保存的书签

DirB 的sl命令打印已保存的书签列表。它有两种形式。最简单的形式是从左到右跨行列出文件,按时间倒序排列,最近访问的书签排在最前面

% sl
d test prod tmp beta alpha

在此示例中,我桌面的书签 d 是最近访问的。

在较长的形式中,sl列出每个书签上次被引用的日期和时间

% sl -l
2010-03-10 14:42 d
2010-03-01 14:19 test
2010-02-27 10:17 prod
2010-02-27 14:21 tmp
2009-10-22 17:26 beta
2009-08-05 11:37 alpha

在此更完整的列表中,您可以看到 d 书签在 3 月 10 日被引用,而上次引用测试书签是在九天前。如果长列表不适合在一个屏幕上显示,则less命令将自动分页浏览列表。

可以将正则表达式传递给sl并仅列出匹配的书签。要列出以字母 t 开头的已保存书签

% sl "t*"
test  tmp
% sl -l "t*"
2010-03-10 14:19 test
2010-02-27 14:21 tmp

请注意,正则表达式需要用双引号(或单引号)括起来,以防止 shell 在sl命令最常见的用法。

看到它之前尝试展开它。每当书签是g, ps命令的目标时,其时间戳都会更新以记录引用。但是,当使用cd, pushd或通过目录堆栈操作访问目录时,时间戳不会更新。

删除过时的书签

目录书签非常容易制作,以至于我经常创建它们。我的许多书签都是短暂的。如果听之任之,已保存的书签列表将变得非常长且杂乱。DirB 的r命令简化了删除不需要的书签

% sl
test  prod  d  tmp  beta  alpha
% r alpha
% sl
test  prod  d  tmp  beta

第二个已保存的书签列表显示r alpha删除了不需要的 alpha 书签。

当遇到问题时,DirB 或底层 Bash 命令会发出错误消息。访问已删除的书签会导致此类消息

% g alpha
bash: cd: alpha: No such file or directory

这是书签不存在时发出的错误消息,可能是由于拼写错误。

在脚本中使用书签

书签可以节省击键次数,并允许在目录之间快速移动。书签还可以用于使脚本更具可移植性。通过引用书签而不是固定路径,可以轻松地在不同环境中重用脚本。我同时在 Linux 和 Cygwin 平台上工作。(Cygwin 是 Windows 平台的类 Linux 环境。有关更多信息或下载 Cygwin,请参阅 www.cygwin.com。)由于 Cygwin 呈现出非常类似 Linux 的外观和感觉,因此过渡是轻松的。但是,Linux 和 Cygwin 目录结构是不同的。我使用 DirB 在每个系统上设置相同的常用书签列表。这样,无论平台如何,我都可以使用相同的击键在命令行上更改目录。

除了 Linux 和 Cygwin 环境外,DirB 还在 BSD UNIX 和 Mac OS X 平台上进行了测试。因此,DirB 书签引用的灵活性可以跨越各种系统。

d命令将 DirB 功能扩展到 shell 脚本。(d 是“display bookmark path”或“dereference bookmark path”的缩写,您可以选择。)它允许脚本获取书签目录的完整路径名。

Bash 的命令替换$(command)功能通常用于访问d:

% DTOP="$(d d)"
% echo $DTOP
/home/Desktop

双引号需要包围 shell 替换,以防目录路径中存在空格。不幸的是,这在基于 Windows 的 Cygwin 平台上太常见了,所以我总是使用引号。在上面的示例中,shell 变量$DTOP可用于访问桌面。要在桌面上创建新的日志文件,可以将命令的输出重定向到$DTOP/logfile。不要忘记双引号,以防反向引用的路径包含空格。

我建议使用 Bash 的替换功能,如上所示。但是,打印路径名称的更短方法是直接使用 DirB 的d命令

% d d
/home/Desktop
幕后花絮

DirB 将所有目录书签保存在 ~/.DirB 中,这是用户主目录的“隐藏”子目录。当从 ~/.bashrc 中获取文件 ~/.bashDirB 时,它会检查 ~/.DirB 目录是否存在。如果目录不存在,则会创建该目录。这保证了书签存储库的存在。

每个书签在 ~/.DirB 目录中都有一个关联的文件,文件名与书签相同。书签文件包含单行命令,例如

$CD /home/Desktop

shell 变量 $CD 由gp命令设置为cdpushd,分别由 shell 在调用书签时展开。本质上,命令g d被转换为cd /home/Desktop,和p d被转换为pushd /home/Desktop.

DirB 命令实现为 Bash 函数,这些函数执行一些错误检查,确定要执行的操作,然后调用标准命令。例如,g命令在调用cd命令

# "g" - Go to bookmark
function g () {
    # if no arguments, go to the home directory
    if [ -z "$1" ]
    then
        cd
    else
        # if $1 is in ~/.DirB and does not
        # begin with ".", then go to it
        if [ -f ~/.DirB/"$1"
            -a ${1:0:1} != "." ]
        then
            # update the bookmark's timestamp a
            # and then execute it
            touch ~/.DirB/"$1" ;
            CD=cd source ~/.DirB/"$1" ;
        else
            # else just "cd" to the argument,
            # usually a directory path of "-"
            cd "$1"
        fi
    fi
}

之前执行一些检查。函数g检查是否存在参数。如果 $1 是零长度字符串,则使用cd(不带参数调用)将用户发送到主目录。否则,将检查参数是否是已保存书签的名称,并且参数的第一个字符不是句点。

如果两个条件都满足,则通过获取书签文件,将书签作为当前 shell 的一部分运行。在执行之前,shell 变量$CD设置为cd命令最常见的用法。source用于代替将书签作为 shell 脚本调用,以便目录更改将影响当前 shell。调用的脚本将具有唯一的 shell 会话,该会话将在cdpushd之后终止。因此,它对当前 shell 会话没有持久影响。

如果参数不是书签的名称,或者如果它以句点开头,则调用cd命令,并将参数用于转到指定的目录路径。

请注意,source函数中的g命令以变量赋值开头

CD=cd source ~/.DirB/"$1" ;

Bash 语法允许命令前面有一个或多个变量赋值。

错误处理

大多数 DirB 命令最终都会调用cd, pushdpopd来执行请求的操作。如果其中一个标准命令遇到问题,它会向标准错误 (stderr) 流发出错误消息,并以失败的返回代码退出。

请注意,由于书签是它们在 ~/.DirB 存储库中关联文件的名称,因此它们的名称中不能包含斜杠。如果无法创建书签(最可能是由于名称中存在无效字符),s将向标准错误打印错误消息

% s a/d
bash: DirB: /home/.DirB/a/b could not be created

如果gp的参数既不是书签也不是有效的目录路径,则会产生错误消息

% p missing
bash: pushd: missing: No such file or directory

如果书签名称拼写错误或书签已被删除,则会发生这种情况。如果dr命令的参数不是已保存书签的有效名称,也会导致类似的错误消息

% d missing
bash: DirB: /home/.DirB/missing does not exist
% r missing
bash: DirB: /home/.DirB/missing does not exist

如果遇到错误,DirB 命令将以失败的返回代码退出。此行为允许其他 Bash 脚本使用这些函数,并在发生错误时采取适当的操作。

结论

DirB 是作为一组 Bash 函数创建的,旨在将书签的概念扩展到 Linux 目录。它可以加速从命令行或 shell 脚本在常用目录之间移动。虽然它是一个简单的工具,但我每天都依赖 DirB,并希望其他人也会发现它很有用。

Ira Chayut 是一位资深的 UNIX/Linux 开发人员,于 1976 年首次从事 version 6 UNIX 的工作。他是 C 和 UNIX 参考手册的作者,运营 www.verilog.net,并就集成电路验证发表演讲。目前,他是一家消费品公司的创始人,负责所有嵌入式和 DSP 编程。可以通过 ira@dirb.info 与他联系。

加载 Disqus 评论