gnu,bsdコマンドの差を吸収しよう


linuxとその他のunix、例えばmacなど両方を使う
環境の人も多いだろう。

その場合はgnuコマンドなのか、bsdコマンドなのかで混乱することも多い。
また、LinuxでもDebian系はgawkでなく、mawkという少機能だが、速度が改善されている
awkを使っている。普通のプロジェクトでmawkじゃないとダメということはなく、
むしろgawkで使いたい関数が使えない弊害のほうが大きいため、これもgawkで置き換える。

ほとんどの場合はgnuコマンドの方が使い勝手が良く、
ソースコードも簡潔になる。

この二つのコマンドの差を吸収する方法を書く。

やり方

コマンド名のfunctionを定義して、そのコマンドの中でgnuコマンドを実行すると
gnuコマンドとbsdコマンドの差を吸収できる。
aliasでコマンド名を定義すると、対話形式のみで有効になり、
ファイルを読み込んで実行することができないので、functionで定義する方法しか取れない。

下のような関数が良いだろう。

function sed {
  gsed "$@"
}

またこの上書き処理を~/.bashrcなどに直接書くと影響範囲が広過ぎて
既存の処理を邪魔する可能性がある。
よって、使いたいときにだけgnu,bsdコマンドの吸収する処理を読んで、
好きなときに変更を破棄する処理があると良い。

.bashrcに上書き関数群を読み込む関数の定義を書くか、
関数群を読み込むファイルをsourceすると上手くいくか。

このやり方だと、子プロセスで差を吸収する処理を読んでも、
親やその他のプロセスに影響がないため、使いやすいはず。

ソースコード

$HOME/.bashrc
# 関数群を読み込むコマンド
source $HOME/bash/function/read_function
$HOME/bash/function/read_function
# $HOME/bash/function/配下のファイルをすべて読み込む関数
function read_function {

  local function_dir=$HOME/bash/function

  eval "$(
    ls ${function_dir} |
    grep -v .md |
    grep -v read_function |
    xargs -I {} echo source ${function_dir}/{};
  )"
  unset -f read_function
}

read_function

上のread_functionにより読み込まれる。

$HOME/bash/function/gnu_alias
#!/bin/bash
#
# gnu command util.

#######################################
# set alias gnu command for Mac and BSD.
# Globals:
#   GNU_ALIAS
# Arguments:
#   None
# Outputs:
#   None
# Returns:
#   0 if  set alias, non-zero on error.
# Example:
#   gnu_alias # => you use use sed
#######################################
function gnu_alias {
    for arg in "$@"; do
    case "$arg" in
      -h|--help)
        function usage() {
          cat 1>&2 << END
gnu_alias
output search engine setting

USAGE:
    gnu_alias [FLAGS] [OPTIONS]

FLAGS:
    -h, --help              Prints help information

OPTIONS:
    --debug                 Set bash debug Option
END
          unset -f usage
        }
        usage
        return
        ;;
      --debug)
        # set debug
        set -x
        trap "
          set +x
          trap - RETURN
        " RETURN
        ;;
      *)
        ;;
    esac
  done

  # you use Darwin, set alias for use Gnu sed, awk, xargs
  if uname | grep -e Darwin -e BSD > /dev/null; then
    if ! gnu_exists; then
      echo "Any Gnu command is not exists." >&2
      return 1
    fi

    function getopt {
      if uname | grep -e Open > /dev/null; then
        # openbsd
        /usr/local/bin/gnugetopt "$@"
      elif uname | grep -e Free > /dev/null; then
        # freebsd
        /usr/local/bin/getopt "$@"
      elif uname | grep -e Darwin > /dev/null; then
        # macos
        # darwin
        /usr/local/opt/gnu-getopt/bin/getopt "$@"
      fi
    }
    
    # alias not using shellscript in norepl.
    # and using function for alternative alias.
    function sed {
      gsed "$@"
    }
    function awk {
      gawk "$@"
    }
    function xargs {
      gxargs "$@"
    }
    function find {
      gfind "$@"
    }
    function date {
      gdate "$@"
    }
    function cut {
      gcut "$@"
    }
    function df {
      gdf "$@"
    }

    export GNU_ALIAS=yes
  # debian default awk is mawk.
  # alias gawk.
  elif ls /etc | grep debian_version >> /dev/null; then
    if ! gnu_exists; then
      echo "Any Gnu command is not exists." >&2
      return 1
    fi
    function awk {
      gawk "$@"
    }
    export GNU_ALIAS=yes
  fi
}

