更改目录的Bash功能

时间:2012-05-16 15:12:33

标签: bash cd

我有一个常见的用例,我想编写一个函数:我经常想要cd到相对于某个文件的某个目录。

我目前的工作流程如下:

$ gem which rspec/core | xargs echo -n | pbcopy
$ cd *paste and delete end until direcory looks right*

<子> 注意:gem which rspec/core打印类似&#34; /Users/joshcheek/.rvm/gems/ruby-1.9.3-p125/gems/rspec-core-2.10.0/lib/rspec/core .RB&#34;

我希望它看起来像这样:

$ gem which rspec/core | 2dir 3

这将把我带入&#34; /Users/joshcheek/.rvm/gems/ruby-1.9.3-p125/gems/rspec-core-2.10.0" (传递参数&#34; 3&#34;告诉它从最后删除&#34; lib / rspec / core.rb&#34;

这是我迄今为止所获得的最好成绩:

2dir() {
  read dir
  for i in $(seq 1 $1)
    do
      dir="${dir%/*}"
  done
  cd "$dir"
}

但是cd改变了函数的目录,而不是我的。我尝试用别名交换它,但无法弄清楚如何制作匿名函数或传递论据。

5 个答案:

答案 0 :(得分:9)

我会用:

2dir()
{
    name=${2:?'Usage: 2dir count path'}
    count=$1
    while [[ $count -gt 0 ]]; do name=$(dirname "$name"); ((count--)); done
    cd "$name"
}

并将其用作:

2dir 3 $(gem which rspec/core)

这适用于您的管道无法使用的地方。管道进程中的cd会影响该(子)shell,但不会影响父进程的当前目录。可以使这个功能起作用。

如果您愿意,可以使用dir="${dir%/*}"代替dirname,除非您最终进入主目录而不是当前目录(或根目录,具体取决于是否如果在只有5个组件时指定10,则给出相对或绝对路径名称。

答案 1 :(得分:2)

这是@Jonathan Leffler关于简化使用的建议的一个变体 - 它使count参数可选,并且避免了命令周围$( )的需要:

2dir() {
# If first arg is a number, use it as a trim count; otherwise assume 2
if [[ "$1" =~ ^[0-9]+$ ]]; then
    count="$1"
    shift
else
    count=2
fi

if [[ $# -lt 1 ]]; then  # Make sure a command was specified
    echo "Usage: 2dir [count] command [commandargs ...]" >&2
    return 1
fi

name="$("$@")"  # Execute the remaining args as a command to get the target directory
while [[ $count -gt 0 ]]; do name=$(dirname "$name"); ((count--)); done
cd "$name"
}

示例用途:

2dir 3 gem which rspec/core
2dir gem which rspec/core

答案 2 :(得分:1)

命令 gem which rspec/core | 2dir 3 在shell的说法中被称为“管道”。管道中的每个命令都作为单独的进程执行。如果管道中的一个命令是shell函数,则它可以由当前(交互式)shell进程执行。但它不能保证,在你的情况下,这不会发生。

要解决您的问题,您只需确保在交互式shell中评估该功能。您只需要修复该功能,然后以不同方式使用它。这是更新的功能:

2dir() {
  declare -ir snip="$1"
  declare dir="$2"
  for i in $(seq 1 "$snip"); do
      dir="${dir%/*}"
  done
  cd "$dir"
}

你这样使用它:

$ 2dir 3 "$(gem which rspec/core)"

答案 3 :(得分:0)

shell脚本无法更改交互式shell的工作目录。只有一个别名可以做到这一点,因为它在你试图改变其目录的shell中运行。

换句话说:

有一个运行shell的Linux进程并接受来自您的命令。它有一个工作目录。当你告诉它执行一个shell脚本时,它会创建一个带有独立工作目录的全新流程,与第一个工作目录断开连接。

答案 4 :(得分:0)

根据Jonathan Leffler的回答,但没有循环:

2dir () {
    local levels name=${2:?"Usage: $FUNCNAME count path"};
    printf -v levels '%*s' "$1" '';
    cd "/${name%${levels// //*}}"
}

一个令人烦恼的是,它使用perfectly valid生成leading double slashes目录(例如echo "$PWD"输出&#34; // foo / bar / baz&#34;使用该函数后)。< / p>

另一个是它太聪明了一半。&#34;

修改

修正了双斜线问题:

2dir () {
    local levels name=${2:?"Usage: $FUNCNAME count path"};
    printf -v levels '%*s' $1 '';
    name=/${name%${levels// //*}};
    cd "${name/\/\///}"
}