我正在尝试编写一个脚本(更准确地说是一个别名),它允许我移动文件并跟随 ( 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"; }
链接mv
和cd
命令&&
在这里很重要,因为&&
可以确保仅在前一个命令成功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 一起工作..
,或者它不能与文件名一起工作或者......是的。如果有人可以在没有条件的情况下做到这一点,我非常愿意接受建议。