bash - simultaneous logging

#### simultaneous logging

bash script の実行時に、画面 (Stdout/Stderr) に表示される情報を、同時にログファイルにも記録したい場合があります。このような要件には、以下で対応することができます。
```shell
# bash.log に Stdout/Stderr を追記する
exec &> >(tee -a bash.log)
# OR
exec > >(tee -a bash.log) 2>&1
```

以下のようなサンプルを実行してみます。
```shell
#!/bin/bash

exec &> >(tee -a bash.log)

date
ls -1 1 2 3
ls -1 NO_FILE_FOR_STDERR
```
```shell
$ ls -l
total 1
-rwxrwxrwx. 1 root root   0 Dec  7 09:24 1
-rwxrwxrwx. 1 root root   0 Dec  7 09:24 2
-rwxrwxrwx. 1 root root   0 Dec  7 09:24 3
-rwxrwxrwx. 1 root root 117 Dec  7 09:24 bash_logging.sh

$ bash_logging.sh
Thu Dec  7 09:25:19 JST 2023
1
2
3
ls: cannot access 'NO_FILE_FOR_STDERR': No such file or directory

$ ls -l
total 1
-rwxrwxrwx. 1 root root   0 Dec  7 09:24 1
-rwxrwxrwx. 1 root root   0 Dec  7 09:24 2
-rwxrwxrwx. 1 root root   0 Dec  7 09:24 3
-rwxrwxrwx. 1 root root 101 Dec  7 09:25 bash.log
-rwxrwxrwx. 1 root root 117 Dec  7 09:24 bash_logging.sh

$ cat bash.log
Thu Dec  7 09:25:19 JST 2023
1
2
3
ls: cannot access 'NO_FILE_FOR_STDERR': No such file or directory

$ bash_logging.sh
Thu Dec  7 09:26:56 JST 2023
1
2
3
ls: cannot access 'NO_FILE_FOR_STDERR': No such file or directory

$ cat bash.log
Thu Dec  7 09:25:19 JST 2023
1
2
3
ls: cannot access 'NO_FILE_FOR_STDERR': No such file or directory
Thu Dec  7 09:26:56 JST 2023
1
2
3
ls: cannot access 'NO_FILE_FOR_STDERR': No such file or directory
$
```
#### bash 以外のケース
bash 以外の Shell では `exec` が使用できない場合があります。ここでは、ksh (KornShell) を見てみます。
```shell
#!/bin/ksh

exec &> >(tee -a bash.log)

date
ls -1 1 2 3
ls -1 NO_FILE_FOR_STDERR
```
```shell
$ diff bash_logging.sh ksh_logging.ksh
1c1
< #!/bin/bash
---
> #!/bin/ksh
3c3
< exec &> >(tee -a bash.log)
---
> exec &> >(tee -a ksh.log)

$ ksh -vx ksh_logging.ksh
#!/bin/ksh

exec &> >(tee -a ksh.log)

+ exec
+ ksh_logging.ksh[3]: cannot create [No such file or directory]
date
+ date
Thu Dec  7 09:45:12 JST 2023
ls -1 1 2 3
+ ls -1 1 2 3
1
2
3
ls -1 NO_FILE_FOR_STDERR
+ ls -1 NO_FILE_FOR_STDERR
ls: cannot access 'NO_FILE_FOR_STDERR': No such file or directory

$ ls -l
total 1
-rwxrwxrwx. 1 root root  0 Dec  7 09:24 1
-rwxrwxrwx. 1 root root  0 Dec  7 09:24 2
-rwxrwxrwx. 1 root root  0 Dec  7 09:24 3
-rwxrwxrwx. 1 root root 82 Dec  7 09:43 ksh_logging.ksh
```
`exec` の実行で `cannot create [No such file or directory]` エラーが発生して、ログファイルが作成されていないことが分かります。このような場合、以下のような `mkfifo` を使用する実装が可能です。
```shell
# MOD_ksh_logging.ksh
#!/bin/ksh

mkfifo ksh_fifo.fifo
tee -a ksh.log < ksh_fifo.fifo &
exec > ksh_fifo.fifo 2>&1

date
ls -1 1 2 3
ls -1 NO_FILE_FOR_STDERR
```
```shell
$ sdiff ksh_logging.ksh MOD_ksh_logging.ksh
#!/bin/ksh                                                      #!/bin/ksh

exec &> >(tee -a ksh.log)                                     | mkfifo ksh_fifo.fifo
                                                              > tee -a ksh.log < ksh_fifo.fifo &
                                                              > exec > ksh_fifo.fifo 2>&1

date                                                            date
ls -1 1 2 3                                                     ls -1 1 2 3
ls -1 NO_FILE_FOR_STDERR                                        ls -1 NO_FILE_FOR_STDERR
```
```shell
$ MOD_ksh_logging.ksh
Thu Dec  7 10:00:18 JST 2023
1
2
3
ls: cannot access 'NO_FILE_FOR_STDERR': No such file or directory

$ ls -l
total 16
-rwxrwxrwx. 1 root root   0 Dec  7 09:24 1
-rwxrwxrwx. 1 root root   0 Dec  7 09:24 2
-rwxrwxrwx. 1 root root   0 Dec  7 09:24 3
prw-r--r--. 1 root root   0 Dec  7 10:00 ksh_fifo.fifo
-rw-r--r--. 1 root root 101 Dec  7 10:00 ksh.log
-rwxrwxrwx. 1 root root  82 Dec  7 09:51 ksh_logging.ksh
-rwxrwxrwx. 1 root root 135 Dec  7 09:51 MOD_ksh_logging.ksh

$ cat ksh.log
Thu Dec  7 10:00:18 JST 2023
1
2
3
ls: cannot access 'NO_FILE_FOR_STDERR': No such file or directory
```
`mkfifo` によって作成される FIFO の削除処理等、更に実装を工夫することが可能です。