UNIX环境下按文件名前缀将文件移至对应目标目录的实现
按文件名前缀批量移动文件到对应目录的正确实现
首先得指出你原来的代码存在几个关键问题:
- 循环里的
find命令会把/home/source/下所有文件都复制到当前循环的目录里,导致每个目标目录都会出现所有源文件,完全不符合需求 - 用
ls输出到文件再循环读取的方式不够健壮(虽然这次文件名没空格,但如果以后遇到带空格的文件名就会出错) - 目标目录已经存在,
mkdir -p虽然不会报错,但属于多余操作
针对你的场景,这里提供几种简洁且可靠的实现方式:
方法一:最直观的for循环(推荐)
直接遍历源目录下的文件,逐个提取前缀并移动:
# 遍历源目录下的所有txt文件(如果要更精准匹配前缀开头的文件,可改用[0-9]{8}*.txt) for file in /home/source/*.txt; do # 先提取纯文件名(去掉路径) filename=$(basename "$file") # 截取文件名的前8个字符作为目标目录名 dir_prefix=${filename:0:8} # 移动文件到对应目标目录 mv "$file" "/home/target/${dir_prefix}/" done
优点:
- 逻辑清晰,容易理解和维护
- 用Shell内置的参数扩展
${filename:0:8}代替cut,不需要调用外部命令,效率更高 - 用
"$file"和"${dir_prefix}"的引号包裹,能兼容带空格或特殊字符的文件名(虽然你现在的文件名没有,但养成好习惯很重要)
如果不确定目标目录是否一定存在,可以在mv之前加上mkdir -p "/home/target/${dir_prefix}/",确保目录存在。
方法二:一行式批量处理(适合熟悉awk的用户)
如果你喜欢用一行命令搞定,可以结合awk和while循环:
ls /home/source/*.txt | awk '{split($0, arr, "/"); fname=arr[4]; print substr(fname,1,8), $0}' | while read prefix filepath; do mv "$filepath" "/home/target/${prefix}/" done
这个方法通过awk拆分路径、提取前缀,再通过while循环完成移动,和方法一效果一致。
为什么原代码会出错?
原代码里的find /home/source/ -type f -exec cp {} /home/target/${dir_path} \;会遍历/home/source/下所有文件并复制,不管当前循环的是哪个文件。比如第一次循环处理20180401abc.txt时,会把所有三个文件都复制到20180401目录;第二次循环处理20180402acb.txt时,又会把所有三个文件复制到20180402目录,这显然不是你想要的结果。
内容的提问来源于stack exchange,提问作者sidhartha swain
相关产品推荐
相关产品推荐

