Bash/Zshで&&連結した全てのコマンドに環境変数を一時的に適用する方法

## はじめに
以下のような&&で連結されたコマンドの2つめのコマンドに対して`NODE_ENV=production`を適用したい、という状況があったのだが、当初想定している方法が利用できなかったので、試行錯誤した。

```shell
$ yarn && webpack --mode=production
```

なぜ2つ目のコマンドに環境変数を受け渡したいかというと、Webpackの設定ファイルwebpack.config.js内で`process.env.NODE_ENV`として`NODE_ENV`環境変数を参照しているためであった。

一番簡単なのは以下のように`export`を利用する方法だが、一時的に適用する方法が知りたかったので調査した。

```shell
$ export NODE_ENV=production
$ yarn && webpack --mode=production
```

## 検証環境

```shell
$ bash --version
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin18)
Copyright (C) 2007 Free Software Foundation, Inc.
```

以下のshellスクリプトを利用して、検証する。

```shell
$ cat hoge.sh
#!/bin/bash
echo "$1: $HOGE"
```

以下のようにコマンドを連結し、first/secondの後に`HOGE`環境変数の値が両方表示されることを最終目標とする。

```shell
$ export HOGE=true
$ ./hoge.sh first && ./hoge.sh second
first: true
second: true
```

## 結論

2通り方法があるが、どちらもシェルでラップするという発想。

### 1. サブシェルを利用

```shell
$ (export HOGE=true; ./hoge.sh first && ./hoge.sh second)
first: true
second: true

$ echo $HOGE

※出力なし(カレントシェルには影響を与えていない)
```

### 2. bash/zshの`-c`オプションを利用

```shell
$ HOGE=true bash -c './hoge.sh first && ./hoge.sh second'
first: true
second: true

$ echo $HOGE

※出力なし(カレントシェルには影響を与えていない)
```

## 検証過程

参考までに結論に到るまでの検証過程を以下に示す。

まずはじめに思いついたのは、一時的に環境変数をコマンドに適用する方法。
調べてみると、この実行方法はパラメータとして指定するというものらしい。

```shell
$ HOGE=true ./hoge.sh first
first: true
```

しかし、&&で連結すると、2つ目のコマンドにはパラメータとして設定できていない。

```shell
$ HOGE=true ./hoge.sh first && ./hoge.sh second
first: true
second:
```

指定するにはそれぞれのコマンドに対して指定する必要があった。

```shell
$ HOGE=true ./hoge.sh first && HOGE=true ./hoge.sh second
first: true
second: true
```

いや、でもコマンドをまとめる方法もあるよね、と思って以下を実行すると

