0
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?

More than 1 year has passed since last update.

sh script.sh と bash script.sh では動きが違います

Last updated at Posted at 2024-01-11

※不正確。コメントみたほうがいいです

どういうこと

Linux のように bash しかない OS があります。sh コマンドは使えるのですが、 /bin/sh や /usr/bin/sh が /usr/bin/bash へのシンボリックリンクになっていて、プログラムのバイナリは同一なのです。

バイナリが同一だからといって動きは同じではありません。

bash の文法には伝統的な sh から相変わらず使えるものと、bash で独自に拡張されたものがあります。bash 拡張は sh という名前で起動されたときには使えないのです。

bash 拡張構文の例

コマンドの結果をコマンドにリダイレクトする

bash
bash$ wc <(ls -l)
     15     128     867 /dev/fd/63
sh
sh$ wc <(ls -l)
sh: 1: Syntax error: "(" unexpected

標準出力と標準エラー出力を両方パイプでプログラムに流し込む

bash
bash$ ruby -e 'puts("stdout"); STDERR.puts("stderr")' | wc
stderr
      1       1       7
bash$ ruby -e 'puts("stdout"); STDERR.puts("stderr")' |& wc
      2       2      14```
sh
sh$ ruby -e 'puts("stdout"); STDERR.puts("stderr")' | wc
stderr
      1       1       7
sh$ ruby -e 'puts("stdout"); STDERR.puts("stderr")' |& wc
sh: 2: Syntax error: "&" unexpected

FORTRAN みたいな回数指定の for ループ

bash
$ for ((k=1; k<=10; k++)) ; do echo $k ; done
1
2
3
4
5
6
7
8
9
10
sh
$ for ((k=1; k<=10; k++)) ; do echo $k ; done
sh: 1: Syntax error: Bad for loop variable

bash マニュアルページの記述

このことは bash のマニュアルページの "INVOCATION" の節で、 "sh" という名前で起動されたときには posix モードになる、というふうに説明されています。

If bash is invoked with the name sh, it tries to mimic the
startup behavior of historical versions of sh as closely as
possible, while conforming to the POSIX standard as well. When
invoked as an interactive login shell, or a non-interactive shell
with the --login option, it first attempts to read and execute
commands from /etc/profile and ~/.profile, in that order.
(設定ファイルに関する話が続くので略)
When invoked as sh,
bash enters posix mode after the startup files are read.

そんで posix mode だと何が変わるという記述は、マニュアルページのあちこちに散りばめられていて、正直拾う気力がありません。マニュアルページの最後に次のウェブを見ろとあるのですが、これはシェル組み込みコマンドの動作変更一覧のようです。

構文的な差異については、次の POSIX のシェルの規定に書いていない機能はあてにするな、と考えるのがよいでしょう。

sh という名前で起動されるって何

ところで動作を変える条件、「sh という名前で起動される」というのは何でしょうか。

  • コマンドラインから sh script.sh のようにスクリプトを起動する場合
  • コマンドラインから sh -c 'commands ...' のようにコマンドを起動する場合
    (上記、sh とあるところは /usr/bin/sh や /bin/sh としても同様)
  • スクリプト先頭行に #!/bin/sh と書いて実行可能ビットを立ててコマンドとする場合
    (/bin/sh の代わりに /usr/bin/sh などとしても同様)

せっかくだからbashのソースコードを追う

bash は C 言語で書かれていますから容易に読むことができます。
main 関数は shell.c にあって、main 関数の引数 argv のうちコマンド名にあたる argv[0] が set_shell_name 関数に渡されます。

その中では base_pathname 関数によって「最後のスラッシュ以下のファイル名」が抜き出されて、それが "sh" であれば act_like_sh フラグが立てられます。

bash shell.c
static void
set_shell_name (argv0)
     char *argv0;
{
  /* Here's a hack.  If the name of this shell is "sh", then don't do
     any startup files; just try to be more like /bin/sh. */
  shell_name = argv0 ? base_pathname (argv0) : PROGRAM;

  if (argv0 && *argv0 == '-')
    {
      if (*shell_name == '-')
	shell_name++;
      login_shell = 1;
    }

  if (shell_name[0] == 's' && shell_name[1] == 'h' && shell_name[2] == '\0')
    act_like_sh++;
0
0
2

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
0
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?