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 5 years have passed since last update.

パイプの前のプロセスが成功したときだけ処理したい2(Eitherっぽいパイプ)

Last updated at Posted at 2019-12-22

前の記事ではMaybeっぽいパイプを作成したので今度はEitherっぽいパイプを作ってみる。

シェルスクリプトを書いているとこんなコードを書いてなんだかなぁという気持ちになることがある。(シェル力の問題なのかもしれないが)

x=`command` 2>$TMPDIR/cmd_err || {
  my_logger "ERROR: command failed, ecode=$?, msg=`cat $TMPDIR/cmd_err`"
}

こんな感じでどうでもいい一時ファイルや変数は処理から見えないようにしたい。

# ケース1
x=`either command | fmap command | cmd 2>&1` || {
  my_logger "ERROR: hoge failed, ecode=$?, msg=$x"
}
# ケース2
either command | fmap command | either_case
in 0) cat -
;; *) my_logger 'ERROR: hoge failed, ecode=%d, msg=%s' $? $(cat)
either_esac

実装例

前回のコードを素直に拡張する。

標準入出力のフォーマット

# 成功の場合
either
0
<いつもの標準出力>

# 失敗の場合
either
<0以外の数値>
<標準エラー出力>

maybeの場合はエラーの場合は出力はなかったが、eitherの場合は(ややこしいが)エラー出力が標準出力に出力する。

関数

fmapやcmd関数は前回を参照

either() {
  local out=`mktemp` err=`mktemp`
  { rm $out $err
    "$@" >/dev/fd/3 2>/dev/fd/4 &&
      right cat /dev/fd/3 ||
      left  $? cat /dev/fd/4
  } 3>$out 4>$err
}

right() {
  echo either
  echo 0
  "$@"
  return 0
}

left() {
  local ret=$1
  shift
  echo either
  echo $ret
  "$@"
  return $ret
}

fmap_either() {
  bind_either either "$@"
}

bind_either() {
  local value
  read value
  case "$value"
  in 0) "$@"
  ;; *) left "$value" cat -
  esac
}

cmd_either() {
  local r
  read r || exit
  case $r
  in 0) cat -
  ;; *) cat - >&2
        return $r
  esac
}

_eithercase() {
  local r
  read && read r || exit
  return $r
}
alias either_case='{ _eithercase; case $?'
alias either_esac='esac; }'

使ってみる

$ x=`either a | fmap b | cmd 2>&1` || {
> echo "ERROR: command failed, ecode=$?, msg=$x"
> }
ERROR: command failed, ecode=127, msg=a: コマンドが見つかりません

これだと x=$(cmd 2>&1) と何が違うのかと言われそうだが、正常系の場合にxに変なワーニングが入り込まないことが保証されていることが異なる。

$ either echo a | fmap a | either_case
> in 0) cat -
> ;; *) echo "ERROR: command failed, ecode=$?, msg=$(cat)"
> either_esac
ERROR: command failed, ecode=127, msg=a: コマンドが見つかりません
$
$ either echo a | fmap grep a | either_case
> in 0) cat -
> ;; *) echo "ERROR: command failed, ecode=$?, msg=$(cat)"
> either_esac
a
0
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
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?