1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

echo コマンドを改造して GNU/Linux (GNU Coreutils) の理解を深める

Last updated at Posted at 2025-09-20

この記事について

この記事は echo コマンドのソースコードを書き換えて動かすハンズオンです。
書き換える過程で触れる技術の理解を深めることを目的としています。

こんな感じの処理に書き換えます

animation.png

映画『マトリックス』(原題: The Matrix) の冒頭でトリニティがネオにコンタクトを取るシーンが好きなのでオマージュしてみました。

こんな人におすすめ

  • GNU/Linux が好き
  • ディストリビューション標準のパッケージの知見を深めたい
  • GNU Coreutils ともっと仲良くなりたい
  • C のプログラムに触ってみたい
  • Makefile でプログラムのコンパイルしてみたい

作業環境

  • MacOS on Macbook M1
  • Ubuntu 24.04 on Docker

Docker 上で完結するので Windows 環境の場合はコンテナの準備手順を適当に読み替えてください。

コンテナ準備

コンテナイメージを引っ張ってきます。
特に何でもいいですが今回はメジャーなところで Ubuntu の最新LTS(※執筆時点)である 24.04 を使います。

$ docker pull ubuntu:24.04 
24.04: Pulling from library/ubuntu
59a5d47f84c3: Download complete 
Digest: sha256:353675e2a41babd526e2b837d7ec780c2a05bca0164f7ea5dbbd433d21d166fc
Status: Downloaded newer image for ubuntu:24.04
docker.io/library/ubuntu:24.04

What's next:
    View a summary of image vulnerabilities and recommendations → docker scout quickview ubuntu:24.04

イメージ確認したらコンテナを起動してシェルにアタッチ。

$ docker images -f reference=ubuntu:24.04
REPOSITORY   TAG       IMAGE ID       CREATED      SIZE
ubuntu       24.04     353675e2a41b   8 days ago   139MB
$ docker run --name ubuntu -it ubuntu bash
root@3fabf328c64a:/# 

ここから先はすべてコンテナのシェル上での作業です。

ディストリビューションに含まれている echo の確認

echo の動作確認。

root@3fabf328c64a:/# echo hogehoge
hogehoge

実行ファイルのパスを調べます。

aptリポジトリから対象パッケージを検索するためにファイルパスを使用します。

root@3fabf328c64a:/# which echo
/usr/bin/echo
root@3fabf328c64a:/# ls -l /usr/bin/echo
-rwxr-xr-x 1 root root 67792 Jun 22 16:21 /usr/bin/echo

シンボリックリンクも貼られていないので /usr/bin/echo が実態と見てOK。

apt-file のインストール

実行ファイルが提供されている apt パッケージを特定するために apt-file を使います。
ディストリビューションには最初から含まれないので apt でダウンロードしましょう。

まずはお約束。
最新のパッケージリストを取得してパッケージをすべて更新しておきます。

root@3fabf328c64a:/# apt update && apt upgrade -y

...(略)...

インストール。

root@3fabf328c64a:/# apt install -y apt-file  

...(略)...

apt-file 自体もパッケージリストを参照するので最新を取得します。
(というかインストール直後だからパッケージリスト自体存在してないはず)

root@3fabf328c64a:/# apt-file update

...(略)...

それでは /usr/bin/echo が提供されるパッケージを調べていきます。

root@3fabf328c64a:/# apt-file -F search `which echo`
coreutils: /usr/bin/echo

分かりきってたことですが echo コマンドが含まれているパッケージは coreutils です。

ディストリビューションによっては付属のシェルにビルトインで実装されているケースも存在するので確認するに越したことはないです。
(昔 WSL2 で使っていた Ubuntu が確かこれでした)

Coreutils の詳細はこんな感じ。
見たところで今必要な情報は特にないです。(強いて言えばバージョン分かるくらい)

root@3fabf328c64a:/# apt show coreutils
Package: coreutils
Version: 9.4-3ubuntu6.1
Priority: required
Essential: yes
Section: utils
Origin: Ubuntu
Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
Original-Maintainer: Michael Stone <mstone@debian.org>
Bugs: https://bugs.launchpad.net/ubuntu/+filebug
Installed-Size: 9691 kB
Pre-Depends: libacl1 (>= 2.2.23), libattr1 (>= 1:2.4.48), libc6 (>= 2.38), libgmp10 (>= 2:6.3.0+dfsg), libselinux1 (>= 3.1~), libssl3t64 (>= 3.0.0)
Breaks: usrmerge (<< 39)
Homepage: http://gnu.org/software/coreutils
Task: minimal, server-minimal
Download-Size: 1364 kB
APT-Manual-Installed: yes
APT-Sources: http://ports.ubuntu.com/ubuntu-ports noble-updates/main arm64 Packages
Description: GNU core utilities

