見出し画像

【マイグレーションTips】OSバージョンアップ時はシェルのビルトインコマンドに注意(RHEL8へのバージョンアップ)


はじめに


皆さん、こんにちは。
株式会社SHIFT アプリケーションサービスGの村田です。
主にお客様が使用するコンピュータシステムのマイグレーション作業支援を担当しています。

マイグレーションTipsと題して、マイグレーション作業を行う技術者に向けて役に立つと考えられる情報を選んで紹介しています。

※「マイグレーションって何?」と思われた方は、以下の記事で説明していますので先にお読み頂けると嬉しいです。

【現象】RedHat LinuxをRHEL8にバージョンアップしたら、今まで稼働していたシェルスクリプトがエラー終了した!


今回紹介する事例は、OSバージョンアップに伴ってシェルスクリプトを移植する時に遭遇した問題への解決方法です。

  • システム:RedHat Linuxのバージョンアップ(RHEL6.5→RHEL8.2)

  • 言語:シェル(mksh)

  • 事象:RHEL6.5で稼働中のシステムをRHEL8.2に刷新。
    RHEL6.5で稼働中のシェルスクリプトをRHEL8.2の環境に移植してテストしたら、RHEL8.2ではエラー終了した

マイグレーション作業では、OSやDBMSのバージョンアップに対応するためにまず新旧の仕様差異を調べます。そして仕様差異に影響を受けるプログラムやシェルスクリプトがあれば必要な改修を行ってから動作確認するという流れを取ります。

もちろん今回もこれらの作業は実施済みでしたが、仕様差異の影響はないと判断していたシェルスクリプトで問題が起きました。

何が起きたのか、簡単なサンプルコードで説明しましょう。

-- sample.sh --

#!/bin/mksh
# 初期設定

INIT_PROC

# ファイル監視
i=0

while [ $i -lt 1000 ]
do
	KANSHI_PROC
	i=`expr $i + 1`
	sleep 10m
done

~

問題が起きたシェルスクリプトから、ポイントとなるコードを抽出しました。
シェルスクリプトを起動すると、10分毎に関数KANSHI_PROCの中の処理を繰り返します。
OSの仕様差異調査では問題なかったため、RHEL 6.5、RHEL 8.2それぞれでテストしてみたところ…

RHEL6.5は正常終了しますが、RHEL8.2ではシェルスクリプト内のsleepコマンドがエラーを検知しました。
RHEL6.5とRHEL8.2で実行したシェルスクリプトの内容は一致しており、一見どこも悪いようには見えません。
一体何が問題なのでしょう。

【原因】OSバージョンアップにより、同名のコマンドがビルトインコマンドに追加された!


本件はシェルスクリプトで呼び出すsleepコマンドがOSバージョンアップによって変わったことが原因でした。

<原因>
RHEL8.2のmkshにて、ビルトインコマンドとしてsleepが追加された

RHEL6.5では、シェルスクリプトで「sleep」を記述するとOSで標準装備されているsleepコマンドを実行していましたが、RHEL8.2のmkshではmkshのビルトインコマンドsleepを実行するように動作が変わっていました。

== RHEL6.5 sleepコマンド ==
RHEL6 $ echo $SHELL
/bin/mksh
RHEL6 $ type sleep
sleep is /bin/sleep
RHEL6 $ 
== RHEL8.2 sleepコマンド ==
RHEL8 $ echo $SHELL
/usr/bin/mksh
RHEL8 $ type sleep
sleep is a shell builtin
RHEL8 $ 

OS標準装備のsleepコマンドとビルトインコマンドのsleepには実は仕様差があり、RHEL8.2ではビルトインコマンドsleepが動いたことでエラーを検知したことがわかりました。

詳しく説明しましょう。
サンプルコードにあるsleepコマンドの記述は以下のとおりでした。

	sleep 10m

sleepコマンドの第1引数には、待ち合わせる(動きを止める)時間を指定します。 RHEL6.5でOS標準装備のsleepコマンドの説明から、ここで10分待つという動きを期待していることがわかります。

RHEL6 $ man sleep
sleep(1)

名前
    sleep - 指定した時間だけ遅延する
書式
    sleep [number[smhd]...]
    sleep [--help] [--version]
説明
    sleep はコマンドラインで指定した引数の値を足した時間の分だけ停止して待つ。  
    引数は数値で、あとに単位を続けることもできる。  
    デフォルトは秒。単位は以下の通り:
    s    秒
    m    分
    h    時間
    d    日
~

RHEL8.2では、mkshのビルトインコマンドsleepが動作します。ビルトインコマンドsleepの説明は以下のとおりでした。

RHEL8 $ man mksh
MKSH(1)

NAME
    mksh, sh -- MirBSD Korn Shell
