AtCoder Beginner Contest A問題で学ぶシェル芸・sed芸

# .
AtCoder Beginner Contest の A 問題がシェル芸の練習にちょうどよいことに気づいたのでいくつか問題を紹介します。
今回は awk は使用禁止とします。
フォーマットは @kotatsugame さんの https://qiita.com/kotatsugame/items/184bfd63d9b21f214475 を参考にしています。  
**zshでのみ動くコードが多いです　bashでも動くように修正中です**

# ABC077A - Rotation

> $C_{11}C_{12}C_{13}$
> $C_{21}C_{22}C_{23}$

この長方形を180度回転させて元の長方形に一致するかを判定します。  
**bashでは動きません**
```bash
read s;echo $s|rev|sed -e "/`cat`/cYES" -e "cNO"
```
`read s`で一行目を読み込み、一行目を反転した結果(`rev`)が残りの標準入力と一致するなら`YES`、そうでないなら`NO`を出力します(`sed`)。  
`sed`の部分は<code>\`cat\`</code>の結果に入力がマッチした場合には行を`YES`に置換、そうでない場合は`NO`に置換します。
`cat` は `dd` でも代用できますが可読性が下がりすぎるのでこの記事では `cat` を使うことにします。

# ABC119A - Still TBD

> $S$

`yyyy/mm/dd`で入力される文字が`2019/04/30`より前か判定します。

```bash
(($(date -d `cat` '+%s')>`date -d 2019/4/30 '+%s'`))&&echo TBD||echo Heisei
```
今回の制約に$S$が2019年の日付とあるのでコードをより短くすることは可能ですが実用性が高い方法で実装しました。
`$(hogehoge)`と<code>\`hogehoge\`</code>は同じ意味で、`hogehoge`の実行結果を展開する記法ですが、ネストする場合は外側は`$(hogehoge)`にする必要があります。(<code>\`\`hogehoge\`\`</code>のようにすると前２つと後ろ２つで区切られてしまうため)
`date`コマンドの`-d`は現在時刻の代わりに引数の文字列を変換して出力するオプションです。`'+%s'`は日時ををエポック秒に直します。
2019/4/30と入力のエポック秒を比較して入力のほうが大きいなら`TBD`、そうでないなら`Heisei`と出力します。短絡評価というやつです。

# ABC197A - Rotate

> $S$

長さ3文字の$S$の先頭の文字を$S$の末尾に移動して出力します
```bash
sed -E 's/(.)(.*)/\2\1/'
```
sed入門です。
`s`コマンドで一文字とその後の文字にマッチして、一文字目以外の文字を最初に、最後に一文字目の文字を出力しています。
`(hoge)`は`-E`で使える拡張正規表現の機能で、hogeにマッチした文字を`\1`,`\2`...に保存します。
なお、sedは最長一致なので`^`,`$`などを指定する必要はありません。

# ABC248A - Lacked Number
> $S$

`0`から`9`までのうち9種類の文字が含まれる文字列$S$に唯一存在しない`0`から`9`までのうちの文字を出力します。
**bashでは動きません**
```bash
seq 0 9|tr -d `cat`
```
`seq 0 9`で改行区切りで`0`から`9`までの文字を出力し、それのうち入力に含まれる文字を`tr`の`-d`オプションで削除します。
なお AtCoder は基本余計な改行空白があっても正解と判定されるのでこのコードでも正解できますがそうでない場合は`\n`も`tr`コマンドの削除する文字に含める必要があります。

# ABC271A - 484558
> $N$

2 桁の 16 進表記に10進数で与えられる$N$を変換します。
```bash
printf '%02X' `cat`
```
printfコマンドの便利な機能です。
`%02X`の`02`は2桁に自動でパディングすること、`X`は英大文字の16進数で数字を表示することを示しています(`x`だと小文字で表示される)。
似たような記法が他の言語でも使えることが多いので覚えておくと便利です。

# ABC279A - wwwvvvvvv
> $S$

`w`と`v`からなる文字列$S$に、いくつ下に尖っている箇所があるかをカウントします。
```bash
sed 's/w/vv/g'|wc -L
```
`sed`で`w`を適当な二文字に変えて`wc -L`で文字数をカウントします。
`-L`オプションは文字列の中で最も長い行のバイト数を出力するものです。
なお、`-c`でカウントしようとすると改行がカウントされてしまい最初に改行を消すかあとからカウントから 1 を引く必要があるので注意が必要です。

# ABC281A - Count Down
> $N$

$N$ 以下の非負整数を降順にすべて出力します。
```zsh:zshで動くコード
echo {`cat`..0}
```
シェルの展開の機能を使います。
`{n..m}`は`n`から`m`までの数字をスペース区切りに並べたものへ展開されます。
**追記: このコードはシェルによっては動きません。zsh, ksh では動きましたが sh, bash では動きませんでした。@ko1nksm さんに指摘をいただきました。**
<details><summary> 原因の説明　長いです</summary>

原因は bash ではシェルの展開の順序が
> The order of expansions is: brace expansion; tilde expansion, parameter and variable expansion, arithmetic expansion, and command substitution (done  in  a  left-to-right  fashion); word splitting; and pathname expansion.

と、brace expansion の方が command substitution よりも優先度が高いことにより、<code>{\`cat\`..0}</code>が先に無効な brace expansion としてパースされ、展開されずにあとから `cat` の実行結果に置き換わることです。

なお zsh で動作するのは、展開の順序が
> The following types of expansions are performed in the indicated order in five steps:
> 1. History Expansion
    (説明略)
> 1. Alias Expansion
    (説明略)
> 1. Process Substitution
>    Parameter Expansion
>    Command Substitution
>    Arithmetic Expansion
>    Brace Expansion
    (説明略、5つは優先順位が同じで左から右の順に実行される/同一単語の場合は上から順に実行される)
> 1. Filename Expansion
    (説明略)
> 1. Filename Generation
(説明略)

となっており、Command Substitution と Brace Expansion の優先順位が同じであること、また、ネストされている場合は内側が先に展開されること（この部分の似たような説明はいくつかあったのですが Command Substitution に関する記述が見つかりませんでした　知っている方がいれば教えてください）により、先に`cat`が実行され正しく`n n-1 n-2 ... 0` と展開されるからです。

説明終わり
</details>

bash でこの方法を使うには、
```bash: bashで動くコード
eval "echo {`cat`..5}"
```
のように二段階で展開させる必要があります。(シェル側で`cat`を展開し、evalで`{n..m}`を展開する)


なお、`seq`コマンドを使った
```bash: 両方で動くコード
seq `cat` -1 0
```
でも正解できます。こちらは環境に関係なく動作します。（スペース区切りが改行区切りになります）

# ABC282A - Generalized ABC
> $K$

`A`から順番に英大文字を$K$個連続で出力します。
**bashでは動きません**
```bash
echo {A..Z}|tr -d ' '|head -c `cat`
```
展開part2です。
実は`{x..y}`はアルファベットも展開することが可能です。
AからZまでの文字を展開して`tr`でスペースを除いて`head`で最初の$K$文字を出力しています。
`tr`コマンドの`-c`オプションは文字列のバイト数を出力するオプションです。

# ABC284A - Sequence of Strings
> $N$
> $S_1$
> $S_2$
> $\vdots$
> $S_N$

文字列の配列$S$を逆順に出力します。
```bash
sed 1d|tac
```
`sed`で一行目の邪魔な$N$を削除、`tac` で逆順に$S$を出力しています。
`sed`の`1d`は、**1**行目を**d**eleteした文字列を出力し直すものです。
`tac`は最後の行から逆に最初の行までを出力するコマンドです。
似たコマンドとして`rev`があり、こちらは文字列全体を逆から出力します。(例:`hello`→`olleh`)

# ABC299A - Treasure Chest
> $N$
> $S$

2つの`|`と1つの`*`、残りは`.`で構成される文字列$S$で`*`が2つの`|`の間にあるなら`in`ないなら`out`を出力します。
```bash
tr -cd '|*'|sed -e '/|\*|/cin' -e 'cout'
```
`tr`で`|`、`*`以外の文字を削除してから`sed`で文字列に`|*|`が含まれるなら`in`、そうでないなら`out`を出力しています。
`tr`の`-c`オプションは指定された文字**以外**を置換するオプションです。(この場合は`-d`もついているので削除されます)
`tr -cd '|*'`は`tr -d '0-9.\n'`でもこの場合は代替が可能です。

# ABC292A - CAPS LOCK
> $S$

英小文字からなる$S$をすべて大文字に変換します。
```bash
tr a-z A-Z
```
`tr`コマンドは`tr abcdefghijk...`と入力せずとも`a-z`、`A-Z`、`0-9`などで置換する文字を一気に指定することができます。
これは使う機会がかなり多いので覚えておくと便利です。

# ABC338A - Capitalized?
> $S$

$S$の一文字目が大文字、それ以降が小文字なら`Yes`、そうでないなら`No`を出力します。
```bash
sed -e '/^[A-Z][a-z]*$/cYes' -e 'cNo'
```
`^`で行頭、`[A-Z]`で行頭の次の文字が大文字であること、`[a-z]*`でその後の文字が0個以上の小文字の繰り返しであること、`$`で行末を指定しています。
この問題には関係ありませんが、Vimの`^`は空白を除いた行頭、正規表現の`^`は空白を除かない行頭であることに注意してください。

# ABC339A - TLD
> $S$

英小文字と`.`からなる文字列$S$の最後の`.`以降の文字列を出力します。
```bash
grep -o '[^.]*$'
```
`-o`はマッチした部分のみを出力するオプションです。正規表現は`[^.]`で`.`以外の文字、`*`で繰り返し、`$`で行末を示しています。
また、
```bash
tr . '\n'|tail -1
```
でも正解することができます。
これは`.`を`\n`、つまり改行に変換して最後の一行を`tail`コマンドで出力しています。