N: There is 1 additional record. Please use the '-a' switch to see it

deb-src 有効化

/usr/bin/echo の提供パッケージが Coreutils であると特定できたので、これをダウンロードするために deb-src を

従来の作法に則って /etc/apt/sources.list を確認したところ、

# Ubuntu sources have moved to the /etc/apt/sources.list.d/ubuntu.sources
# file, which uses the deb822 format. Use deb822-formatted .sources files
# to manage package sources in the /etc/apt/sources.list.d/ directory.とのこと。

とのことでファイルが /etc/apt/sources.list.d/ubuntu.sources に移動し、書式は deb822 に変更されています。

ubuntu.sources のコメントには、

## Types: Append deb-src to enable the fetching of source package.

とあるので Types: debTypes: deb deb-src に変更して deb-src を有効化します。

このハンズオンでは vim を使ってますが、それ以外のテキストエディタでも大丈夫です。
vim はディストリビューションに含まれてないと思うので、使う場合は入れておいてください。

root@3fabf328c64a:/# apt install -y vim
root@3fabf328c64a:/# vi /etc/apt/sources.list.d/ubuntu.sources
- Types: deb
+ Types: deb deb-src
URIs: http://ports.ubuntu.com/ubuntu-ports/
Suites: noble noble-updates noble-backports
Components: main universe restricted multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

## Ubuntu security updates. Aside from URIs and Suites,
## this should mirror your choices in the previous section.
- Types: deb
+ Types: deb deb-src
URIs: http://ports.ubuntu.com/ubuntu-ports/
Suites: noble-security
Components: main universe restricted multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
root@3fabf328c64a:~/coreutils-9.4# 

deb822 の書式は apt マニュアルの『THE DEB AND DEB-SRC TYPES: GENERAL FORMAT』 に記載があります。

The format for two one-line-style entries using the deb and deb-src types is:

deb [ option1=value1 option2=value2 ] uri suite [component1] [component2] [...]
deb-src [ option1=value1 option2=value2 ] uri suite [component1] [component2] [...]

Alternatively the equivalent entry in deb822 style looks like this:

Types: deb deb-src
URIs: uri
Suites: suite
Components: [component1] [component2] [...]
option1: value1
option2: value2

deb-src を有効化したのでパッケージリストを更新。

root@3fabf328c64a:/# apt update

...(略)...

Coreutils のソースコードをリポジトリからダウンロード

ルートディレクトリに落とすのは忍びないのでホームディレクトリに移動してダウンロードします。

恐らく apt source 実行時に以下のエラーで怒られると思います。

E: Unpack command 'dpkg-source --no-check -x coreutils_9.4-3ubuntu6.1.dsc' failed.
N: Check if the 'dpkg-dev' package is installed.

怒られたら Notice に従って dpkg-dev をインストールしましょう。

root@3fabf328c64a:/# apt install -y dpkg-dev
root@3fabf328c64a:/# cd ~
root@3fabf328c64a:~# apt source coreutils
Reading package lists... Done
Need to get 6023 kB of source archives.
Get:1 http://ports.ubuntu.com/ubuntu-ports noble-updates/main coreutils 9.4-3ubuntu6.1 (dsc) [2030 B]
Get:2 http://ports.ubuntu.com/ubuntu-ports noble-updates/main coreutils 9.4-3ubuntu6.1 (tar) [5979 kB]
Get:3 http://ports.ubuntu.com/ubuntu-ports noble-updates/main coreutils 9.4-3ubuntu6.1 (diff) [41.3 kB]
Fetched 6023 kB in 2s (2774 kB/s)
dpkg-source: info: extracting coreutils in coreutils-9.4
dpkg-source: info: unpacking coreutils_9.4.orig.tar.xz
dpkg-source: info: unpacking coreutils_9.4-3ubuntu6.1.debian.tar.xz
dpkg-source: info: using patch list from debian/patches/series
dpkg-source: info: applying 72_id_checkngroups.patch
dpkg-source: info: applying cp-n.diff
dpkg-source: info: applying 80_fedora_sysinfo.patch
dpkg-source: info: applying 99_float_endian_detection.patch
dpkg-source: info: applying treat-devtmpfs-and-squashfs-as-dummy-filesystems.patch
dpkg-source: info: applying tail-fix-tailing-sysfs-files-where-PAGE_SIZE-BUFSIZ.patch
dpkg-source: info: applying CVE-2024-0684.patch
dpkg-source: info: applying suppress-permission-denied-errors-on-nfs.patch
W: Download is performed unsandboxed as root as file 'coreutils_9.4-3ubuntu6.1.dsc' couldn't be accessed by user '_apt'. - pkgAcquire::Run (13: Permission denied)

