尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Linux mkdir 命令完全指南:从基础语法到权限与脚本实战

Linux mkdir 命令完全指南:从基础语法到权限与脚本实战 在 Linux 世界里mkdir 大概是和 ls、cd 一样让人毫无防备的命令敲下去回车目录就出现了。但我在实际项目里遇到过的目录问题十次里有七次都跟创建目录的方式有关——要么是脚本反复执行报错要么是建出来的目录权限不对导致服务起不来要么是团队共享目录时发现别人完全没有写入权限。所以这篇我决定把 mkdir 创建目录的各种用法彻底拆一遍从最基础的语法到多级目录、批量创建、权限控制、脚本容错以及我自己踩过的那些坑一次性说清楚。这篇内容适合刚接触 Linux 的新手系统入门也适合已经用过一段时间但没深究过细节的开发者、运维同学当作一份自查手册来用。1. mkdir 的运行逻辑命令执行语义与文件系统底层视角1.1 语法、三个核心选项与无提示即成功的执行语义mkdir 的全称是 make directories它的核心语法并不复杂mkdir [选项] 目录名...需要记住的选项其实只有三个-p--parents创建所有必需的父目录父目录缺失时自动补全。-m--mode创建目录的同时直接指定权限模式。-v--verbose打印每个实际被创建的目录名。其余像--contextSELinux 上下文参数属于特定场景才会用到日常命令行里基本碰不上。先把这三个核心选项刻在脑子里整篇文章都在围绕它们展开。一个很容易被忽略的执行语义是mkdir 成功时默认没有输出。命令执行完光标静悄悄地落到下一行目录就已经出现在当前路径下了。很多第一次接触 Linux 的人会以为命令没生效忍不住再敲一遍结果碰到File exists的报错才知道目录早就建好了。记住这个无提示即成功的规矩能少走很多弯路。1.2 一个目录在文件系统里到底是怎么被造出来的从底层看mkdir 命令本身只是一个用户态外壳真正干活的是内核的mkdir()系统调用。目录在文件系统里的本质是一种特殊文件里面存放的是文件名 - inode 编号的映射表。执行一次 mkdir内核实际上要完成三件事分配一个新的 inode用来记录目录的元数据属主、权限、时间戳。分配数据块写入两条特殊记录.点指向目录自身和..点点指向父目录。在父目录的数据块中追加一条映射新目录名 - 新 inode 编号。这三件事恰好对应三种常见故障。第 1 步失败通常是磁盘配额或 inode 耗尽——你df -i一看inode 使用率 100%这时候哪怕磁盘还有空间也建不了目录第 2 步失败通常是磁盘真的满了数据块分配不出来第 3 步失败则大概率是父目录没有写权限。搞清楚这个对应关系之后很多为什么建不了目录的问题就成功了一半因为你至少知道该往哪个方向去排查了。另外顺带补充一个文件系统常识新建一个空目录后它的硬链接数至少是 2一个是父目录里的目录项一个是目录内部的.。这也是为什么普通用户不能给目录手工创建硬链接——目录的硬链接只能由内核在创建子目录时自动增加避免文件系统里出现目录环。2. -p 参数救了多少脚本多级目录创建与幂等特性2.1 没有 -p 的日子父目录不存在直接报错默认情况下mkdir 只创建你指定的那一个叶子目录它不会顺路把父目录一起建好。所以当你执行mkdir /data/logs/nginx/2025/01而/data/logs/nginx还不存在时你会看到这么一条报错mkdir: cannot create directory /data/logs/nginx/2025/01: No such file or directory很多新手看到No such file or directory的第一反应是权限不够这是个典型误区。真正的原因非常简单父目录链路断掉了内核在递归创建时发现/data/logs/nginx这一层根本不存在于是一路向上追溯到底也找不到可落的根最终直接拒绝执行。这个报错和权限无关是路径层级的问题。解决方法就是加-pmkdir -p /data/logs/nginx/2025/01加了这个参数后mkdir 会像爬楼梯一样先检查/data存不存在不存在就建再检查/data/logs依次类推直到把完整路径逐层补齐。2.2 -p 的幂等特性能解决什么问题-p还有一个被严重低估的语义幂等。同一个mkdir -p /data/logs连续执行一万次和只执行一次结果完全一样。目录存在时它不会报错、不会重复创建、也不会覆盖目录里已有的内容而是安静地返回成功状态码 0。这个特性在脚本里简直就是为确保目录存在量身定做的。我见过不少同事写过这样的代码if [ ! -d $LOG_DIR ]; then mkdir $LOG_DIR fi单次执行没问题但一旦涉及多个进程同时启动、同时判断、同时创建或者脚本逻辑分支复杂时这种写法就容易出现竞态条件。更优雅的做法是直接声明目标状态mkdir -p $LOG_DIR一行解决连判断都省了。我自己写脚本时凡是涉及要确保某个目录存在的地方一律用mkdir -p不写test -d判断。这是实战里最省心、也最不容易出错的标准姿势。2.3 -v 参数把创建过程变成可审计的日志-p单独用的时候你只知道结果不知道过程——尤其是在目录层级很深时你甚至不知道它到底建了哪几层哪一层是本来就有、哪一层是新建出来的。这时候把-v加上去mkdir -pv /data/logs/nginx/2025/01输出会变成这种风格mkdir: created directory /data mkdir: created directory /data/logs mkdir: created directory /data/logs/nginx mkdir: created directory /data/logs/nginx/2025 mkdir: created directory /data/logs/nginx/2025/01逐层打印建了哪些目录一目了然。我印象很深的一次同事的部署脚本里用mkdir -p建目录结果服务起不来排查半天发现是其中某一层路径的属主不对。后来加了-v重新跑一遍立刻看清楚是哪一层被建到了错误的位置、哪一层权限有异常。在初始化脚本或 CI 日志里使用-pv组合等于给目录创建过程上了审计日志成本极低但价值很高。3. 批量创建目录的实操方案多参数、大括号展开与日期目录3.1 一条命令创建多个兄弟目录mkdir 天生支持一次创建多个目录多个目录名之间用空格分隔即可mkdir docs src tests build它会按顺序逐个创建如果中途某个目录创建失败比如权限不足后面的目录会继续执行但命令最终会返回非零状态码。这一点在脚本里要注意如果你需要确保所有目录都创建成功最好在命令后面加上判断或者干脆逐条执行。不过一条命令敲这么多目录名手写容易漏也容易打错字。更推荐的方式是配合 shell 的大括号展开brace expansionmkdir {docs,src,tests,build}花括号里的逗号分隔列表会被 shell 展开成多个参数作用等同于一个个敲目录名。目录结构相似、数量多时这个写法既清晰又不容易出错。3.2 大括号展开的批量目录树玩法大括号展开还可以做笛卡尔积式的组合这是批量创建目录的一把利器。比如你想给项目创建一套年份 月份 业务类型的目录骨架mkdir -p {2024,2025}/{01,02,03}/{logs,backup}一行命令就能生成2024/01/logs、2024/01/backup、2024/02/logs……总共 12 个多层目录比手写mkdir -p十几遍省太多事了。再复杂一点创建前后端分离项目的标准骨架mkdir -p project/{src/{components,utils,api},public,config,tests}注意这里的大括号是可以嵌套的shell 会按规则一层层展开。用熟之后你会发现批量建目录这件事基本不需要写循环一行命令就能搞定绝大多数场景。3.3 用日期时间自动生成归档目录日志归档、备份管理是另一个高频场景核心思路是把当前日期拼进路径里。直接写在命令行里mkdir -p $(date %Y/%m/%d)这会创建出2025/06/18这样的三级目录结构如果希望按天归档且路径更扁平一点mkdir -p logs/$(date %Y%m%d)生成的就是logs/20250618。我习惯把这类逻辑封装进备份脚本LOG_DIRlogs/$(date %Y%m%d-%H%M%S) mkdir -p $LOG_DIR%H%M%S把时分秒也拼进去每次执行都能得到一个不重复的目录。这里有一个细节值得强调命令替换产生的路径最好用双引号包起来。虽然日期格式不会产生空格但养成路径有变量就加引号的习惯能避免很多潜在问题。4. 目录权限失控的根源umask、-m 与 setgid 位4.1 默认权限是算出来的umask 与 0777 的关系不加-m时mkdir 创建出的目录权限并不由你直接决定而是由当前 shell 的umask值计算得出。公式非常固定目录默认权限 0777 ~umask比如常见的 umask 是022那么创建出的目录权限就是0777 ~022 0755对应权限就是rwxr-xr-x属主可读可写可进入组用户和其他用户只能读和进入。如果 umask 是002目录默认权限会变成0775也就是组内用户可以写入。这里有一个新手经常绕晕的点创建文件时默认权限的基础值是0666而不是0777因为普通文件天生不应该带上执行位。目录则恰恰相反必须要有x执行位才能进入所以目录的默认权限基数会保留执行位。两者用的 umask 相同产出的权限数值却不一样这不是 bug是 Unix 权限设计的基本逻辑。4.2 -m 参数显式指定目录权限绕开默认计算如果默认的 umask 计算结果不符合需求就用-m参数直接指定mkdir -m 700 private_data这条命令创建出的private_data权限就是rwx------无论当前 umask 是什么都不受影响。-m只影响这次创建的目录不会改动 shell 的 umask 值。我在实际项目里的选型习惯大致如下目录用途推荐权限说明个人私有目录700只有自己能读、写、进入通用项目目录755属主可写组和其他人只读可进团队协作目录2775带 setgid组内可写自动继承组Web 上传目录750属主和组可写避免 others 误碰敏感数据目录700最小权限原则这些数值没有标准答案但思路是固定的先想清楚谁需要写、谁只需要读和进再定权限位。别图省事一律 777后面迟早要出事。4.3 setgid 位让协作目录自动继承用户组团队协作服务器的共享目录有一个经典痛点多个用户共同维护/data/shared但某个成员用 mkdir 在里面新建的子目录用户组会默认变成该成员自己的主用户组而不是目录的共享组。这样一来其他队友就进不去了。解决办法是给目录加上 setgid 位。权限数字写作2775最前面的2就是 setgid 位。创建时可以一步到位mkdir -m 2775 /data/shared也可以先建目录再改权限mkdir -p /data/shared chown root:devteam /data/shared chmod 2775 /data/sharedsetgid 位的作用是在这个目录下新建的任何子目录或文件用户组都会自动继承目录的用户组而不是创建者的主组。这是一个非常实用的运维技巧配合团队账号体系使用能省掉大量手动chgrp的操作。5. 真实场景里的 mkdir 组合拳从项目骨架到日志归档5.1 初始化项目目录结构每个新项目开始时我都会用一条命令生成完整目录骨架mkdir -p $PROJECT_HOME/{src/components,src/utils,config,public,tests,docs,scripts}$PROJECT_HOME是项目根目录花括号展开负责补齐子目录。如果你的项目不希望其他人拥有进入权限可以再补一句chmod -R grX $PROJECT_HOME这里有个细节值得说明大写的X只给目录和已有执行位的文件加上执行权限不会给普通文件乱加执行位。chmod -R配合X是批量调整目录树权限时最不容易出错的一条命令。5.2 按日期归档服务日志服务日志的归档逻辑我通常组织成下面这样BASE/data/logs/app TODAY$(date %Y-%m-%d) mkdir -p $BASE/$TODAY每天凌晨的定时任务跑一次目录已经存在时不会报错不存在时自动创建完全幂等。之后无论是rsync同步还是logrotate轮转都是基于这个目录结构去操作非常顺滑。5.3 多用户共享目录的完整配置路径团队共享场景下我建议按这三步走mkdir -p /srv/project chown -R ops:devgroup /srv/project chmod 2770 /srv/project最后一条2770的意思是带 setgid属主和组内成员可以读写进入其他人完全不可见。这样配置之后组内任意成员在/srv/project下新建的目录都会自动归devgroup组团队协作基本不会再因为权限互相踩脚。5.4 一个经常被忽略的细节目录建错了位置我见过最多的低级但影响不小的错误是——目录建到了错误的位置。原因很简单mkdir 如果给的是相对路径就会建在当前工作目录下。命令行里无所谓因为你知道自己在哪但脚本里面尤其是有cd操作或者由 cron 调用时当前目录往往不是你以为的那个。规避方法也很简单在脚本里创建目录时要么先cd到明确路径再执行要么直接在 mkdir 命令里写绝对路径mkdir -p /opt/app/web/logs写绝对路径脚本执行位置再乱也不会把目录建到荒郊野岭去。这不是 mkdir 本身的问题但确实是因为 mkdir 踩得最多的一种坑。6. 脚本与自动化中的 mkdir幂等、容错与临时目录安全6.1 set -e 环境下 mkdir 的正确姿势很多 bash 脚本开头会写set -e意思是任何命令返回非零状态码就立刻终止脚本。此时普通 mkdir 在目标目录已存在时会返回 1直接导致脚本中断退出——这个行为很容易让初次写脚本的人摸不着头脑目录明明在脚本却挂了。解决办法仍然是-p。mkdir -p在目录已存在时返回 0在目录不存在且能创建时返回 0整个就是永不因目录存在而失败的语义。所以在set -e环境下脚本里涉及目录创建的部分统一使用mkdir -p是基本纪律。6.2 判断目录是否存在test -d 与 -p 的取舍在不适合用-p的场景下判断目录是否存在的标准姿势是if test -d $DIR; then echo $DIR 已存在 else mkdir $DIR fitest -d等价写法是[ -d $DIR ]会在路径存在且为目录时返回真。注意变量要加引号否则路径里有空格时会报错。平时跑脚本能用mkdir -p直接声明目录存在就优先用-p必须区分目录是否刚创建的时候才写test -d这个分支。6.3 临时目录用 mktemp -d别自己手搓需要临时目录时很多人的第一反应是mkdir -p /tmp/myapp_$$$$是当前 shell 的 PID每次运行确实会变化但 PID 是可以被预测的。在多用户服务器上有人可以预先创建/tmp/myapp_1234这样的路径做符号链接攻击把程序要写入的文件引到别处造成数据覆盖。这种问题不常见但一旦发生就是安全事件。标准做法是用mktemp -dTMPDIR$(mktemp -d) trap rm -rf $TMPDIR EXITmktemp -d会自动生成带随机字符串的目录名默认权限也只有属主可读写从源头规避了临时目录的预测风险。脚本退出时通过trap清理临时目录连收尾都一起做完了。这就是我用mktemp分担掉 mkdir 一部分职责的原因——临时目录这种高风险场景专业的工具显然更靠谱。7. 顶着 mkdir 名头翻过的车高频踩坑排查清单7.1 带空格的路径总是被拆分Linux 命令行下路径里有空格却不加引号是出现频率最高的错误之一。例如mkdir /tmp/My Documents/projectshell 会把这个命令拆成三个参数/tmp/My、Documents/project在/tmp下建出名为My的目录然后在当前目录下建Documents/project。要想正确创建必须把完整路径用引号包起来mkdir /tmp/My Documents/project使用-p时同理。只要路径中任何一个层级包含空格就老老实实给整个参数加引号。7.2 Read-only file system权限无关的陷阱如果 mkdir 报错信息是Read-only file system先别急着去chmod——这个错误和权限位几乎没有关系而是文件系统本身被挂载成了只读。常见原因包括NFS 只读挂载、系统启动时根分区被以只读方式挂载、U 盘写保护开关、磁盘文件系统有损坏被自动降级为只读。排查命令是mount或findmnt看对应路径的挂载选项里有没有ro。如果是临时问题可以用mount -o remount,rw /path重新挂载为读写如果是磁盘损坏那就要先处理文件系统而不是硬着头皮建目录。7.3 符号链接把目录建到了意想不到的位置当路径中间存在符号链接时mkdir -p的默认行为是跟随链接继续创建。举个例子ln -s /data/real /opt/app mkdir -p /opt/app/logs这个命令实际上会在/data/real/logs下创建目录而不是在/opt下新建一个app目录再往里面塞logs。如果你本意是想在链接旁边建目录这个行为就会和你预期完全不同。排查方法是先ls -ld看路径上有没有-符号理清链接关系再决定路径怎么写。7.4 File exists 的另一种解读mkdir报File exists时很多人第一反应是目录已经存在了嘛正常。但这里有个容易误判的细节目录存在时普通 mkdir 确实会报这个错可如果目标路径上已经存在一个同名文件报错信息和目录存在完全一样。所以看到File exists不要急着下结论用ls -ld看一眼路径对应的到底是一个目录还是一个普通文件。如果是文件占着这个名字你要么换个目录名要么先把文件处理掉。7.5 目录权限 r、w、x 各自对应的能力边界目录上的r、w、x和文件上的语义完全不同这也是共享目录权限问题频发的根源。简单对照r能列出目录中有哪些条目但仅此而已。w能增删目录中的条目但实际操作通常还依赖目标条目的权限。x能进入目录能访问目录中的文件是权限链上最关键的一环。举个例子一个只有r权限的目录你ls能看到文件名但cd进不去无法读取里面的文件内容一个只有w权限的目录理论上可以创建和删除文件但进不去也列不出来因为缺少x。很多我对目录有读写权限但就是进不去的问题细查下来都是目录权限写成了0644——r-x都没有自然被挡在门外。创建共享目录时至少给0755others 能读能进需要写入再考虑0775或2775。最后聊一点我对 mkdir 的长期使用体会。这命令本身三秒就能学会难的是把它放进真实环境后不踩坑——权限计算、umask、磁盘只读、引号、符号链接、临时目录安全每一件事单拎出来都是小问题叠在一起就可能变成线上事故的导火索。我现在的习惯基本固定成了四件事脚本里一律mkdir -p共享目录顺手加 setgid临时目录只用mktemp -d带变量的路径永远加引号。这套组合用下来因为目录创建翻车的次数基本归零了。如果你也在 Linux 下做开发或运维不妨把这几点记进自己的命令习惯里比背一堆冷门参数实用得多。
返回列表