~
    Builtins that are not special:
    ※訳(特別ではない組み込み)
    [, alias, bg, bind, cat, cd, command, echo, false, fc, fg, getopts, jobs,
    kill, let, mknod, print, pwd, read, realpath, rename, sleep, test, true, 
    ulimit, umask, unalias, whence
~
    sleep seconds
        Suspends execution for a minimum of the seconds specified as positive decimal value 
        with an optional fractional part. Signal delivery may continue execution earlier.
        ※訳(オプションの小数部分を含む正の10進数値として指定された最小秒数の間、
          実行を一時停止します。シグナル配信は、より早く実行を継続する場合があります。)
~

ビルトインコマンドのsleepでは第1引数に指定する待ち合わせ時間として「秒数の数値のみ」が指定可能。ここにOS標準装備のsleepコマンドとの仕様差がありました。
問題のシェルスクリプトでは「単位を示す英字(s,m,h,d)」を指定していたことから、sleepコマンドがエラーを検知しました。

ちなみにRHEL8.2にもOS標準装備のsleepコマンドは存在します。RHEL6.5時代と仕様差はありませんので、こちらが動作すればOSをバージョンアップしたとしても同じ動作を行います。
しかしシェルスクリプトに相対パスでコマンドを記載した場合、コマンドサーチパスの優先順位に従って実行するコマンドを探します。
コマンドサーチパスの優先順位は、ビルトインコマンドが最優先です。そのため環境変数PATHに記載したディレクトリに同一名称のコマンドがあった場合でも、RHEL8.2ではビルトインコマンドを実行するという形になってしまいました。

【対策】sleepコマンドの記述を見直す


この問題の対策は、sleepコマンドの記述を見直します。

  • [対策1] 待ち合わせ時間の指定方法をビルトインコマンドの仕様に合わせる
    ビルトインコマンドの仕様にあわせて、sleepコマンドの第1引数を秒数指定に変更します。

#	sleep 10m         # 対策前 
	sleep 600         # 対策後 10分 → 600秒
  • [対策2] OS標準装備のsleepコマンドを明示的に実行する
    sleepコマンドを完全パスで指定して、OS標準装備のコマンドを動作させるようにします。

#	sleep 10m           # 対策前 
	/usr/bin/sleep 10m  # 対策後 完全パス指定

どちらの対策もシェルスクリプトの修正が必要なため優劣はありません。しかし今後の保守を見据えて、システム内では対策方法を統一することが重要ですね。

【関連事項】ビルトインコマンドのラインアップはシェル毎に異なる


Linuxではmksh以外にもシェルが存在します。実はシェルの種類によりビルトインコマンドのラインアップは異なります。

シェルの種類とsleepコマンドの対応状況を以下に示します。

今回のマイグレーション対象システムではシェルとしてmkshを使っていたため、問題となりました。
ビルトインコマンドの名称は、readなどOS提供のコマンドと同じ名称のものが多く、今後のOSバージョンアップでも類似事例が発生する可能性があります。
新環境に移植したシェルスクリプトがエラーを検知した時は、シェルのビルトインコマンドが原因となっていないか、についても確認するようにして下さい。

最後に


マイグレーションTipsとして「OSバージョンアップ時はシェルのビルトインコマンドに注意(RHEL8へのバージョンアップ)」をご紹介しましたが、いかがだったでしょうか。

Redhat Linuxは、古いバージョンのサービス提供終了(EOSL(End of Service Life))が迫ってきたこともあり、RHEL8やRHEL9へのバージョンアップに伴うマイグレーション案件が増えてきています。

  • RHEL6 延長ライフサポート(ELS) 2024年6月30日終了。以降サポートなし

  • RHEL7 メンテナンスサポート2  2024年6月30日終了
    延長ライフサポート(ELS) 2028年5月31日終了。以降サポートなし

マイグレーションに携わる技術者の皆さんに、この記事が目に留まり少しでも役立てて頂ければ幸いです。

マイグレーションTipsの記事一覧


執筆者プロフィール:村田 博之
IT業界に入って40年突破。データベース関連ミドルウェアの開発・保守(20年間)やメインフレームおよびオープン環境での社内システム開発・保守SE(合計19年間)に従事した後、SHIFTに⼊社。
現在は数々の案件対応で蓄積した知⾒と経験を活かして、マイグレーション関連のプロジェクトに参画している。
特種情報処理技術者、データベーススペシャリスト

マイグレーション支援サービスのご紹介

株式会社SHIFTでは、コンピュータシステムのマイグレーションを円滑に進めるための支援サービスを提供しています。
マイグレーションに関する支援サービスの詳細は以下のとおりです。
システムのマイグレーションをご検討、または課題解決に困っている方がいらっしゃったら、以下のページよりお気軽に問い合わせいただければ幸いです。

お問合せはお気軽に

SHIFTについて(コーポレートサイト)
https://www.shiftinc.jp/

SHIFTのサービスについて(サービスサイト)
https://service.shiftinc.jp/

SHIFTの導入事例
https://service.shiftinc.jp/case/

お役立ち資料はこちら
https://service.shiftinc.jp/resources/

SHIFTの採用情報はこちら
https://recruit.shiftinc.jp/career/

みんなにも読んでほしいですか?

オススメした記事はフォロワーのタイムラインに表示されます!