ls でダウンロード結果を確認。
なおダウンロード時に Permission denied の警告が出ていましたが目的のものはダウンロードできているので今回は無視します。

root@3fabf328c64a:~# ls
coreutils-9.4  coreutils_9.4-3ubuntu6.1.debian.tar.xz  coreutils_9.4-3ubuntu6.1.dsc  coreutils_9.4.orig.tar.xz

先ほど確認した apt show coreutilsVersion: 9.4-3ubuntu6.1 とあったように、ディレクトリ名は coreutils-9.4 で作成されています。

なお GNU Coreutils のホームページを見たら執筆時点の最新版は 9.7 でした

Coreutils のソースを確認

早速中身を見ていきましょう。
なおここから先は GNU Coreutils プロジェクトの世界のお話です。(GNU プロジェクトの Coreutils サブプロジェクトって言った方が正しいかも)

root@3fabf328c64a:~# cd coreutils-9.4/

目的の echo のソースは src/echo.c です。

root@3fabf328c64a:~/coreutils-9.4# wc -l src/echo.c 
277 src/echo.c

Version 9.4 時点で 277 行。

個人的な感想:
echo ってロジック自体がシンプルなのでソースコード自体のボリュームもコンパクトで、コマンドのオプション引数の実装の仕方とかを参考にする上で見やすくて教材としてすごく良いプログラムだと思ってます。

余談ですが src 配下の .c extension のファイルを眺めて見ると Coreutils 全体のボリューム感とか知れて面白いですよ。

↓例えばこんな感じとか。

root@3fabf328c64a:~/coreutils-9.4# wc -l src/*.c | sort -nr

echo コマンド改造 Step 1: 引数の出力ごとに0.1秒待つ

vim で編集します。

root@3fabf328c64a:~/coreutils-9.4# vi src/echo.c 

300行に満たないとは言え全文載せると長くなるのと、数行程度の書き換えなので抜粋します。

プログラム冒頭
#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include "system.h"
#include "assure.h"
+ #include <unistd.h>
just_echo 内
just_echo:

  if (do_v9 || posixly_correct)
    {
      //
      // 省略
      //
    }
  else
    {
      while (argc > 0)
        {
+         usleep(0.1 * 1000 * 1000);
          fputs (argv[0], stdout);
+         fflush(stdout);
          argc--;
          argv++;
          if (argc > 0)
            putchar (' ');
        }
    }

  if (display_return)
    putchar ('\n');
  return EXIT_SUCCESS;

インストール

INSTALL を参照して手順を確認します。

root@3fabf328c64a:~/coreutils-9.4# head -n 13 INSTALL
Installation Instructions
*************************

Basic Installation
==================

   The following shell commands:

     test -f configure || ./bootstrap
     ./configure
     make
     make install

test コマンド実行。

root@3fabf328c64a:~/coreutils-9.4# test -f configure || ./bootstrap

次は configure を実行。
ですがエラーが発生しました。

root@3fabf328c64a:~/coreutils-9.4# ./configure 

...(略)...

checking whether mknod can create fifo without root privileges... configure: error: in '/root/coreutils-9.4':
configure: error: you should not run configure as root (set FORCE_UNSAFE_CONFIGURE=1 in environment to bypass this check)
See 'config.log' for more details

エラーメッセージに従って環境変数を設定。

root@3fabf328c64a:~/coreutils-9.4# export FORCE_UNSAFE_CONFIGURE=1

文脈と環境変数名からして本来的にはroot権限じゃなくてユーザ権限で実行することを想定してるはず。
ただし今回はコンテナ環境なのでこのあたりのケアを気にせず進めます。

