bash シェルで＜HR＞（水平線）を表現する方法各種（横の罫線、区切り線）

# Horizontal Line にひかれて

HTMLでいう**`<HR>`タグのように、横線/区切り線をシェルスクリプトで表現する各種方法**です。

- 関連記事：[CLI アプリケーションを作成する場合の区切り線の PHP サンプル](https://qiita.com/KEINOS/items/574921324b705952b478)

## fix / dynamic

罫線を引くには「固定長」と「可変長」の大きく２通りの方法があります。

1. 固定長：「幅（線の長さ）を指定する」タイプ
2. 可変長：「画面幅を取得して、そのぶんを繰り返す」タイプ

## 注意点

タイプ②の[ターミナルの横幅いっぱいの水平線を引く](#tput-cols%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%82%BF%E3%83%BC%E3%83%9F%E3%83%8A%E3%83%AB%E7%94%BB%E9%9D%A2%E3%81%84%E3%81%A3%E3%81%B1%E3%81%84%E3%81%AE%E6%B0%B4%E5%B9%B3%E7%B7%9A)には `tput` コマンドが使える必要があります。

シェルが `bash` や `zsh` などの環境なら大抵は大丈夫ですが、 `sh` （ボーンシェル）などがメインの `tput` コマンドがない環境や、ターミナル情報（環境変数の $TREM の値）が空の場合、情報が取得できずエラーが出ます。

特に、[Docker](https://ja.wikipedia.org/wiki/Docker) などのコンテナのベース OS によっては `tput` コマンドも `$TERM` 変数はセットされていても `cols` コマンドが使えない場合はエラーが発生します。

```bash
tput: No value for $TERM and no -T specified
```

その場合に備えて、`if` などによる分岐が必要になります。

以下は、[sh](https://ja.wikipedia.org/wiki/Bourne_Shell), [bash](https://ja.wikipedia.org/wiki/Bash), [zsh](https://ja.wikipedia.org/wiki/Z_Shell) のシェルで使えるサンプルです。ターミナルの横幅が取得できる場合は画面いっぱいに、取得できない場合は 80 文字の罫線を引きます。

```bash:罫線を引くサンプル
# Set width
$(tput cols 2>/dev/null 1>/dev/null) && {
    SCREEN_WIDTH=$(tput cols);
}
# If not supported fallback to default as 80 chars/line
SCREEN_WIDTH=${SCREEN_WIDTH:-80};

# Function to draw horizontal line
echo_hr(){
  printf -v _hr "%*s" ${SCREEN_WIDTH} && echo ${_hr// /${1--}}
}

# Usage
echo_hr
```

タイプ②の場合、基本的にどれも `tput cols` でターミナルの横幅を取得して画面横幅いっぱいに描画するのですが、結果は同じでもその過程で表現が違うのが面白いです。

# 固定長（指定した長さ）の水平線

## もっとも単純な方法

```bash
echo --------------------
```
```bash
printf '%s\n' --------------------
```

## 繰り返し処理で表現する方法

### `for`ループで表現する

```bash
for ((x = 0; x < 20; x++)); do
  printf %s -
done
echo
```

### [`yes`コマンド](https://www.google.co.jp/search?q=%E3%82%B7%E3%82%A7%E3%83%AB+yes+%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89&oq=%E3%82%B7%E3%82%A7%E3%83%AB+yes+%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89)で表現する

```bash
yes = | head --lines=20 | tr --delete '\n' ; echo
```

### スペースをハイフンに置換して表現する

```bash
printf '%20s\n' | tr ' ' -
```

```bash
printf -v res %20s
printf '%s\n' "${res// /-}"
```

### `printf` 関数を使って表現する

```bash
printf '%.0s-' {1..20}; echo
```

# 可変長（ターミナル画面いっぱい）の水平線

- 要 `tput cols`

## 文字の置換を使った表現方法

```bash
printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -
```

## パラメーター指定の表現方法

```bash
hr="[WARNING]-[WARNING]-[WARNING]-[WARNING]-[WARNING]-[WARNING]-[WARNING]-[WARNING]-[WARNING]-[WARNING]-[WARNING]-"
printf '%s\n' "${hr:0:${COLUMNS:-$(tput cols)}}"
```

## 関数を使ってハイフンでなく直線で水平線を引く方法

```bash
hr() {
  local start=$'\e(0' end=$'\e(B' line='qqqqqqqqqqqqqqqq'
  local cols=${COLUMNS:-$(tput cols)}
  while ((${#line} < cols)); do line+="$line"; done
  printf '%s%s%s\n' "$start" "${line:0:cols}" "$end"
}

hr
```

## 関数を使って引数で渡された文字で水平線を引く方法

```bash
rule () {
  printf -v _hr "%*s" $(tput cols) && echo ${_hr// /${1--}}
}

# イコールを横幅いっぱいに引く
rule =

# 骸骨の文字絵を横幅いっぱいに引く
rule `echo -e "\\xE2\\x98\\xA0"`
```

## `printf` 関数を使った表現方法

```bash
eval printf %.0s- '{1..'"${COLUMNS:-$(tput cols)}"\}; echo
```

```bash
eval printf %.1s '-{1..'"${COLUMNS:-$(tput cols)}"\}; echo
```

```bash
_=- command eval printf %s '"${_[0]"{0..'"${COLUMNS:-$(tput cols)}"'}"}"'; echo
```

#参考文献
- The printf command @ Bash Hackers<br>http://wiki.bash-hackers.org/commands/builtin/printf
- Print ahorizontal line @ Bash Hackers<br>http://wiki.bash-hackers.org/snipplets/print_horizontal_line
- Shell trick:Printf rules @ brettterpstra.com<br>http://brettterpstra.com/2015/02/20/shell-trick-printf-rules/
- Bash/プロンプトのカスタマイズ |
 ArchWiki @ arch linux<br>https://wiki.archlinuxjp.org/index.php/Bash/%E3%83%97%E3%83%AD%E3%83%B3%E3%83%97%E3%83%88%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA
- [@nakataSyunsuke さんのコメント](#comment-b302d6e9c6c780f1707f)