1 背景

大家很习惯的使用cp命令mv命令配合通配符*来实现特定目的,如期望拷贝或移动特定目录下的所有文件:

cp foo/* bar/
mv foo/* bar/

如果我们有封装此类函数的需要,则需要写一个函数:

function copyTo () {
	source_path=$1
	target_path=$2
 
	cp $source_path $target_path
	if (( $? != 0 )); then
		echo "error!"
		exit 1
	fi
}

此时我们期望将目录foo下的所有文件

拷贝到目录bar里,于是我们尝试调用它:

copyTo foo/* bar

然后我们可能会得到一堆奇奇怪怪的东西,就比如aaabbb文件完全一样了!

这种写法是错误的。

2 原理

假设我们写一个函数:

function test (){
	echo $@
}

然后同样尝试调用:

test foo/* bar

我们可能会很惊讶的发现它的输出是:

foo/aaa foo/bbb bar

也就是说,shell 将路径通配符展开成多个参数,然后传递给test函数,然而我们的目的是将foo/aaafoo/bbb拷贝到bar目录。

3 解决方案

我们可以利用${!#}获取最后一个参数,通过for遍历${@:1:$# - 1}来获取除最后一个以外的所有元素。最终得到的函数如下:

function copyTo () {
    target_path=${!#}
    for source_path in "${@:1:$# - 1}"
    do
        echo "copy" $source_path "->" $target_path
        cp $source_path $target_path
        if (( $? != 0 )); then
            echo "error!"
            exit 1
        fi
    done
}

我们执行一下copyTo foo/* bar得到

完成!