如何制作自己的“ shell命令”(例如mkdir / cd组合)?


41

我无法告诉我希望有多少次命令既可以创建目录又可以移动到该目录。基本上,我想要以下等效:

mkdir -p /arbitrarily/long/path; cd /arbitrarily/long/path

但只需键入/arbitrarily/long/path一次即可,例如:

mk-cd /arbitrarily/long/path

我尝试创建脚本来执行此操作,但是它只会更改脚本中的目录。我希望外壳程序中的目录也已更改。

#!/bin/bash
mkdir $1
cd $1
export PWD=$PWD

我怎么能使这项工作?


12
使用cd,您从一开始就选择了一种特殊情况。:D
丹尼尔·B

2
不正是我一直在寻找,但超爽cd(用返回到前一个目录-相关信息cd -,使用pushdpopd维护一个目录的“堆栈”):superuser.com/questions/324512/...
克里斯托弗下装

8
您可以键入mkdir -p /very/long/path,然后使用cd,空格,然后按Alt + .重复最后一个参数,即目录名称。
choroba '16

2
不是答案,只是类似于@choroba的评论:您也可以使用mkdir -p /very/long/path; cd !#:2。该字符串!#:2将扩展为参数nr。2(即第三个参数/very/long/path,因为计数从零开始)。
Ingo Blechschmidt's

3
@IngoBlechschmidt,甚至更容易,因为它是最后一个命令的最后一个参数,所以您可以使用!$。我一直在使用这个特殊的技巧,尽管历史扩展还可以做很多事情
通配符

Answers:


92

最简单的方法是使用shell函数:

mkcd() {
    mkdir -p -- "$1" && cd -- "$1"
}

将其放置在.bashrc文件中,使其像另一个shell命令一样对您可用。

它不能用作外部脚本的原因是cd更改了正在运行的脚本的当前目录,但不影响调用脚本。这是设计使然!每个进程都有其自己的工作目录,该工作目录由其子进程继承,但相反的情况是不可能的。

除非是管道的一部分,要么在后台运行,要么在子外壳中显式运行,否则外壳函数不会在单独的进程中运行,而会在同一进程中运行,就像命令已经发出一样。然后可以通过函数更改当前目录外壳。

&&这里的“ 使用”将两个使用的命令分开,这意味着,如果第一个命令成功(mkdir),则运行第二个命令(cd)。因此,如果mkdir无法创建请求的目录,则没有必要尝试进入该目录。仅显示错误消息mkdir

使用的-p选项mkdir可以告诉该实用程序创建任何丢失的目录,该目录是作为参数传递的目录名称的完整路径的一部分。副作用是,如果您要求创建一个已经存在的目录,该mkcd函数将不会失败,并且最终会进入该目录。这可能被视为问题或功能。在前一种情况下,可以例如以仅警告用户的方式修改函数:

mkcd() {
    if [ -d "$1" ]; then
        printf "mkcd: warning, \"%s\" already exists\n" "$1"
    else
        mkdir -p "$1" 
    fi && cd "$1"
}

如果没有该-p选项,则初始功能的行为将大不相同。

如果包含要创建的目录的目录不存在,则mkdir失败,函数也会失败。

如果要创建的目录已经存在,则mkdir也会失败并且cd不会被调用。

最后,请注意,设置/导出PWD是没有意义的,因为外壳程序已经在内部进行了设置。

编辑:我向该--功能的两个命令都添加了该选项,以允许以短划线开头的目录名称。


5
您还应该补充一点,除非添加到该命令~/.bashrc或其他初始化脚本之一,否则该命令将不会永久可用。
AFH,

没有alias mk-cd 'mkdir -p \!:1; cd \!:1'函数,bash中没有办法做等同于tcsh的方法吗?也许我应该开始考虑bash函数更像扩展别名了?
Thomas Padron-McCarthy

@ ThomasPadron-McCarthy此csh参数传递机制未使用bourne样式别名实现。功能确实是要走的路,要灵活得多。
jlliagre

