Linuxバイナリ更新時の「Text file busy」エラーの解決方法

初級🐧 Linux2026-07-09| 標準的なファイルシステム(Ext4やXFSなど)を使用するLinux (Ubuntu, Debian, CentOS, RHEL, Arch)。

Error Message

cp: cannot create regular file '/usr/local/bin/app': Text file busy
#linux#sysadmin#devops#troubleshooting#bash

問題のシナリオGoサービスやC++ツールの新しいビルドを本番サーバーにデプロイしている場面を想像してください。古いバイナリを上書きするために、標準的なコピーコマンドを実行します。

cp /tmp/new_version /usr/local/bin/app

クリーンな更新が行われる代わりに、ターミナルに以下のようなイライラさせるエラーが表示されます。

cp: cannot create regular file '/usr/local/bin/app': Text file busy

Linuxがこの保護機能をトリガーするのは、プロセスがアクティブに実行されている間、kernelがバイナリの内容の変更を拒否するためです。もしkernelが実行中の上書きを許可してしまうと、実行中のアプリケーションは、足元のコードが変更されることでクラッシュしたり、メモリの破損に遭遇したりする可能性が高くなります。

ステップ 1: ロックしているプロセスの特定ロックを解除するには、まずファイルを保持しているプロセスを突き止める必要があります。主に2つのツールで簡単に実行できます。

fuserを使用するfuser ユーティリティは、特定のファイルやディレクトリを使用しているプロセスを特定します。多くの場合、PIDを取得する最も速い方法です。

fuser /usr/local/bin/app

次のような出力が表示されることがあります。

/usr/local/bin/app:  4812e

この例では、4812 がプロセスIDです。e というサフィックスは、そのファイルが実行可能ファイルとして使用されていることを示しています。

lsofを使用するユーザーや特定のコマンド名など、より詳細なコンテキストが必要な場合は、lsof (List Open Files) を使用します。

lsof /usr/local/bin/app

これにより、プロセス名、ユーザー、ファイル記述子のタイプを示す詳細なテーブルが表示されます。

ステップ 2: 手動アプローチ(停止して置換)最も単純なアプローチは、いわゆる「強硬手段」です。停止し、入れ替えて、再起動します。これは、ホットリロードをうまく処理できないアプリケーションにとって最も安全です。

  • プロセスを終了する: kill 4812- 停止したことを確認し、新しいバージョンをコピーする: ``` cp /tmp/new_version /usr/local/bin/app
### 「rm」戦略既存のファイルを開いて切り詰めようとする `cp` を使う代わりに、まず `rm` を使用します。

rm /usr/local/bin/app cp /tmp/new_version /usr/local/bin/app


実行中のバイナリを `rm` すると、そのファイルのディレクトリエントリが削除されます。しかし、実際のデータはプロセスが実行されている限りディスク上に残ります。新しい `cp` コマンドは、完全に新しいinodeを作成します。既存のプロセスはメモリ内の古いコードを実行し続け、新しく起動されたプロセスは新しいバージョンを使用します。
### 「install」コマンド`install` コマンドは、これを一括で処理するプロフェッショナルな方法です。コピーする前に、コピー先のアンリンクを自動的に行います。

install -m 755 /tmp/new_version /usr/local/bin/app


これは、「busy」エラーを完全に回避できるため、多くの Makefile や CI/CD パイプラインにおける標準的な手法となっています。
## ステップ 4: Systemdによる管理`systemd` で管理されている本番サービスの場合、更新を再起動ロジックに組み込む必要があります。これにより、ログや子プロセスをクリーンな状態に保つことができます。

sudo systemctl stop my-app sudo install -m 755 /tmp/new_version /usr/local/bin/app sudo systemctl start my-app


## 検証ファイルのメタデータとプロセス状態を確認して、更新が成功したことを確かめます。
- タイムスタンプを確認する: ```
ls -l /usr/local/bin/app
```- バージョン文字列を確認する: ```
/usr/local/bin/app --version
```- プロセスマップを確認する: ```
ls -l /proc/$(pgrep app_name)/exe
``` もしシンボリックリンクがパスを指しているものの、`(deleted)` が含まれている場合、そのプロセスはアンリンクしたファイルの古いバージョンをまだ実行しています。新しいコードに切り替えるには再起動が必要です。

Related Error Notes