というわけで、もう一回 configure を実行。

root@3fabf328c64a:~/coreutils-9.4# ./configure 

...(略)...

今度はエラー発生せずに終了しました。
make を実行。
そしてまたエラー。

root@3fabf328c64a:~/coreutils-9.4# make
 cd . && /bin/bash /root/coreutils-9.4/build-aux/missing automake-1.16 --gnu Makefile
/root/coreutils-9.4/build-aux/missing: line 81: automake-1.16: command not found
WARNING: 'automake-1.16' is missing on your system.
         You should only need it if you modified 'Makefile.am' or
         'configure.ac' or m4 files included by 'configure.ac'.
         The 'automake' program is part of the GNU Automake package:
         <https://www.gnu.org/software/automake>
         It also requires GNU Autoconf, GNU m4 and Perl in order to run:
         <https://www.gnu.org/software/autoconf>
         <https://www.gnu.org/software/m4/>
         <https://www.perl.org/>
make: *** [Makefile:8842: Makefile.in] Error 127

お次は automake-1.16 が無いとのこと。
なので入れます。

root@3fabf328c64a:~/coreutils-9.4# apt install -y automake

...(略)...

入れました。
make リトライ。

root@3fabf328c64a:~/coreutils-9.4# make

...(略)...

今度はエラー発生せずに終了しました。
最後に make install 実行。

root@3fabf328c64a:~/coreutils-9.4# make install

インストール終了しました。

make[3] の結果を見るに、インストール先は /usr/local/bin/ 配下です。

make install の標準出力より抜粋
make[3]: Entering directory '/root/coreutils-9.4'
 /usr/bin/mkdir -p '/usr/local/bin'
  src/ginstall -c src/ginstall '/usr/local/bin/./install'
  src/ginstall -c src/chroot src/hostid src/timeout src/nice src/who src/users src/pinky src/stty src/df src/stdbuf src/[ src/b2sum src/base64 src/base32 src/basenc src/basename src/cat src/chcon src/chgrp src/chmod src/chown src/cksum src/comm src/cp src/csplit src/cut src/date src/dd src/dir src/dircolors src/dirname src/du src/echo src/env src/expand src/expr src/factor src/false src/fmt src/fold src/groups src/head src/id src/join src/kill src/link src/ln src/logname src/ls src/md5sum src/mkdir src/mkfifo src/mknod src/mktemp src/mv src/nl src/nproc src/nohup src/numfmt src/od src/paste src/pathchk src/pr src/printenv src/printf src/ptx src/pwd src/readlink src/realpath src/rm src/rmdir src/runcon src/seq src/sha1sum src/sha224sum src/sha256sum src/sha384sum src/sha512sum src/shred src/shuf src/sleep src/sort src/split src/stat src/sum src/sync src/tac src/tail src/tee src/test src/touch src/tr src/true src/truncate src/tsort src/tty src/uname src/unexpand src/uniq src/unlink src/uptime src/vdir src/wc src/whoami src/yes '/usr/local/bin'
 /usr/bin/mkdir -p '/usr/local/libexec/coreutils'
  src/ginstall -c src/libstdbuf.so '/usr/local/libexec/coreutils'

実行してみる

animation.png

引数単位でちゃんとスリープしてくれています。

echo コマンド改造 Step 2: 1文字出力するごとに0.1秒待つ

root@3fabf328c64a:~/coreutils-9.4# vi src/echo.c 

just_echo:else 配下のみ抜粋

just_echo 内
just_echo:

  if (do_v9 || posixly_correct)
    {
      //
      // 省略
      //
    }
  else
    {
      while (argc > 0)
        {
-         usleep(0.1 * 1000 * 1000);
-         fputs (argv[0], stdout);
-         fflush(stdout);
+         char const *s = argv[0];
+         unsigned char c;
+         while ((c = *s++))
+           {
+             usleep(0.1 * 1000 * 1000);
+             putchar (c);
+             fflush(stdout);
+           }
          argc--;
          argv++;
          if (argc > 0)
            putchar (' ');
        }
    }

サクッとキャッシュ削除 & 再コンパイル

root@3fabf328c64a:~/coreutils-9.4# make clean && make distclean && test -f configure || ./bootstrap && ./configure && make && make install

実行してみる

animation.png

完成です。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?