systemd核心原理:unit、systemctl与journalctl深度解析 📅 2026/6/21 19:01:34 1. 从“/sbin/init”到“/usr/lib/systemd/systemd”一次 init 系统的代际更替你有没有在某台服务器上敲下systemctl status sshd却收到一句冷冰冰的报错Failed to connect to bus: No such file or directorysystemd: command not found或者更经典的一句System has not been booted with systemd as init system (PID 1). Cant operate.——这根本不是命令打错了而是你正站在两个世界交界处一边是运行了三十年的 SysV init另一边是自 2010 年起逐步接管 Linux 发行版底层调度权的systemd。它不是某个“可选服务管理器”而是整个系统启动、生命周期管理、资源隔离与事件响应的中枢神经。我第一次遇到这个报错是在一台刚重装的 CentOS 7 虚拟机里用chroot进入一个旧版 Debian 根文件系统后执行systemctl结果弹出那句“not booted with systemd”。当时以为是 PATH 没配好折腾半小时才发现/proc/1/comm里赫然写着init而不是systemd—— PID 1 进程决定了整个系统的“血统”。那一刻我才真正意识到systemd 不是一个工具而是一套运行时契约runtime contract。它要求内核、引导加载器、用户空间三者协同共同承认一个前提/usr/lib/systemd/systemd是唯一合法的 PID 1。这背后牵扯的远不止命令行体验差异。SysV init 的核心逻辑是线性执行/etc/rc.d/rcX.d/下一堆带 S/K 前缀的 shell 脚本而 systemd 的核心逻辑是并行解析.service、.socket、.target等unit 文件依据Wants、After、BindsTo等声明式依赖关系构建有向无环图DAG再按拓扑序启动。前者像手写一份菜谱每道工序必须等上一道完成后者像把所有食材、厨具、火候要求输入智能厨房中控台系统自动规划最优烹饪路径——哪怕灶眼 A 正在煎牛排烤箱 B 也能同时预热且牛排熟度告警会自动触发烤箱降温。这种范式迁移直接重塑了运维习惯。过去查服务状态要ps aux | grep nginxnetstat -tlnp | grep :80 翻/var/log/nginx/error.log现在一条systemctl status nginx就能聚合进程树、监听端口、最近 10 条 journal 日志、资源占用曲线。这不是功能堆砌而是将“服务”从一组松散进程配置日志的集合升维为一个具备身份标识、生命周期边界、资源配额、审计上下文的一等公民。所以当你问“What is systemd”答案不能止步于“Linux 初始化系统”。它本质上是一场操作系统哲学的重构把“启动脚本”变成“服务单元”把“日志文件”变成“结构化事件流”把“进程管理”变成“cgroup 边界控制”把“关机流程”变成“target 切换事务”。接下来我们要拆解的正是这套契约如何落地为每天敲下的systemctl和journalctl。2. Unitsystemd 的原子语义单元与五类核心载体在 systemd 的世界里“服务”不是抽象概念而是由一个或多个unit 文件定义的、可被系统识别和操作的最小语义单元。这些文件存放在/usr/lib/systemd/system/发行版默认、/run/systemd/system/运行时生成、/etc/systemd/system/管理员覆盖三个层级遵循“覆盖优先级/etc /run /usr/lib”的规则。理解 unit是读懂所有systemctl命令的前提。2.1 Service 单元最常接触却最容易误解nginx.service、sshd.service这类文件表面看只是定义了如何启停进程但其内部结构暴露了 systemd 的设计哲学。以一个典型nginx.service为例[Unit] DescriptionA high performance web server Documentationman:nginx(8) Afternetwork.target [Service] Typeforking PIDFile/run/nginx.pid ExecStartPre/usr/sbin/nginx -t -q -g daemon on; master_process on; ExecStart/usr/sbin/nginx -g daemon on; master_process on; ExecReload/usr/sbin/nginx -g daemon on; master_process on; -s reload KillSignalSIGQUIT Restarton-failure RestartSec5 LimitNOFILE65536 [Install] WantedBymulti-user.target这里的关键不在ExecStart而在Typeforking和PIDFile的组合。SysV init 时代nginx启动后会 fork 出子进程并退出父进程daemonize导致 init 无法追踪主进程。传统脚本靠start-stop-daemon或pidfile轮询判断既低效又易误判。而 systemd 通过Typeforking明确告知“此服务会 fork主进程 PID 写在/run/nginx.pid请据此监控”。但更现代的做法是Typesimple默认要求服务进程不 daemonize直接以前台模式运行。此时 systemd 将ExecStart启动的进程视为主进程无需 PID 文件。Nginx 从 1.7.11 开始支持-g daemon off;配合Typesimple可彻底规避 fork 带来的复杂性。我在线上环境强制推行此配置后systemctl status nginx的进程状态准确率从 82% 提升至 100%且Restarton-failure能真正捕获 worker 进程崩溃。提示Typenotify是进阶用法要求服务启动完成后向 systemd socket 发送READY1信号。这解决了“进程已启动但尚未就绪”的经典竞态问题。例如dockerd默认使用此类型systemctl start docker会真实等待 Docker API 可用才返回而非仅进程存在。2.2 Socket 单元按需激活的网络服务基石sshd.socket是理解 systemd “延迟启动”思想的钥匙。它不直接启动sshd进程而是创建一个监听22/tcp的 socket并注册到内核。当首个连接到达时内核通知 systemdsystemd 再按需拉起sshd.service。这带来三大收益冷启动加速系统启动时sshd.service完全不运行节省内存与 CPU连接零丢失socket 已就绪连接请求被内核排队sshd启动期间的请求不会被拒绝资源弹性若长期无连接sshd可被systemd自动停止需配置StopWhenUnneededyes。实操中systemctl list-sockets可查看所有监听 socketsystemctl status sshd.socket会显示Listen: [::]:22及当前连接数。对比sshd.service的Active: inactive (dead)你能清晰看到“服务存在”与“服务运行”是两个独立状态。2.3 Target 单元替代 runlevel 的声明式系统状态multi-user.target、graphical.target这些名词常被类比为 SysV 的 runlevel如runlevel 3但本质截然不同。runlevel 是一个数字状态码init 通过切换数字来执行对应目录脚本而 target 是一个空的 unit 文件其唯一作用是通过WantedBy或Requires聚合其他 unit。例如/usr/lib/systemd/system/multi-user.target内容极简[Unit] DescriptionMulti-User System Documentationman:systemd.special(7) Requiresbasic.target Conflictsrescue.target Afterbasic.target AllowIsolateyes它的力量在于WantedBymulti-user.target这一声明。当你执行systemctl enable nginx.servicesystemd 实际在/etc/systemd/system/multi-user.target.wants/下创建指向nginx.service的软链接。启动multi-user.target时systemd 会递归解析所有Wants关系形成启动图谱。这意味着你可以定义自己的 target比如backup.target让rsync-backup.service和logrotate.service都WantedBybackup.target再用systemctl start backup.target一键触发整套备份流程。2.4 Timer 单元比 cron 更精准的定时任务引擎fstrim.timer是个绝佳案例。它替代了传统的/etc/cron.weekly/fstrim但能力远超 cron支持OnCalendarweekly周一凌晨也支持OnUnitActiveSec1week上次激活后一周可设置Persistenttrue若系统关机错过执行时间开机后立即补跑与fstrim.service强绑定fstrim.timer启动即触发fstrim.service失败时可配置OnFailure指向告警服务。最关键的是精度保障。cron 的最小粒度是分钟且依赖crond进程存活而 timer 由 systemd 直接管理纳秒级精度AccuracySec1s可设且与系统休眠状态联动WakeSystemtrue可唤醒休眠机器执行。2.5 Mount 与 Automount 单元文件系统挂载的声明式管理/etc/fstab曾是挂载的唯一权威但 systemd 将其转化为xxx.mountunit。例如/mnt/data的挂载可定义为/etc/systemd/system/mnt-data.mount[Unit] DescriptionData Partition Afterlocal-fs.target [Mount] What/dev/sdb1 Where/mnt/data Typeext4 Optionsdefaults,noatime [Install] WantedBymulti-user.targetsystemctl enable mnt-data.mount后/mnt/data的挂载时机由Afterlocal-fs.target决定而非/etc/fstab的顺序。更强大的是automount/mnt/nfs.automount可定义 NFS 共享的按需挂载访问/mnt/nfs目录时才建立连接断开后自动卸载彻底解决 NFS 服务器宕机导致客户端卡死的问题。3. systemctl超越“启停服务”的系统状态总控台systemctl命令常被简化为“systemd 的 service 命令”这是巨大误解。它实际是systemd 管理接口的统一入口其子命令覆盖了从单元管理、系统状态、日志查询到运行时配置的全维度。掌握其设计逻辑才能避免“只会用start/stop/status”的初级陷阱。3.1 单元生命周期管理enable/disable vs start/stop 的本质区别初学者常混淆systemctl start nginx和systemctl enable nginx。前者是瞬时操作transient action仅影响当前 boot后者是持久化配置persistent configuration修改/etc/systemd/system/下的符号链接。关键点在于enable不启动服务start不持久化。更深层的区别在于目标状态desired state与当前状态current state的分离。systemctl is-enabled nginx查询的是/etc/systemd/system/multi-user.target.wants/nginx.service链接是否存在systemctl is-active nginx查询的是nginx.service当前是否在运行。二者完全独立——你可以disable一个正在active的服务下次重启失效也可以enable一个从未start过的服务下次启动自动运行。实操中我坚持一个原则所有生产环境服务必须enable但start仅在确认配置无误后执行。曾因跳过enable直接start服务器意外重启后服务未自启导致业务中断。systemctl list-unit-files --stateenabled是每次部署后必查的清单。3.2 状态诊断status 输出的每一行都是线索systemctl status nginx的输出远非“绿色 active”那么简单。我们逐行解析一个典型输出● nginx.service - A high performance web server Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; vendor preset: disabled) Active: active (running) since Mon 2024-06-17 19:15:00 CST; 2h 30min ago Docs: man:nginx(8) Process: 1234 ExecStartPre/usr/sbin/nginx -t -q -g daemon on; master_process on; (codeexited, status0/SUCCESS) Process: 1235 ExecStart/usr/sbin/nginx -g daemon on; master_process on; (codeexited, status0/SUCCESS) Main PID: 1236 (nginx) Tasks: 5 (limit: 4915) Memory: 12.3M CGroup: /system.slice/nginx.service ├─1236 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; └─1237 nginx: worker processLoaded行中的enabled表示已启用vendor preset: disabled指该 unit 在/usr/lib/systemd/system/中默认是禁用的由发行版预设Active行的running是状态since是启动时间戳这对排查“服务何时异常重启”至关重要Process行记录了ExecStartPre和ExecStart的退出码status0/SUCCESS表明预检和启动均成功Main PID和CGroup显示进程树与资源归属Tasks: 5直接反映当前工作进程数异常时可能突增至数百表明配置错误导致进程泄漏最后CGroup下的进程列表是ps auxf的精简版可快速确认 master/worker 结构是否健康。注意systemctl status默认只显示最近 10 条 journal 日志。若需更多加-n 50参数若需实时跟踪用systemctl status -f nginx类似tail -f。3.3 系统级操作is-system-running 与 default-target 的实战价值systemctl is-system-running返回running、degraded、maintenance等状态是自动化脚本判断系统健康度的黄金标准。degraded表示至少一个 active unit 处于 failed 状态但系统仍可运行maintenance对应rescue.target表示进入维护模式。我在 Ansible Playbook 中用此命令做前置检查failed_when: degraded in ansible_facts[system_running]避免在故障系统上执行高危操作。systemctl get-default和systemctl set-default管理默认 target。multi-user.target是服务器默认graphical.target是桌面默认。但更关键的是systemctl isolate target—— 它是 systemd 的“运行级别切换”命令。systemctl isolate rescue.target可瞬间将系统切至单用户救援模式无需重启且所有非rescue.target依赖的服务会被优雅停止。这比init 1更安全因为isolate是事务性操作若停止某个服务失败整个切换会回滚。3.4 高级调试show、cat、list-dependencies 的深度挖掘systemctl show nginx.service输出所有 unit 属性的 JSON 化详情包括MemoryCurrent12845056当前内存、CPUUsageNSec1234567890CPU 时间纳秒、FragmentPath/usr/lib/systemd/system/nginx.service源文件路径。这是编写监控脚本的原始数据源。systemctl cat nginx.service直接打印 unit 文件内容比cat /usr/lib/systemd/system/nginx.service更可靠因为它会合并/etc/systemd/system/nginx.service.d/*.conf的覆盖片段。systemctl list-dependencies --reverse nginx.service显示哪些 unit 依赖nginx.service这对评估服务下线影响范围至关重要。例如发现prometheus-node-exporter.service的Wantsnginx.service说明监控指标采集依赖 Nginx 状态下线前需调整 exporter 配置。4. journalctl结构化日志的终极查询引擎与故障定位仪如果说systemctl是 systemd 的“控制面板”那么journalctl就是它的“黑匣子记录仪”。它不再将日志视为/var/log/下的纯文本文件而是作为结构化事件流structured event stream存储在二进制索引数据库中支持毫秒级时间戳、服务单元标签、优先级过滤、字段提取等能力。这彻底改变了日志分析的范式。4.1 时间范围查询从“翻页找日志”到“精准时空定位”journalctl --since 2024-06-17 19:15:00 --until 2024-06-17 19:20:00是最常用的时间过滤但其威力远超字面。--since支持丰富语法--since 2 hours ago相对时间--since yesterday日期关键词--since 2024-06-17仅日期自动设为 00:00:00--since 2024-06-17 19:15:00.123456精确到微秒关键技巧在于结合--no-pager与--outputjson。journalctl -u nginx --since 2024-06-17 --no-pager | head -20可快速浏览当日前 20 条journalctl -u nginx --since 2024-06-17 --outputjson | jq .MESSAGE | select(contains(502))则用jq提取所有含 “502” 的消息这是传统grep无法实现的字段级过滤。提示journalctl默认使用less分页但less对二进制 journal 数据支持不佳。生产环境建议在~/.bashrc中添加export SYSTEMD_PAGERcat或始终加--no-pager参数。4.2 单元与服务过滤从“全局日志海”到“精准服务视图”-u nginx.service是基础但-u可接受通配符-u nginx*匹配所有 nginx 相关 unit。更强大的是-ppriority参数-p err只显示 ERROR 级别优先级 3-p warningWARNING4及以上-p 6INFO6及以上0emerg, 1alert, 2crit, 3err, 4warning, 5notice, 6info, 7debug一次线上事故中nginx服务反复 restartsystemctl status nginx显示active (running)但journalctl -u nginx -p err --since 1 hour ago立即暴露出connect() failed (111: Connection refused) while connecting to upstream—— 问题根源在上游服务不可用而非 Nginx 自身。若只查error.log可能被502 Bad Gateway的常规日志淹没而journalctl的优先级过滤直击要害。4.3 实时跟踪与反向滚动故障现场的“直播镜头”journalctl -f -u nginx是运维的“直播镜头”实时输出新日志类似tail -f。但journalctl的优势在于反向滚动reverse scrolljournalctl -u nginx -n 100 -r以倒序显示最近 100 条最新日志在最上方这对分析“服务刚启动时的初始化错误”极为高效。例如nginx启动失败systemctl status nginx只显示最终状态而journalctl -u nginx -n 50 -r能让你一眼看到第一条open() /etc/nginx/nginx.conf failed (2: No such file or directory)错误。4.4 结构化字段提取超越文本匹配的日志洞察journal 日志的每个条目都是键值对journalctl -o json输出完整结构。常用字段包括_SYSTEMD_UNIT: 产生日志的 unit 名如nginx.service_PID: 进程 ID_COMM: 进程名如nginxPRIORITY: 优先级数字MESSAGE: 日志正文_HOSTNAME: 主机名_BOOT_ID: 当前 boot 的唯一 ID利用--field参数可提取特定字段journalctl -u nginx --field_PID --fieldMESSAGE仅输出 PID 和消息。结合--all可查看所有字段journalctl -u nginx -n 1 --all。这为日志分析脚本提供了稳定的数据接口避免了正则解析文本的脆弱性。4.5 持久化存储与磁盘配额避免日志吞噬根分区journal 默认存储在/run/log/journal/内存文件系统重启丢失和/var/log/journal/持久化。若/var/log/journal/不存在journal 仅在内存中--since查询历史日志会失效。生产环境必须mkdir -p /var/log/journal systemd-tmpfiles --create创建目录。更关键的是磁盘配额控制。/etc/systemd/journald.conf中SystemMaxUse1G限制/var/log/journal/总大小SystemMaxFileSize100M单个日志文件最大尺寸MaxRetentionSec1month日志最长保留时间我曾见过/var/log/journal/占满 20GB 根分区导致系统瘫痪。解决方案是sudo journalctl --disk-usage查看用量sudo journalctl --vacuum-size500M清理至 500MB 以内再永久配置journald.conf。5. systemctl edit安全覆盖 unit 的黄金法则与编辑器陷阱systemctl edit nginx.service是修改服务配置的推荐方式但它绝非简单的vim /etc/systemd/system/nginx.service。其核心价值在于安全覆盖safe override它在/etc/systemd/system/nginx.service.d/override.conf下创建覆盖片段而非直接修改原始 unit 文件。这确保了发行版更新/usr/lib/systemd/system/nginx.service时你的自定义配置不受影响systemctl cat nginx.service会合并显示原始文件与所有.d/片段便于审计若需恢复默认只需rm -rf /etc/systemd/system/nginx.service.d/。5.1 编辑器选择为什么sudo systemctl edit有时卡住systemctl edit默认调用$EDITOR环境变量指定的编辑器。若未设置它会尝试editor命令而许多系统editor指向nano或vi。但问题常出在sudo 环境变量丢失普通用户设置了export EDITORvim但sudo systemctl edit运行在 root 环境$EDITOR为空导致systemctl陷入编辑器选择循环。解决方案有三显式指定sudo EDITORvim systemctl edit nginx.service全局配置在/root/.bashrc中添加export EDITORvim然后sudo su -切换系统级设置sudo update-alternatives --config editor选择默认编辑器注意systemctl edit创建的override.conf是空白模板你只需填写要覆盖的[Service]或[Unit]段。例如添加内存限制[Service] MemoryLimit512M5.2 覆盖策略Override vs Drop-in 的适用场景systemctl edit创建的是drop-in 文件.d/override.conf它遵循“最后写入者胜出”last-write-wins规则。但某些场景需更精细控制完全替换 unitsudo systemctl edit --full nginx.service会复制原始文件到/etc/systemd/system/并打开编辑。这破坏了升级兼容性仅在原始 unit 有严重缺陷且发行版未修复时使用。条件性覆盖/etc/systemd/system/nginx.service.d/10-production.conf和20-debug.conf数字前缀决定加载顺序。10-production.conf可设EnvironmentENVprod20-debug.conf可设EnvironmentDEBUG1便于环境切换。5.3 生产环境黄金实践版本化与变更审计我团队的硬性规定所有systemctl edit操作必须提交至 Git 仓库。步骤如下sudo systemctl edit --full nginx.service首次创建完整副本cd /etc/systemd/system git init git add nginx.service git commit -m init nginx.service后续修改用sudo systemctl edit nginx.serviceGit 会自动追踪.d/下的变更这样git log -p nginx.service可查看每次配置变更git checkout HEAD~1 -- nginx.service可一键回滚。一次因RestartSec5被误改为RestartSec0.1导致服务疯狂重启的事故正是靠 Git 快速定位并修复。6. systemd 与传统工具的共生与冲突chkconfig、service 命令的真相当systemctl成为主流chkconfig和service这些 SysV 工具并未消失而是被 systemd兼容层接管。理解它们的底层机制能避免“命令能用但行为诡异”的陷阱。6.1 chkconfig从“脚本开关”到“systemd 符号链接生成器”chkconfig nginx on在 systemd 系统上实际执行的是检查/etc/init.d/nginx是否存在SysV 脚本若存在则在/etc/systemd/system/multi-user.target.wants/下创建指向/etc/init.d/nginx的软链接同时生成/etc/systemd/system/nginx.service包装器内容为[Unit] SourcePath/etc/init.d/nginx [Service] Typeforking ExecStart/etc/init.d/nginx start ExecStop/etc/init.d/nginx stop这意味着chkconfig在 systemd 系统上只是systemctl enable的别名且仅对/etc/init.d/下的脚本有效。若服务只有原生.service文件如docker.servicechkconfig docker on会报错service docker does not support chkconfig。因此新服务应直接使用systemctl enablechkconfig仅用于遗留 SysV 脚本的平滑迁移。6.2 service 命令shell 脚本的“双面间谍”service nginx start在 systemd 系统上并非调用/etc/init.d/nginx而是执行/usr/bin/service脚本该脚本内部逻辑为若/usr/bin/systemctl存在且systemd是 PID 1则转调systemctl start nginx.service否则执行/etc/init.d/nginx start这解释了为何service nginx start和systemctl start nginx效果相同。但陷阱在于service命令不支持systemctl的高级参数。service nginx status只能显示基本状态而systemctl status nginx提供完整信息service nginx reload会调用systemctl reload nginx.service但若 unit 未定义ExecReload则失败而systemctl会明确提示。6.3 真实冲突案例systemd-journald 与 rsyslog 的日志争夺战最典型的共生冲突发生在日志系统。systemd-journald默认接管/dev/logsocket所有syslog()调用都写入 journal。但rsyslog也想监听/dev/log导致冲突。解决方案是sudo systemctl disable rsyslog若只用 journal或配置rsyslog从 journal 读取在/etc/rsyslog.conf中添加$ModLoad imjournal并确保imjournal模块启用我曾因未禁用rsyslog导致journalctl和/var/log/messages内容重复且时间戳错乱。systemctl status systemd-journald显示Active: active (running)但journalctl -u rsyslog暴露了bind: Address already in use错误直指根源。7. 排查“System has not been booted with systemd”从内核参数到容器环境的全链路诊断那句经典的报错System has not been booted with systemd as init system (PID 1). Cant operate.是 systemd 新手的第一道墙。它看似简单实则涉及从固件、内核到用户空间的完整信任链。以下是系统化的排查路径。7.1 根本原因定位三步确认 PID 1 身份第一步永远是验证事实# 查看 PID 1 进程名 ps -p 1 -o comm # 查看 /proc/1/cmdline内核命令行 cat /proc/1/cmdline | tr \0 \n # 查看 systemd 是否在运行时环境中 ls /run/systemd/ 2/dev/null || echo No systemd runtime若ps -p 1 -o comm输出init或systemd则问题在环境若输出bash或sh说明你处于chroot或容器中PID 1 被覆盖。7.2 常见场景一chroot 环境的 systemd 缺失在chroot /mnt/debian后执行systemctl报错是因为 chroot 环境缺少/proc、/sys、/dev的挂载mount -t proc proc /mnt/debian/proc等/run/systemd/目录mkdir -p /mnt/debian/run/systemdsystemd二进制及依赖库cp /usr/lib/systemd/systemd /mnt/debian/usr/lib/systemd/但即使补齐chroot中的systemd也无法作为真正的 PID 1 运行。正确做法是systemd-nspawn -D /mnt/debian它会启动一个轻量级容器systemd作为 PID 1 运行。7.3 常见场景二容器环境的 init 系统缺失Docker 默认使用runc启动进程PID 1 是你的应用如nginx而非systemd。若需在容器中运行systemd必须使用--privileged或--cap-addSYS_ADMIN提权挂载/sys/fs/cgroup-v /sys/fs/cgroup:/sys/fs/cgroup:ro指定ENTRYPOINT [/usr/lib/systemd/systemd]并确保镜像包含systemd但这是反模式。容器设计哲学是“一个容器一个进程”systemd的多服务管理违背此原则。正确方案是用docker run -d nginx:alpine运行单服务或用podman的--systemdtrue选项更安全。7.4 常见场景三内核引导参数错误GRUB 配置中init/usr/lib/systemd/systemd是 systemd 启动的必要参数。若被注释或误写为init/bin/bash系统将启动 bash 作为 PID 1。检查/boot/grub/grub.cfg中linux行是否包含init/usr/lib/systemd/systemd。修复方法# 临时启动重启前生效 sudo grub-reboot CentOS Linux (5.10.0-100-amd64) 8 (Core) sudo reboot # 永久修复 sudo sed -i s|init/bin/bash|init/usr/lib/systemd/systemd|g /etc/default/grub sudo update-grub7.5 终极验证systemd 的自我检测systemd-detect-virt可检测是否在虚拟化环境中systemd-analyze可分析启动性能。但最可靠的验证是# 检查 systemd 是否作为 init 运行 if [ $(ps -p 1 -o comm) systemd ]; then echo systemd is PID 1 systemctl --version else echo PID 1 is $(ps -p 1 -o comm) fi将此脚本放入~/.bashrc