#######################################
# check exists gnu command alias gnu command for Mac and BSD.
# gsed, gawk, gxargs, gfind, ... and many
# Globals:
#   None
# Arguments:
#   None
# Outputs:
#   None
# Returns:
#   0 if exists gnu commands, non-zero on error.
# Example:
#   gnu_exists # => you use use sed
#######################################
function gnu_exists {
  # you use Darwin, set alias for use Gnu sed, awk, xargs
  if uname | grep -e Darwin -e BSD > /dev/null; then
    # gsedがなかったら、return 1

    # gnu-getopt
    if uname | grep -e Open > /dev/null; then
      # openbsd
      if ! type "/usr/local/bin/gnugetopt" > /dev/null; then
        echo "gnu-getopt is not exists." >&2
        return 1
      fi
    elif uname | grep -e Free > /dev/null; then
      # freebsd
      if ! type "/usr/local/bin/getopt" > /dev/null; then
        echo "gnu-getopt is not exists." >&2
        return 1
      fi
    elif uname | grep -e Darwin > /dev/null; then
      # macos
      # darwin
      if ! type "/usr/local/opt/gnu-getopt/bin/getopt" > /dev/null; then
        echo "gnu-getopt is not exists." >&2
        return 1
      fi
    else
      echo "unknown Unix!" >&2
      return 1
    fi

    if ! type "gsed" > /dev/null; then
      echo "gsed is not exists." >&2
      return 1
    fi
    if ! type "gawk" > /dev/null; then
      echo "gawk is not exists." >&2
      return 1
    fi
    if ! type "gxargs" > /dev/null; then
      echo "gxargs is not exists." >&2
      return 1
    fi
    if ! type "gfind" > /dev/null; then
      echo "gfind is not exists." >&2
      return 1
    fi
    if ! type "gdate" > /dev/null; then
      echo "gdate is not exists." >&2
      return 1
    fi
    if ! type "gcut" > /dev/null; then
      echo "gcut is not exists." >&2
      return 1
    fi
    if ! type "gdf" > /dev/null; then
      echo "gdf is not exists." >&2
      return 1
    fi
  # you use debian distribution, set alias awk.
  elif ls /etc | grep debian_version >> /dev/null; then
    if ! type "gawk" > /dev/null; then
      echo "gawk is not exists." >&2
      return 1
    fi
  fi
}

#######################################
# unalias gnu command for Mac.
# Globals:
#   GNU_ALIAS
# Arguments:
#   None
# Outputs:
#   None
# Returns:
#   0 if thing was deleted, non-zero on error.
# Example:
#   gnu_unalias # => you use use sed
#######################################
function gnu_unalias {
  # you use Darwin, set alias for use Gnu sed, awk, xargs
  if uname | grep -e Darwin -e BSD > /dev/null; then
    unset -f getopt
    unset -f sed
    unset -f awk
    unset -f xargs
    unset -f find
    unset -f date
    unset -f cut
    unset -f df
    unset GNU_ALIAS
  fi
  # you use debian distribution, set alias awk.
  if ls /etc | grep debian_version >> /dev/null; then
    
    unset -f awk
    unset GNU_ALIAS
  fi
}

使い方

好きなときに下記のように対話形式で読んで下記のように破棄すると良い。

# overwrite bsd command to gnu command.
gnu_alias

# gnuコマンドに置き換わっているので-iでsedの上書きができる。
sed -i.org /etc/hoge

# gnuコマンドが上書きされているかどうかは下記の環境変数に入っている。
# これをもとに判断する。
echo $GNU_ALIAS

# 関数の上書き解除
gnu_unalias

下記のように他のシェルスクリプトに組み込むのも良い。

g++find_include.sh
function g++find_include {

  source $(dirname $0)/../function/gnu_alias
  if ! gnu_alias; then
    return 1;
  fi
  ...
}

g++find_include

シェルスクリプト呼び出し。

bash g++find_include.sh

最新のソースコード

github