```shell
$ HOGE=true (./hoge.sh first && ./hoge.sh second)
bash: syntax error near unexpected token `('
```

シンタックスエラーだった。

という過程を経て、シェルでラップする方法にたどり着いた。

## おまけ

検証方法を検討する中で、わざわざ`hoge.sh`使わなくてもいいかなと思って以下のコマンドを考えた。

```shell
$ HOGE=true echo "first: ${HOGE}" && echo "second: ${HOGE}"
first:
second:
```

が、予想に反して`first: true`が表示されなかった。

よくよく考えるとパラメータとしてコマンドに環境変数を設定する方法だと、カレントシェルには`HOGE`環境変数が定義されていないことになる為。失敗ではあるが、仕組みがわかって面白い。

## 参考

### Bashのmanページ

```shell
$ man bash
BASH(1)                                                                                                                                                                                                                                BASH(1)

NAME
       bash - GNU Bourne-Again SHell

SYNOPSIS
       bash [options] [file]

COPYRIGHT
       Bash is Copyright (C) 1989-2005 by the Free Software Foundation, Inc.

DESCRIPTION
       Bash is an sh-compatible command language interpreter that executes commands read from the standard input or from a file.  Bash also incorporates useful features from the Korn and C shells (ksh and csh).

       Bash is intended to be a conformant implementation of the Shell and Utilities portion of the IEEE POSIX specification (IEEE Standard 1003.1).  Bash can be configured to be POSIX-conformant by default.
       
…

SHELL GRAMMAR
…
   Lists
       A list is a sequence of one or more pipelines separated by one of the operators ;, &, &&, or ||, and optionally terminated by one of ;, &, or <newline>.

       Of these list operators, && and || have equal precedence, followed by ; and &, which have equal precedence.

       A sequence of one or more newlines may appear in a list instead of a semicolon to delimit commands.

       If a command is terminated by the control operator &, the shell executes the command in the background in a subshell.  The shell does not wait for the command to finish, and the return status is 0.  Commands separated by  a  ;  are
       executed sequentially; the shell waits for each command to terminate in turn.  The return status is the exit status of the last command executed.

       The control operators && and || denote AND lists and OR lists, respectively.  An AND list has the form

              command1 && command2

       command2 is executed if, and only if, command1 returns an exit status of zero.

       An OR list has the form

              command1 || command2

       command2 is executed if and only if command1 returns a non-zero exit status.  The return status of AND and OR lists is the exit status of the last command executed in the list.

   Compound Commands
       A compound command is one of the following:

       (list) list  is  executed in a subshell environment (see COMMAND EXECUTION ENVIRONMENT below).  Variable assignments and builtin commands that affect the shell's environment do not remain in effect after the command completes.  The
              return status is the exit status of list.
…
PARAMETERS
       A parameter is an entity that stores values.  It can be a name, a number, or one of the special characters listed below under Special Parameters.  A variable is a parameter denoted by a name.  A variable has a  value  and  zero  or
       more attributes.  Attributes are assigned using the declare builtin command (see declare below in SHELL BUILTIN COMMANDS).

       A parameter is set if it has been assigned a value.  The null string is a valid value.  Once a variable is set, it may be unset only by using the unset builtin command (see SHELL BUILTIN COMMANDS below).

       A variable may be assigned to by a statement of the form

              name=[value]

       If  value  is  not given, the variable is assigned the null string.  All values undergo tilde expansion, parameter and variable expansion, command substitution, arithmetic expansion, and quote removal (see EXPANSION below).  If the
       variable has its integer attribute set, then value is evaluated as an arithmetic expression even if the $((...)) expansion is not used (see Arithmetic Expansion below).  Word splitting is not performed, with the exception  of  "$@"
       as explained below under Special Parameters.  Pathname expansion is not performed.  Assignment statements may also appear as arguments to the alias, declare, typeset, export, readonly, and local builtin commands.

       In the context where an assignment statement is assigning a value to a shell variable or array index, the += operator can be used to append to or add to the variable's previous value.  When += is applied to a variable for which the
       integer attribute has been set, value is evaluated as an arithmetic expression and added to the variable's current value, which is also evaluated.  When += is applied to an array  variable  using  compound  assignment  (see  Arrays
       below),  the  variable's value is not unset (as it is when using =), and new values are appended to the array beginning at one greater than the array's maximum index.  When applied to a string-valued variable, value is expanded and
       appended to the variable's value.

…

ENVIRONMENT
       When a program is invoked it is given an array of strings called the environment.  This is a list of name-value pairs, of the form name=value.

       The  shell provides several ways to manipulate the environment.  On invocation, the shell scans its own environment and creates a parameter for each name found, automatically marking it for export to child processes.  Executed com-
       mands inherit the environment.  The export and declare -x commands allow parameters and functions to be added to and deleted from the environment.  If the value of a parameter in the environment is modified, the new  value  becomes
       part  of  the  environment,  replacing the old.  The environment inherited by any executed command consists of the shell's initial environment, whose values may be modified in the shell, less any pairs removed by the unset command,
       plus any additions via the export and declare -x commands.

       The environment for any simple command or function may be augmented temporarily by prefixing it with parameter assignments, as described above in PARAMETERS.  These assignment statements affect only the  environment  seen  by  that
       command.

       If the -k option is set (see the set builtin command below), then all parameter assignments are placed in the environment for a command, not just those that precede the command name.

       When bash invokes an external command, the variable _ is set to the full file name of the command and passed to that command in its environment.
```