我想在我的bashrc文件中添加一个别名,以便切换到当前目录中最新的子目录:
alias cdl="cd $(ls -t | head -n 1)"
问题是,当我执行源文件时,该命令仅被评估一次。如果我在更改目录后使用新的
cdl
命令,则它仍然会尝试更改为旧目录,该目录可能不存在于我的新位置,并且对我来说并不是很有用。如何将此命令别名为每次运行都进行评估?
cdl() {
local mrd
IFS= read -r -d '' mrd < <(
shopt -s nullglob
mrd=
for i in */; do
if [[ -z $mrd ]] || [[ $i -nt $mrd ]]; then
mrd=$i
fi
done
[[ $mrd ]] && printf '%s\0' "$mrd"
) && cd -- "$mrd"
}
首先,我们要集中精力处理内部部分:
shopt -s nullglob
mrd=
for i in */; do
if [[ -z $mrd ]] || [[ $i -nt $mrd ]]; then
mrd=$i
fi
done
[[ $mrd ]] && printf '%s\0' "$mrd"
nullglob
shell选项可以使通配符在没有匹配项的情况下扩展为空。循环中使用了带有尾部斜杠的通配符for i in */
,因此它会扩展到当前目录中的所有目录(或者如果没有目录,那么就是空的,感谢nullglob
)。mrd
变量初始化为空字符串,并循环遍历所有目录,循环变量为i
:如果mrd
为空或i
扩展到比mrd
更新的目录,则用i
替换mrd
。mrd
仍然为空(如果根本没有找到目录),我们不做任何事情;否则,我们打印该目录名称并以尾随的空字节结尾。IFS= read -r -d '' mrd < <( ... )
这将内部部分的输出(因此,要么没有内容,要么是最近目录的内容,并带有尾随的nullbyte)存储在变量mrd
中。如果没有读取到任何内容,则read
失败,否则read
成功。在成功的情况下,我们高兴地cd
进入最新的目录。
我想提出两点:
It's possible to write cdl
as:
cdl() {
local mrd i
for i in */; do
if [[ -z $mrd ]] || [[ $i -nt $mrd ]]; then
mrd=$i
fi
done
[[ $mrd ]] && cd -- "$mrd"
}
As you can see, this doesn't set nullglob
, which is mandatory here. But you don't want to set it globally. So you need to save old nullglob
:
local old_nullglob=$(shopt -p nullglob)
and reset it at the end of your function:
eval "$old_nullglob"
While this is perfectly fine, I now try to avoid this, since if your function exits before completing (e.g., if user breaks its execution), nullglob
wouldn't be reset. That's why I chose to run the loop in a subshell!
At this point, you might think that the following would solve the problem:
local mrd=$( ... loop that outputs most recent dir... ) && cd -- "$mrd"
Unfortunately, $(...)
trims trailing newlines. So it's not 100% working, hence it's broken.
IFS= read -r -d '' v < <( .... printf '...\0' )
如果你想要一个疯狂的函数:你可能已经注意到我给出的 cdl
函数不能处理隐藏目录。那么我们如何允许以下调用:
cdl .
您需要查找隐藏目录的功能吗?那么,我们是否允许向函数传递参数,这样就可以像下面这样调用:
cdl . /path/to/dir1 /path/to/dir2 ...
你希望将/path/to/dir1
、/path/to/dir2
等目录的最近子目录cd
进去吗?如果需要包括隐藏目录,则应该在第一个参数中设置开关。
cdl /path/to/dir1 .
使用cd
命令可以进入/path/to/dir1
目录下最新的非隐藏子目录以及当前目录,但是
cdl . /path/to/dir1 .
还包括隐藏目录。
cdl() {
local mrd
IFS= read -r -d '' mrd < <(
[[ $1 = . ]] && shopt -s dotglob && shift
(($#)) || set .
shopt -s nullglob
mrd=
for d in "$@"; do
if [[ ! -d $d ]]; then
printf >&2 '%s is not a directory. Skipping.\n' "$d"
continue
fi
[[ $d = / ]] && d=
for i in "$d"/*/; do
if [[ -z $mrd ]] || [[ $i -nt $mrd ]]; then
mrd=$i
fi
done
done
[[ $mrd ]] && printf '%s\0' "$mrd"
) && cd -- "$mrd"
}
我的下一个编辑将包括一次更新,它将允许调用cdl
函数,在计算π的最后一位数时同时煮咖啡。
alias cdl='cd -- "$(ls -t | head -n 1)"'
ls
可能不会提供最新的目录,它也可能提供一个文件,在这种情况下,该命令将不能像您期望的那样工作。添加--group-directories-first
选项可能有助于解决这个问题。cd $(ls -td */ | head -n 1)
,这样它只会列出目录。当然,如果没有这些目录,那就是另一个问题了。我想我应该为此添加一个检查。不过现在这个方法可以用了。 - Mike
alias cdl='cd $(ls -t | head -n 1)'
。尽管你的命令有误。 - gniourf_gniourf