Shellの基本を学ぶ（４）Input and Output

# Output

# echo

* -n : 改行なし
* -e : エスケープシーケンス有効化
* -x : カラーコード指定

```bash
$ echo -n "hello"
$ echo "hello"
hello
$ echo -e "\thelloo\b"
	hello
$ COL_BLUE="\x1b[34;01m"
$ COL_RESET="\x1b[39;49;00m"
$ echo -e $COL_BLUE"Important Message: "$COL_RESET"This is a message"
> Important Message: が紫になる
```

* [echo](https://ss64.com/bash/echo.html)

# printf

フォーマット可能。デフォルトではechoと違って改行が無い

```bash
$ printf hello
hello$ printf "hello\n"
hello
$ printf "hello %s, how are you?" $USER
hello ushio, how are you?$
```
C言語系のprintfと違って、パラメータが複数あると、複数回実行される様子。

```bash
$ printf "p%st\n" a e i o u
pat
pet
pit
pot
put
```
しかし、２つの`%s`を持っていると、ちゃんと置き換えられる様子。

```bash
$ printf "Hell%s %sorld! %s\n" o W $USER
Hello World! ushio
```
とても柔軟%sが足りなくなったら自動的に繰り返すっぽい。

```bash
$ printf "*%20s *%20s *%20s *\n" $(ls)
*        Applications *                Apps *               Codes *
*             Desktop *           Documents *           Downloads *
*             Dropbox *             Library *              Movies *
*               Music *            OneDrive *            OneDrive *
*                   - *           Microsoft *            Pictures *
*              Public *             aaa.jpg *                 bin *
*                 dev *        foonotes.txt *                  go *
*    google-cloud-sdk *          istio.yaml *                 lib *
*        node_modules *   package-lock.json *                     
```
`-v`のスイッチで、変数に代入することもできる

```bash
$ printf -v hello "hello %s, nice to meet you! \n" $USER
$ echo $hello
hello ushio, nice to meet you!
```
* [The printf command](https://wiki.bash-hackers.org/commands/builtin/printf)

# Input

## read


### $REPLY
read は文字を読み込むコマンド。デフォルトでは`$REPLY`という変数に代入される。

```bash
$ read; echo $REPLY
hello
hello
```

### -r

`-r`オプションは、エスケープシーケンスを無くして、Rawで出力する。入力の際は、エスケープシーケンスがかかると不思議な挙動になるので、常にこのオプションがオススメ

```bash
$ read; echo $REPLY
\\
\
$ read -r; echo $REPLY
\ is ok for now
\ is ok for now
```

# -n, -s

`-n` は何文字の入力を待つかというフラグで、`-s`は入力した文字をエコーするか否かのスイッチ。次のプログラムを作ってみる。

```bash
#!/bin/bash

echo -n "Annie Are you OK? (Y/N)? "

answered=
while [[ ! $answered ]]; do
  read -r -n 1  -s answer 
  if [[ $answer = [Yy] ]]; then
    answered="I'm relieved"
  elif [[ $answer = [Nn] ]]; then
    answered="Annie are you OK?"
  fi
done

printf "\n%s\n" "$answered"
```

結果は予想通りで次の通り。実際にプログラムを作る時に、最初は、`printf "\n%s\n" $answered` だったのですが、それだと、スペースがあると実行が２回繰り返されるので、ダブルクオートで囲っています。

```bash
$ ./answer
Annie Are you OK? (Y/N)?
I'm relieved
$ ./answer
Annie Are you OK? (Y/N)?
Annie are you OK?
```

### parameter

パラメータをとると、それを変数にして代入してくれる。次の実験でわかるように、２つのパラメータがあると、スペースで分割された内容をそれぞれに代入してくれる。

```bash
$ read a b
hello world
$ echo $a $b
hello world
```

足りない場合、より多い場合は次の挙動

```bash
$ read a b
hello
$ echo $a
hello
$ echo $b
```

```bash
$ read a b
hello world tsuyoshi
$ echo $a $b
hello world tsuyoshi
$ echo $a
hello
$ echo $b
world tsuyoshi
```
### IFS

IFS (Internal Field Separator) はワードを分割するのに使われる。デフォルトは、<space><tab><newline>。変更する場合は、変数に代入すれば良い。

```bash
$ IFS=: read a b
hello:world
$ echo $a $b
hello world
```

ちなみに、`;`をデリミタにすると動かなかった。これは、IFSというより、シェルが改行と思うからだと思う。

```bash
$ IFS=; read a b
hello;world
$ echo $a $b
hello;world
```

# Standard Streams

Bashには３つのストリームが存在する。

* 0: Standard Input (stdin)
  /dev/stdin
* 1: Standard Output (stdout)
  /dev/stdout
* 2: Standard Error (stderr)
  /dev/stderr

エラーは、エラーメッセージや診断に使われる。

/dev/null は全てのデータを取り除く。


## Redirection

### Input redirection: <
入力を標準入力以外にします。例えばファイルとか。例えば startup.sh の全ての行から `azure` に関する行を抜き出すには下記の通りです。

```bash
$ grep azure < startup.sh 
git clone https://github.com/Azure-Samples/openhack-devops-proctor.git /home/azureuser/openhack-devops-proctor
# RUN chown azureuser:azureuser -R /home/azureuser/openhack-devops-proctor/.
cd /home/azureuser/openhack-devops-proctor
cd /home/azureuser
install -b /home/azureuser/openhack-devops-proctor/provision-team/kvstore.sh /usr/local/bin/kvstore
echo 'export KVSTORE_DIR=/home/azureuser/team_env/kvstore' >> /home/azureuser/.bashrc
cd /home/azureuser/openhack-devops-proctor/provision-team
PATH=$PATH:/opt/mssql-tools/bin KVSTORE_DIR=/home/azureuser/team_env/kvstore ./setup.sh -i $SUBID -l $LOCATION -n $TEAMNAME -u "$AZUREUSERNAME" -p "$AZUREPASSWORD" -r "$RECIPIENTEMAIL" -c "$CHATCONNECTIONSTRING" -q "$CHATMESSAGEQUEUE" -t "$TENANTID" -a "$APPID" 2>&1 | tee -a /home/azureuser/logs/teamdeploy.out 
```

### Output redirection: >

下記のコマンドで、ls.txtに `ls man`の内容が保存されます。この場合、既存のファイルがあると上書きされます。Append mode で書き出ししたい場合は `>>`を使うと、既存の内容は残したまま追記されます。

```bash
$ man ls > ls.txt
```

### Pipes

パイプは、最初のコマンドの出力を次のコマンドの入力にします。`man ls`の内容を次の`grep directory` に渡して実行します。

```bash
$ man ls | grep directory
     ls -- list directory contents
     For each operand that names a file of a type other than directory, ls
     each operand that names a file of type directory, ls displays the names
     of files contained within that directory, as well as any requested, asso-
     If no operands are given, the contents of the current directory are dis-
     played.  If more than one operand is given, non-directory operands are
     displayed first; directory and non-directory operands are sorted sepa-
     -a      Include directory entries whose names begin with a dot (.).
             directory, an asterisk (`*') after each that is executable, an at
             directory the link references rather than the link itself.  This
     name.  In addition, for each directory whose contents are displayed, the
     total number of 512-byte blocks used by the files in the directory is
     files in the directory.  If the file or directory has extended
     a '@' character.  Otherwise, if the file or directory has extended secu-
                      x     The file is executable or the directory is search-
                           1.   directory
                           10.  directory writable to others, with sticky bit
                           11.  directory writable to others, without sticky
     is a symlink that resolves to a non-directory file, the output wil
```

### Stream のリダイレクト N>

例えば次のリダイレクトは、エラー出力をを無くす。

```bash
cmd 2> /dev/null
```

### 他の Stream へのリダイレクト >&N

標準出力をエラー出力にリダイレクト

```bash
>&2 or 1>&2
```
標準エラー出力を標準出力に

```bash
2>&1
```

標準出力とエラー出力を同じファイルに送る

```
cmd > logfile 2>&1
```
この書き方はうまくいかない

```
cmd > logfile 2> logfile
```
ログファイルが上書き保存されてしまうので中途半端になる。

下記の表現は使わないこと。Depricated.

```
&> >&
```
### 順番は重要

この場合は同じ
```bash

cmd < inputfile > outputfile
> outputfile cmd < inputfile
```

最初のだと、エラーがログファイルにはかれるが、二番目は、エラーが標準出力にはかれる。

```
cmd > logfile 2>&1
2>&1 >logfile cmd
```

_output.sh_

```bash
#!/bin/bash

echo "This is standard output" > /dev/stdout
echo "This is standard error" > /dev/stderr
```

実行結果

```bash
$ ./output.sh  > logfile 2>&1
invincible:Bash ushio$ cat logfile
This is standard output
This is standard error

$ ./output.sh 2> /dev/null
This is standard output
```