@ ThomasPadron-McCarthy-您可能想看一下这个答案,它确实为访问传递给别名的参数(在的定义中bar)提供了一种相当抽象的机制。另一个选择是使用history 1,例如alias mk-cd='args="$(history 1)" && args="${args#*mk-cd\ }" && mkdir -p "$args" && cd'-对于发问者的示例来说,这很好用,但不是通用解决方案,因为同一行上的任何其他命令(例如,对输出进行管道传输)都将导致失败。
AFH

3
@ ThomasPadron-McCarthy您应该开始考虑tcsh的别名,更像是受限制的函数。
吉尔斯(Gilles)'所以

32

我想添加一个替代解决方案。

如果使用 or 命令调用脚本,脚本起作用:.source

. mk-cd /arbitrarily/long/path

如果执行此操作,export则不需要脚本中的。您可以.使用来绕过键入alias

alias mk-cd='. mk-cd'

之所以起作用,是因为脚本通常在子外壳中运行,因此其环境在完成时会丢失,但是.(或source)命令强制脚本在当前外壳中运行。

这项技术对于功能过于复杂或正在开发且经常被编辑的命令序列很有用:一旦alias输入了.bash_aliases,您就可以随意编辑脚本而无需重新初始化。

请注意以下几点:

如果编写必须使用./ source命令调用的脚本,则有两种方法可以确保:

  1. 出于某种原因,用./ 调用的脚本source不必是可执行的,因此只需删除此权限,该脚本将不会在“ $ PATH”中找到,或者如果使用显式路径调用,则会给出权限错误。

  2. 另外,可以在脚本的开头使用以下技巧:

bind |& read && { echo 'Must be run from "." or "source" command'; exit 1; }

之所以有效,是因为在子外壳中bind发出了警告,尽管没有错误状态:因此read用于在没有输入的情况下给出错误。


感谢您提供有关的信息.。我做过. .bashrc无数次,但不知道到底是怎么.做的。类似于export吗?
cst1992年

2
@ cst1992-不,命令是完全不同的:export var将内部变量复制var到环境中,在该环境中继承该内部变量并将其作为变量导入任何子Shell中,但对父Shell无效。通常,将创建一个子外壳程序来运行脚本,并且完成后,它所做的任何更改(包括导出的变量)都会丢失。为了使脚本在Shell中设置变量,它必须在该Shell中运行,这就是.命令的作用:在不创建子Shell的情况下运行脚本。奇怪的是,运行的脚本.无需具有可执行权限。
AFH

谢谢(你的)信息。我坚持要您在帖子中包含此信息。这很有用,坦率地说,评论不能保证明天会出现。
cst1992年

@ cst1992-我解决了最初的问题,但我不想在辅助问题上打乱我的答案。如果您正在寻找.and export命令的解释,那么这个问题的标题不会使您期望在这里给出答案,因此,我将保持原样,但感谢您的评论。
AFH

一般为+1,但对于别名尤其如此。我有一些脚本需要采购,而我总是忘记以这种方式运行它们。别名很好地解决了这个问题。

13

不是单个命令,但是您不必重新输入所有使用的路径!$(这是Shell历史记录中上一个命令的最后一个参数)。

例:

mkdir -p /arbitrarily/long/path
cd !$

1

通过创建别名。创建自己的命令的非常流行的方式。

您可以动态创建别名:

alias myalias='mkdir -p /arbitrarily/long/path; cd /arbitrarily/long/path'

而已。如果要永久保留该别名(而不仅仅是当前会话),则将该命令放在一个点文件中(通常为〜/ .bash_aliases或〜/ .bash_profile)。


4
由于长目录名是硬编码的,因此除非该目录被其他进程定期销毁,否则该别名不会很有用。
jlliagre

1

除了已经提出的建议之外,我还要推荐thefuck。这是一个Python框架,用于通过修复错误来简化您的shell流程。受此推文启发,您所要做的就是键入配置的别名(默认情况下fuck),它将尝试更正您的上一个命令。其更正之一的行为如下:

/etc $ cd somedir
bash: cd: No such file or directory
/etc $ fuck
mkdir somedir && cd somedir
/etc/somedir $ 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.