用于移动文件并跟踪它们到达目的地的别名

用于移动文件并跟踪它们到达目的地的别名

我正在尝试编写一个脚本(更准确地说是一个别名),它允许我移动文件并跟随 ( cd) 它们到目标目录。这接受的答案对于这个问题建议使用以下代码:

mvf() { mv "$@" && goto "$_"; }

其中是andgoto的一个更安全的变体,是传递给最后一个命令的最后一个参数。cd$_

我的派生实现是这样的:

alias mvaf="mv $@ && cd $_"

请注意,我没有引用$@,以免尝试通过所有参数的名称移动文件。我最初确实尝试过这个变体,但脚本也失败了。如果我用 调用上面的实现mvaf test1 test2 ..,它会抛出(翻译):“mv:缺少文件操作数”

在调试时,我尝试不使用cd,实际上alias mvaf="mv $@ ",它基本上只是重命名mv,移动文件。

我现在想知道为什么mv在我的第一个实现中缺少操作数,以及这是如何由&&.

答案1

Alias 不支持 $@、$1、$2 等操作数。

你的命令

alias mvaf="mv $@ && cd $_"

等于mv ' ' && cd $_因为$@别名无法按照您期望的方式识别。

这可以很容易地证明如下:

$ alias mvaf='echo "Part 1:" $@ && echo "Part 2: " $_'
$ mvaf file66 /tmp/
Part 1:
Part 2:  Part 1: file66 /tmp/
#Part 2 includes the previous executed command (echo "Part 1:" $@) & the text sent after alias name 

$ alias mvaf='echo "Part 1:" $@;echo "Part 2: "'
$ mvaf file66 /tmp/
Part 1:
Part 2:  file66 /tmp/

另一方面,这可行,但不是因为 $@

$ alias mvaf='echo "mv $@"'
$ mvaf file66 /tmp/
mv  file66 /tmp/

$ alias mvaf='echo "mv"'
$ mvaf file66 /tmp/
mv file66 /tmp/

一般来说,别名是一种简单的替换。

别名aa='command1;command2',当调用时aa sometext等于command1;command2 sometext

为了使其发挥作用,您需要使用一个函数来完成它。 Bash 不鼓励使用别名并鼓励使用函数来完成此类工作。您可以将此函数粘贴到 bash 配置文件中,并且可以像使用任何别名一样直接从终端按名称调用该函数:

mvcd() { mv "$1" "$2" && cd "$2"; }

链接mvcd命令&&在这里很重要,因为&&可以确保仅在前一个命令成功cd时才执行第二个命令。mv

或者,正如已经建议的那样在您的问题中接受的答案的链接中,你可以做类似的事情

mvf() { mv "$@" && goto "$_"; }
goto() { [ -d "$1" ] && cd "$1" || cd "$(dirname "$1")"; }

小心 bash 分词。为了使这样的函数正常工作,如果要移动的文件或要发送的文件的目录名称中包含空格,则需要在调用该函数时插入双引号。

$ mvcd() { echo "1=$1";echo "2=$2";echo "3=$3";echo "4=$4"; }

$ mvcd spaced file1 /spaced directory/
1=spaced
2=file1
3=/spaced
4=directory/

$ mvcd "spaced file1" "/spaced directory/"
1=spaced file1
2=/spaced directory/
3=
4=

答案2

alias不认为$@or $_(或$anything) 特殊,因此它们只是逐字传递给 shell。

因此你原来的:

alias mvaf="mv $@ && cd $_"

意思是:

mvaf f dst/

变成:

mv $@ && cd $_ f dat/

由于$@$_不太可能被定义,因此 shell 会进行如下解释:

mv && cd f dat/

因此,mv在不带参数的情况下调用并抛出“缺少文件操作数”错误。

当您重新定义为:

alias mvaf="mv $@ "

并执行:

mvaf f dst/

它变成:

mv $@ f dst/

与之前一样简化为:

mv f dst/

并欺骗你认为它已经起作用了!

答案3

正如@Deathgrip 在评论中所写,

没有在替换文本中使用参数的机制。如果需要参数,则应使用 shell 函数(请参阅下面的函数)。

别名如何工作?

让我们想想当您在命令行中键入以下两行时会发生什么:

$ alias word1="echo example"

$ word1 word2 word3

执行第一个命令后,bash请记住,无论何时word1,它都应该将其替换为任何命令的第一个单词echo example。当它看到第二个命令时,首先,它以最愚蠢(最简单)的方式执行替换 - 它echo example word2 word3从该输入创建一个命令。于是,便产生了呼应example word2 word3

现在,您的别名会发生什么情况?

当它看到它时,mfav one two它就会评估mv $@ && cd $_ one two。由于$@是空的(echo $@在控制台中尝试),mv失败。cd也会失败,因为它收到的参数多于一个。

注意:我的写作过于简单化,别名仅替换第一个单词。对于违反此规则的情况,看我的另一个答案

答案4

所有其他答案都解释了为什么别名不起作用,乔治·瓦西利乌的答案给出了一个替代函数来完成这项工作。

然而,我想以这个答案为基础,使其更加稳健。目前,该功能

mvcd() { mv "$1" "$2" && cd "$2"; }

如果目标是文件,则不起作用。为了解决这个问题,我们可以使用 bash 变量子字符串表示法:

mvcd() { mv "$1" "$2" && [[ -d $2 ]] && cd $2 || cd ${2%/*}; }

为了看看这里发生了什么,让我们以更易读的格式编写它:

mvcd () {
    mv "$1" "$2";     # Do the move
    if [[ -d $2 ]]    # Check if arg $2 is a dir
    then
        cd $2;        # if it is, cd into it
    else
        cd ${2%/*};   # else remove the filename and cd
    fi;
}

我们检查目标是否是一个目录[1] - 如果是,我们只需 cd 进入它。如果不是,那么我们将第二个参数从开头到最右边进行子串/,然后 cd 到其中。


[1] 我浪费了大量的时间试图在没有条件的情况下做到这一点,将 dirname 和 readlink 串在一起,或者只是总是在./路径或其他 hack 前面加上 ,但我总是遇到一个问题 - 要么它会 cd 太远(在 readlink 的情况下)或者它不能与 cding to 一起工作..,或者它不能与文件名一起工作或者......是的。如果有人可以在没有条件的情况下做到这一点,我非常愿意接受建议。

相关内容