如何让cp命令在文件未被复制时返回错误信号
这个问题确实挺头疼的——默认的cp -n确实会把“跳过已存在文件”当成正常行为,不管有没有文件没复制,都返回0。要区分“全部复制成功”和“有文件被跳过”的情况,有两个实用的办法:
方案一:写个简单的循环脚本逐个处理文件
这种方法兼容性最好,不管是GNU还是BSD版本的cp都能用。核心思路是手动检查每个目标文件是否存在,再决定是否复制,同时记录有没有文件被跳过或者复制失败:#!/bin/bash # 替换成你的目标目录 DEST_DIR="/path/to/your/destination" exit_status=0 # 遍历所有传入的源文件 for src_file in "$@"; do # 拼接目标文件路径 dest_file="$DEST_DIR/$(basename "$src_file")" if [ -f "$dest_file" ]; then # 目标文件已存在,标记错误状态并输出提示 echo "警告:$dest_file 已存在,跳过复制" >&2 exit_status=1 else # 执行复制并检查是否成功 cp "$src_file" "$dest_file" if [ $? -ne 0 ]; then echo "错误:$src_file 复制失败" >&2 exit_status=1 fi fi done # 返回最终状态码 exit $exit_status用的时候直接把这个脚本保存成
cp-with-check.sh,赋予执行权限chmod +x cp-with-check.sh,然后像用cp一样用它:./cp-with-check.sh file1.txt file2.jpg /path/to/dest。这样只要有文件被跳过或者复制失败,脚本都会返回非0的状态码,全部成功则返回0。方案二:借助cp的输出判断(仅限GNU cp)
如果你用的是GNU版本的cp(大部分Linux发行版都是),它的-v(verbose,详细输出)选项会在跳过文件时输出类似'file.txt' -> 'dest/file.txt' (not copied, destination exists)或者包含skipped的信息。我们可以捕获这个输出,检查是否有跳过的情况:# 执行cp命令并捕获所有输出(包括错误输出) cp_output=$(cp -n -v source_files/* /path/to/dest 2>&1) # 检查输出中是否有表示跳过的关键词 if echo "$cp_output" | grep -qE "(not copied|skipped)"; then echo "提示:有文件因已存在未被复制" >&2 exit 1 fi这个方法更简洁,但要注意:不同版本的
cp输出格式可能有差异,比如macOS上的BSD cp,-v的输出就没有这些关键词,所以这个方法只适合GNU环境。
其实cp的设计逻辑就是“跳过已存在文件不算错误”,所以默认不会返回非0码,上面的两种方法都是通过额外的检查来实现你要的区分效果。
备注:内容来源于stack exchange,提问作者greenoldman

