シェル力が圧倒的に足りてないのでシェル力を養うために第12回シェル芸勉強会の問題に挑戦してみた。
シェル力が圧倒的に足りてないのでシェル力を養うために第12回シェル芸勉強会の問題に挑戦してみた。
問題はすごく工夫してあってパズル感覚で解けて楽しいしシェル力を養うのにも役立ちそう。
自分が使ってる zsh のバージョン。
$ zsh --version zsh 5.8 (x86_64-apple-darwin19.6.0)
はじめに
自分はシェル力が圧倒的に足りてないので勉強のためにシェル芸勉強会の問題に挑戦してみた。
一応自分がシェルを使う用途を書いておくと普段の仕事は Mac を使ってやっててちょっとしたことをやるのにちょろっとシェルを使う程度。
ちなみにシェル芸ってなに? ってところは Wikipedia の USP 友の会 から引用しておく。
シェル芸とは、主にUNIX系オペレーティングシステムにおいて「マウスも使わず、ソースコードも残さず、GUIツールを立ち上げる間もなく、あらゆる調査・計算・テキスト処理を CLI端末へのコマンド入力一撃で終わらせること」(USP友の会会長・上田隆一による定義[9])である。この技術を持つ人物を指すシェル芸人という呼び方も存在する[10]。
古典的には、UNIX系OSの各種CUIシェル上で単一または複数のコマンドを一行のコマンドラインでをつなぎ合わせて処理をおこなう、いわゆる「ワンライナー」系の手順にあたる。
第12回シェル芸勉強会の問題に挑戦してみた
少し前に第10回シェル芸勉強会の問題に挑戦してみたので今回は2つあとの 第12回シェル芸勉強会 の問題に挑戦してみた。 実は第10回シェル芸勉強会のあとは第11回シェル芸勉強会が開催しているようだけど、問題がスライドでしか公開してなくて入力データとかを転記するのが手間がかかりそうなのでやってない。
Q1
Question
次のように、画面にバッテンを描いてください。(この出力例の大きさは21x21です。)
x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
Answer
大きさが決まってるのでベタに sed で特定行を編集してみた。
$ seq 21 | sed 's/.*/x x/' | sed '2,20 s/^/ /' | sed '11 s/x.*/x/' x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x x
Q2
Question
小問1
次のechoの出力から回文を完成させてください。
ueda@remote:~$ echo たけやぶ ###このようにワンライナーで出力を作る### ueda@remote:~$ echo たけやぶ | ... たけやぶやけた
小問2
次のファイルの各行について回文を完成させてください。
ueda@remote:~/tmp$ cat kaibun たけやぶ わたしまけ
Answer
小問1
awk で元の文字列を print してから元の文字列の長さ - 1から文字列の先頭まで逆順に print してみた。
$ echo たけやぶ | sed 's/./& /g' | awk '{printf $0}{for(i=NF-1;i>0;i--){printf $i}}' | tr -d ' ' たけやぶやけた
別解。awk だけでゴリゴリとやってみた。
$ echo たけやぶ | awk '{printf $0}{for(i=length-1;i>0;i--){printf substr($0, i, 1)}}'
小問2
小問1 のやつを while で回してみた。
$ cat kaibun | while read f; do echo $f | sed 's/./& /g' | awk '{printf $0}{for(i=NF-1;i>0;i--){printf $i}}' | tr -d ' '; echo ''; done; たけやぶやけた わたしまけましたわ
Q3
個人的には第11回シェル芸勉強会の問題のなかで一番難しかったかも。 南武線の駅名のリストをネットから探すのが大変だった。
Question
ウェブ等からデータを取得して南武線の駅名のリストを作ってください。
Answer
南武線の駅名のリストは Wikipedia を使わせてもらった。 sed で HTML をゴリゴリと編集してみた。
$ cat station | \ grep '<td>' | \ grep -oE 'title="([^駅"]+駅)"' | \ sed -n '/川崎/,$p' | \ sed -n '1,/立川/p' | \ sed 's/title="//' | \ sed 's/"//' 川崎駅 京急川崎駅 尻手駅 矢向駅 鹿島田駅 平間駅 向河原駅 武蔵小杉駅 武蔵中原駅 武蔵新城駅 武蔵溝ノ口駅 溝の口駅 津田山駅 久地駅 宿河原駅 登戸駅 中野島駅 稲田堤駅 京王稲田堤駅 矢野口駅 稲城長沼駅 南多摩駅 府中本町駅 分倍河原駅 西府駅 谷保駅 矢川駅 西国立駅 立川駅
Q4
こちらもネットからデータを持ってくるやつ。
Q3 よりは情報は見つけやすかったけどデータを HTML とか PDF で公開するの本当にやめてほしい。できれば JSON せめて XML か CSV で公開してほしい。
Question
北から順(正確には都道府県番号順)に並べてください。
ueda@remote:~/tmp$ cat pref 鹿児島県 青森県 大阪府 群馬県
Answer
総務省が CSV を公開してるのを使わせてもらった。CSV ファイル一つだけで総務省が好きになった。
総務省が公開してる CSV はこんな感じのフォーマットになってる。
ken-code,sityouson-code,tiiki-code,ken-name,sityouson-name1,sityouson-name2,sityouson-name3,yomigana 1,0,1000,北海道,,,,ほっかいどう 1,100,1100,北海道,札幌市,,,さっぽろし 1,101,1101,北海道,札幌市,,中央区,ちゅうおうく
総務省が公開してる CSV は文字エンコーディングが Shift_JIS なのが惜しいけど nkf で UTF-8 に変換していろいろやってる。
$ curl -s https://www.soumu.go.jp/main_content/000608358.csv | \ nkf -Sw | \ grep -f pref | \ awk -F, '{print $4}' | \ uniq 青森県 群馬県 大阪府 鹿児島県
国土交通省が公開してるやつはこんな感じの HTML になってて機械にやさしくない作りになってる。
ちなみにタイトルは <title></title>
になってて機械だけじゃなくて人間にもやさしくない。
<tr> <td>01</td><td>北海道</td> <td>25</td><td>滋賀県</td> </tr>
sed でゴリゴリと編集してみた。
$ curl https://nlftp.mlit.go.jp/ksj/gml/codelist/PrefCd.html | \ grep -f pref | \ grep '<td>' | \ sed -E 's@</?td>@ @g' | \ awk '{print $1, $2}' | \ sort | \ awk '{print $2}'
Q5
Question
各行の数字を大きい順にソートしてください。
ueda@remote:~/tmp$ cat input A 31 1234 -42 4 B 10 31.1 -34 94
Answer
一番左の A と B がソートしないように大きな値にしておいて各列を行にして sort してみた。
$ cat input | while read f; do echo $f | awk '$1="99999999" $1{print}' | sed 's/ /\n/g' | sort -nr | tr '\n' ' ' | sed 's/99999999//'; echo ''; done A 1234 31 4 -42 B 94 31.1 10 -34
Q6
Question
次のファイルについてグラフを作ってください。
ueda@remote:~/tmp$ cat num 5 3 4 10 2
このような出力を作ります。
5 ***** 3 *** 4 **** 10 ********** 2 **
Answer
awk でベタにやってみた。
$ cat num | awk '{printf("%2d ", $1)}{for(i=0;i<$1;i++){printf("*")}print ""}'
Q7
Question
Q6のグラフを次のように縦にしてください。 (多少ズレてもよしとします。) * * * * * * * * * * * * * * * * * * * * * * * * 5 3 4 10 2
Answer
なぜか解く気力がわかなかったので解答例を記載しておく。
ueda@remote:~/tmp$ cat num | awk '{printf $1" ";for(i=0;i<$1;i++){printf "* "} for(i=$1;i<=15;i++){printf "_ "};print ""}' | awk '{for(i=1;i<=NF;i++){a[NR,i]=$i}} END{for(i=1;i<=15;i++) {for(j=1;j<=NR;j++){printf a[j,i]" "}print ""}}' | tac | sed -n '/\\*/,$p' | tr _ ' '
Q8
次のデータは、何かの試合の結果ですが、各チームが何勝何敗だったかを集計してください。引き分けは無いと仮定して構いません。
ueda@remote:~/tmp$ cat result A-B 1-2 B-A 3-1 C-A 1-0 B-C 5-4 C-B 2-1
Question
Answer
awk でベタベタにやってみた。
$ cat result | sed 's/-/ /g' | awk '{if($3>$4){w[$1]++; l[$2]++}else{w[$2]++; l[$1]++}}END{for(k in w){t[k]=k}; for(k in l){t[k]=k}; for(k in t){printf("%s %d win, %d lose \n", k, w[k], l[k])}}' A 0 win, 3 lose B 3 win, 1 lose C 2 win, 1 lose
参考サイト
pixela4go が v1.4.0 にバージョンアップしました。
pixela4go が v1.4.0 にバージョンアップしました。
v1.4.0 は context に対応するバージョンアップです。
v1.4.0 アップデート内容
context に対応
context に対応しています。
context に対応しているメソッドは WithContext
サフィックスがメソッド名に付いてます。たとえば既存のメソッド名が Create()
なら context に対応しているメソッドは CreateWithContext()
で、既存のメソッドが Update()
なら UpdateWithContext()
です。
context に対応しているメソッドはこんな感じで使います。
ctx, cancel := context.WithTimeout(context.Background(), 1 * time.Second) defer cancel() result, err := client.User().CreateWithContext(ctx, uci)
既存のメソッドは引き続き使えます。 既存のメソッドは context に対応しているメソッドのラッパーになっています。
つまりこのコードは
result, err := client.User().Create(uci)
このコードと同じように動きます。
result, err := client.User().CreateWithContext(context.Background(), uci)
現場からは以上です。
sort コマンドでバージョン番号をソートするメモ。
sort コマンドでバージョン番号をソートするやり方のメモ。
このメモは Ubuntu 20.04.2 LTS の sort コマンドと macOS の sort コマンドを使ってる。
Ubuntu と sort コマンドのバージョン。
$ cat /etc/os-release | grep -w VERSION VERSION="20.04.2 LTS (Focal Fossa)" $ sort --version sort (GNU coreutils) 8.30 Copyright (C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Written by Mike Haertel and Paul Eggert.
macOS と sort コマンドのバージョン。
$ sw_vers | grep Product ProductName: macOS ProductVersion: 11.2.3 $ sort --version 2.3-Apple (106)
sort コマンドでバージョン番号をソートする
バージョン番号がバラバラに入ってるデータをバージョン番号でソートすることがときどきある。
こんな感じのバージョン番号がファイルに入ってたら 1.0.0
, 1.9.0
, 1.10.0
の順にソートしたいけど、単純に sort コマンドでソートすると
1.1.0
, 1.10.0
, 1.9.0
みたいに 1.10.0
が 1.9.0
よりも前にきてしまう。
$ cat versions.txt 1.0.0 1.10.0 1.9.0 $ cat versions.txt | sort 1.0.0 1.10.0 1.9.0
解決策: -V, --version-sort オプションを指定する
sort コマンドは -V
オプションか --version-sort
オプションを指定すると入力データを自然なバージョン番号の順にソートする。らく。
$ cat versions.txt | sort --version-sort 1.0.0 1.9.0 1.10.0
sort コマンドはバージョン番号が rc とか alpha を含むときには無力
sort コマンドの -V
オプションと --version-sort
オプションはバージョン番号が rc とか alpha みたいなプレリリースのバージョンを含んでいるときはちゃんとソートできない。
バージョン番号が rc とか alpha みたいなプレリリースのバージョンを含むときは こっち
$ cat versions.txt 1.0.0 1.0.0-beta.1 1.0.0-rc.1 1.0.0-alpha.1 $ cat versions.txt | sort --version-sort 1.0.0 1.0.0-alpha.1 1.0.0-beta.1 1.0.0-rc.1 # こんな感じにソートしてほしい # 1.0.0-alpha.1 # 1.0.0-beta.1 # 1.0.0-rc.1 # 1.0.0
MySQL 5.7 を EC2 にインストールする手順のメモ。
何番煎じかとは思うけど MySQL 5.7 を EC2 にインストールする手順をメモしておく。
EC2 は Amazon Linux 2 AMI を使ってる。
$ cat /etc/system-release Amazon Linux release 2 (Karoo)
MySQL 5.7 を EC2 にインストールする手順
MySQL 5.7 を EC2 にインストールすることがちょいちょいあるけど毎回微妙に手順を忘れてたり調べたりして面倒くさいので手順をメモしておく。
以後の手順は EC2 にログインしてやっていく。
前準備
とりあえず yum update
コマンドを実行してあと mariadb は削除しておく。
$ sudo yum update -y $ sudo yum remove -y mariadb-libs
MySQL のリポジトリを yum に追加
yum install
コマンドで MySQL のリポジトリを yum に追加していく。
MySQL のリポジトリの URL は https://dev.mysql.com/get/<rpm ファイル名>
になるのだけどこの URL の調べ方を書いておく。
rpm のファイル名は https://dev.mysql.com/downloads/repo/yum/ の Red Hat Enterprise Linux 7 / Oracle Linux 7 (Architecture Independent), RPM Package
のところつまり RHEL7 のダウンロードボタンを押すとログインするかサインアップするかのページが表示するので、ページの下のほうの No thanks, just start my download.
のリンク先が MySQL のリポジトリの URL になる。
$ sudo yum install -y https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm $ yum list installed | grep mysql mysql80-community-release.noarch el7-3 installed
MySQL 5.7 を有効にする
MySQL のリポジトリを yum に追加するとデフォルトは MySQL 8.0 が有効になっているので sudo yum-config-manager
コマンドで MySQL 8.0 は無効にして MySQL 5.7 を有効にしていく。
$ yum repolist all | grep -E "mysql[0-9]+-community/x86" mysql55-community/x86_64 MySQL 5.5 Community Server 無効 mysql56-community/x86_64 MySQL 5.6 Community Server 無効 mysql57-community/x86_64 MySQL 5.7 Community Server 無効 mysql80-community/x86_64 MySQL 8.0 Community Server 有効: 229 $ sudo yum-config-manager --disable mysql80-community $ sudo yum-config-manager --enable mysql57-community $ yum repolist all | grep -E "mysql[0-9]+-community/x86" mysql55-community/x86_64 MySQL 5.5 Community Server 無効 mysql56-community/x86_64 MySQL 5.6 Community Server 無効 mysql57-community/x86_64 MySQL 5.7 Community Server 有効: 484 mysql80-community/x86_64 MySQL 8.0 Community Server 無効
MySQL 5.7 のインストールと起動
yum install
コマンドで MySQL をインストールして systemctl start mysqld
コマンドで mysqld を起動する。
あと systemctl enable mysqld
コマンドで mysqld がシステム起動時に起動するようにしておく。
$ sudo yum install -y mysql-community-server $ sudo systemctl start mysqld $ systemctl status mysqld.service ● mysqld.service - MySQL Server Loaded: loaded (/usr/lib/systemd/system/mysqld.service; enabled; vendor preset: disabled) Active: active (running) since 土 2021-04-03 05:52:47 UTC; 13s ago Docs: man:mysqld(8) http://dev.mysql.com/doc/refman/en/using-systemd.html Process: 3895 ExecStart=/usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid $MYSQLD_OPTS (code=exited, status=0/SUCCESS) Process: 3846 ExecStartPre=/usr/bin/mysqld_pre_systemd (code=exited, status=0/SUCCESS) Main PID: 3900 (mysqld) CGroup: /system.slice/mysqld.service └─3900 /usr/sbin/mysqld --daemonize --pid-file=/var/run/mysqld/mysqld.pid $ sudo systemctl enable mysqld
MySQL のバージョンを確認しておく。
mysql --version
コマンドを実行すると 5.7.33
が表示しててちゃんと MySQL 5.7 が動いているよう。
$ mysql --version mysql Ver 14.14 Distrib 5.7.33, for Linux (x86_64) using EditLine wrapper
root ユーザーのデフォルトのパスワードの調べ方
MySQL の root ユーザーのデフォルトのパスワードは MySQL のログに記載しているのでログを見たら root ユーザーのパスワードが分かる。
$ sudo grep 'temporary password' /var/log/mysqld.log 2021-04-03T05:52:45.375037Z 1 [Note] A temporary password is generated for root@localhost: LTaGlt2Q?>u3
あとは mysql コマンドで MySQL に接続して適当にパスワードを変えておく (間違ってもこの例みたいな適当なパスワードにしてはいけない)
$ mysql -uroot -p'LTaGlt2Q?>u3' mysql> set password for root@localhost='abCD1234@'; Query OK, 0 rows affected (0.00 sec)
この記事で書いたやつを全部やるスクリプト
この記事で書いたやつを全部やるスクリプトを Gist に置いといた => https://gist.github.com/ebc-2in2crc/d5c0976b4f039764e780049822acc329
こんな感じで sh に流し込んだら全部やってくれる。
$ curl https://gist.githubusercontent.com/ebc-2in2crc/d5c0976b4f039764e780049822acc329/raw/4b44907d376e95843bdd635021761be73f56c5b3/install-mysql5.7-on-ec2.sh | sh
以上。
参考サイト
Docker のコンテナ ID だけを表示する --quiet オプションを知ったメモ。
Docker のコンテナ ID だけを表示する --quiet オプションを知ったメモ。
Docker のコンテナ ID だけを表示する
Docker のコンテナ ID だけを表示する --quiet オプションを知ったのでメモしておく。
Docker のコンテナはコンテナ ID を指定して stop したり restart することが多いけどコンテナ ID を調べようとして docker container ls
コマンドを実行するとコンテナ ID 以外にもいろいろ情報が表示する。
対話的にやってるときはそれでいいけどシェルスクリプトとかはコンテナ ID 以外は必要としないのでちょろっと工夫してコンテナ ID だけ取得しているのだけど docker container ls
コマンドのヘルプを見てたらそのへんの工夫は全然必要ないことが分かったのでメモしておく。
最初に ls するコンテナを起動しておく。
$ docker container run --name my-httpd -dit -p 8080:80 httpd f5e4bb31377d45d6478444480f6b6503e692536151a80bbc3cac606b7f63f2e7
普通に docker container ls
コマンドを実行するとコンテナ ID 以外にもいろいろ情報が表示する。
$ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f5e4bb31377d httpd "httpd-foreground" 3 seconds ago Up 3 seconds 0.0.0.0:8080->80/tcp my-httpd
自分はだいたいはコンテナ ID だけ取得するのでコンテナ名で grep して awk で1つめのフィールドだけ表示する、というかしてた。
$ docker container ls | grep my-httpd | awk '{print $1}' f5e4bb31377d
docker container ls
コマンドの --filter
オプションを指定すると grep する必要がなくなって --quiet
オプションを指定すると awk する必要がなくなり、タイプ数も少し少なくなる。
$ docker container ls --filter "name=my-httpd" --quiet f5e4bb31377d
docker container ls
コマンドの --filter
オプションと --quiet
オプションはショートオプションが用意されているのでそれぞれショートオプションの -f
と -q
を指定するとさらにタイプ数が少なくなる。らく。
$ docker container ls -q -f "name=my-httpd" f5e4bb31377d
以上。
入力行を指定幅で折り返す fold コマンドの使い方のメモ。
入力行を指定幅で折り返す fold コマンドの使い方のメモ。
このメモは Ubuntu 20.04.2 LTS の Docker コンテナ上の fold コマンドを使ってる。
Ubuntu, bash, fold コマンドのバージョン。
$ cat /etc/os-release | grep -w VERSION VERSION="20.04.2 LTS (Focal Fossa)" $ bash --version GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu) Copyright (C) 2019 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. $ fold --version fold (GNU coreutils) 8.30 Copyright (C) 2018 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>. This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
あと macOS でデフォルトでインストールされている fold コマンドを使う箇所もある。
$ sw_vers | grep Product ProductName: macOS ProductVersion: 11.2.3 $ bash --version GNU bash, version 5.1.4(1)-release (x86_64-apple-darwin19.6.0) Copyright (C) 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.
fold コマンドの使い方
fold コマンドは入力行を指定幅で折り返すコマンド。
自分は普段そんなに使わないんだけど圧倒的に不足しているシェル力を養うために シェル芸勉強会 の問題集を解くときに使ったりするので使い方をメモしておく。
--help オプション: ヘルプ
--help
オプションを指定すると fold コマンドの使い方を表示する……んだけど man fold
が使えないときにしか使わない。
$ fold --help Usage: fold [OPTION]... [FILE]... Wrap input lines in each FILE, writing to standard output. With no FILE, or when FILE is -, read standard input. Mandatory arguments to long options are mandatory for short options too. -b, --bytes count bytes rather than columns -s, --spaces break at spaces -w, --width=WIDTH use WIDTH columns instead of 80 --help display this help and exit --version output version information and exit GNU coreutils online help: <https://www.gnu.org/software/coreutils/> Report fold translation bugs to <https://translationproject.org/team/> Full documentation at: <https://www.gnu.org/software/coreutils/fold> or available locally via: info '(coreutils) fold invocation'
基本的な使い方
基本的には fold <ファイル>
みたいにして使う。
$ cat message.txt 0123456789 $ fold -w 2 message.txt 01 23 45 67 89
fold -
みたいに <ファイル>
のところを -
にするとファイルから読み込むのではなくて標準入力から読み込む。
$ echo '0123456789' | fold -w 2 - 01 23 45 67 89
<ファイル>
のところは何も指定しなかったら標準入力を読み込む。
先行するコマンドの出力をパイプで fold コマンドに接続するなら fold -
みたいにするよりも fold
みたいにしちゃうことが多い気がする。
$ echo '0123456789' | fold -w 2 01 23 45 67 89
-w, --width オプション: 指定幅で折り返す
fold コマンドはデフォルトは 80 の幅で入力を折り返すけど -w
オプションか --width
オプションを指定すると入力を指定幅で折り返す。
ちなみに macOS にインストールされている BSD 版の fold コマンドはショートオプションの -w
オプションはサポートしてるけどロングオプションの --width
オプションはサポートしていない。
入力幅が10でデフォルト幅の80に収まるので折り返さない。
$ echo '0123456789' | fold 0123456789
入力幅が10で -w 10
の幅の10に収まるので折り返さない。
$ echo '0123456789' | fold -w 10 0123456789
入力幅が10で -w 9
の幅の9に収まらないので収まらない分の 9
が折り返す。
$ echo '0123456789' | fold -w 9 012345678 9
入力幅が10で -w 3
の幅の3に収まらないので収まらない分の各行がそれぞれ3の幅で折り返す。
$ echo '0123456789' | fold -w 3 012 345 678 9
-s, --spaces オプション
fold コマンドはデフォルトは単に幅で入力を折り返すけど -s
オプションか ---spaces
オプションを指定すると空白を考慮して折り返す。
英語の文章とかを読むときに便利かもしれない。
ちなみに macOS にインストールされている BSD 版の fold コマンドはショートオプションの -s
オプションはサポートしてるけどロングオプションの --spaces
オプションはサポートしていない。
入力幅が5で -w 5
の幅の5に収まらないので折り返すけど、 345
の途中と 678
の途中で折り返しちゃう。
$ echo '012 345 678 9' | fold -w 5 012 3 45 67 8 9
入力幅が5で -w 5
の幅の5に収まらないので折り返すけど、 -s
を指定してるので 345
の途中では折り返さないで 345
の次の空白で折り返す。678
も同じようになる。
$ echo '012 345 678 9' | fold -w 5 -s 012 345 678 9
指定した幅よりも長い単語があるときは単語の途中でも折り返す。
$ echo '012' | fold -w 2 -s 01 2
-b, --bytes オプション: 幅じゃなくてバイト数で折り返す
fold コマンドはデフォルトは幅で入力を折り返すけど -b
オプションか ---bytes
オプションを指定するとバイト数で折り返す。
ちなみに macOS にインストールされている BSD 版の fold コマンドはショートオプションの -b
オプションはサポートしてるけどロングオプションの --bytes
オプションはサポートしていない。
タブ文字を含む文字列を fold コマンドで折り返すとこんな感じになる。
$ echo $'01234\t56789' | fold -w 4 0123 4 5678 9
-b
オプションを指定すると 0123
の4バイトで折り返して $'4\t56' の4バイトで折り返す。
$ echo $'01234\t56789' | fold -w 4 -b 0123 4 56 789
タブ文字はいろいろ扱いにくさがあると思うのでタブそのものに意味がないなら適当にスペースに変換しちゃってもいいと思う。
expand コマンドでタブ文字をスペース1つに展開しちゃうとか。
$ echo $'01234\t56789' | expand -t 1 | fold -w 4 0123 4 56 789
tr コマンドでタブ文字をスペース1つに置換しちゃうとか。
$ echo $'01234\t56789' | tr $'\t' ' ' | fold -w 4 0123 4 56 789
以上。
シェル力が圧倒的に足りてないのでシェル力を養うために第9回シェル芸勉強会の問題に挑戦してみた。
シェル力が圧倒的に足りてないのでシェル力を養うために第9回シェル芸勉強会の問題に挑戦してみた。
問題はすごく工夫してあってパズル感覚で解けて楽しいしシェル力を養うのにも役立ちそう。
自分が使ってる zsh のバージョン。
$ zsh --version zsh 5.8 (x86_64-apple-darwin19.6.0)
はじめに
自分はシェル力が圧倒的に足りてないので勉強のためにシェル芸勉強会の問題に挑戦してみた。
一応自分がシェルを使う用途を書いておくと普段の仕事は Mac を使ってやっててちょっとしたことをやるのにちょろっとシェルを使う程度。
ちなみにシェル芸ってなに? ってところは Wikipedia の USP 友の会 から引用しておく。
シェル芸とは、主にUNIX系オペレーティングシステムにおいて「マウスも使わず、ソースコードも残さず、GUIツールを立ち上げる間もなく、あらゆる調査・計算・テキスト処理を CLI端末へのコマンド入力一撃で終わらせること」(USP友の会会長・上田隆一による定義[9])である。この技術を持つ人物を指すシェル芸人という呼び方も存在する[10]。
古典的には、UNIX系OSの各種CUIシェル上で単一または複数のコマンドを一行のコマンドラインでをつなぎ合わせて処理をおこなう、いわゆる「ワンライナー」系の手順にあたる。
第9回シェル芸勉強会の問題に挑戦してみた
少し前に第10回シェル芸勉強会の問題に挑戦してみたので今回は1つ前の第9回シェル芸勉強会の問題に挑戦してみた。
Q1
Question
まず、次のようにファイルを作ってください。
$ touch apple avocado banana cinnamon melon $ ls apple avocado banana cinnamon melon
「a,b,c,m」というディレクトリを作って、1文字目が対応するファイルをそれぞれのディレクトリに移動してください。
###こうなったらOK### $ ls * a: apple avocado b: banana c: cinnamon m: melon
Answer
cut -c1
で単語の1文字目を強引に抜き出してるけどあとで解答例を見たら ${f:0:1}
みたいに Bash のパラメーター展開を使っててスマートな感じだった。
$ ls | while read f; do d=$(echo $f | cut -c1); mkdir -p $d; mv $f $d; done $ ls * a: apple avocado b: banana c: cinnamon m: melon
Q2
Question
まず、次のように名前にスペースが入ったファイルを作ります。
$ touch "私は 蟹" "オシャレな 蟹" "足が 10本" $ ls -l total 0 -rw-r--r-- 1 ueda staff 0 2 14 11:22 私は 蟹 -rw-r--r-- 1 ueda staff 0 2 14 11:22 足が 10本 -rw-r--r-- 1 ueda staff 0 2 14 11:22 オシャレな 蟹
このままでは何かと扱いづらいので、間にアンダーバーを入れて次のように名前を変更してください。
$ ls -l total 0 -rw-r--r-- 1 ueda staff 0 2 14 11:25 私は_蟹 -rw-r--r-- 1 ueda staff 0 2 14 11:25 足が_10本 -rw-r--r-- 1 ueda staff 0 2 14 11:25 オシャレな_蟹
Answer
$ ls | while read f; do mv "$f" "$(echo $f | tr ' ' '_')"; done
Q3
Question
ディレクトリを適当に作って、20140101から20141231まで、日付に対応したファイルを作って下さい。各ファイルの中には各日付に対応するdateコマンドの出力を書き込んで下さい。
(ワンライナーが思いつかない場合は、とりあえず手作業でやってみてください。)
###こんな感じでどうぞ### uedambp:20140214USPSTUDY ueda$ ls -l | head total 1460 -rw-r--r-- 1 ueda staff 28 2 14 10:23 20140101 -rw-r--r-- 1 ueda staff 28 2 14 10:23 20140102 -rw-r--r-- 1 ueda staff 28 2 14 10:23 20140103 -rw-r--r-- 1 ueda staff 28 2 14 10:23 20140104 -rw-r--r-- 1 ueda staff 28 2 14 10:23 20140105 -rw-r--r-- 1 ueda staff 28 2 14 10:23 20140106 -rw-r--r-- 1 ueda staff 28 2 14 10:23 20140107 -rw-r--r-- 1 ueda staff 28 2 14 10:23 20140108 -rw-r--r-- 1 ueda staff 28 2 14 10:23 20140109 uedambp:20140214USPSTUDY ueda$ cat 20140101 水 1 1 00:00:00 JST 2014
Answer
Mac の date コマンドはつらみがあるので Virtual Box 上の Ubuntu 20.04.2 LTS で解いた。
これ date コマンドで日付を相対指定するのがスマートだと思うんだけど 20140101 から 20141231 まで数値のまま繰り返し処理して強引に日付を作ってみた。
$ seq 0 $(echo '20141231-20140101' | bc) | awk '{print 20140101+$1}' | while read d; do date -d $d && date -d $d > $d; done
Q4
Question
次のように4個ファイルを作って、a_ramenとa_curry、b_appleとb_tomatoのファイルの中身を入れ替えてください。
$ echo カレー > a_ramen $ echo ラーメン > a_curry $ echo トマト > b_apple $ echo リンゴ > b_tomato ###(余談)各ファイルと中身は次のようにgrepで確認できる### $ grep "" * a_curry:ラーメン a_ramen:カレー b_apple:トマト b_tomato:リンゴ
Answer
少しちょろっと考えたけど分からなかったので解答例を見た。 解答例を分解しながら少しずつ書いてみる。
grep "" *
でそれぞれのファイルとファイル内容の組み合わせを作って、
$ grep "" * a_curry:ラーメン a_ramen:カレー b_apple:トマト b_tomato:リンゴ
tr ':' ' '
で :
を削除して、
$ grep "" * | tr ':' ' ' a_curry ラーメン a_ramen カレー b_apple トマト b_tomato リンゴ
xargs -n 4
で a_*
のファイル同士の組み合わせを作って、
$ grep "" * | tr ':' ' ' | xargs -n 4 a_curry ラーメン a_ramen カレー b_apple トマト b_tomato リンゴ
awk '{print $1,$4,$3,$2}'
でファイルとファイル内容の組み合わせを入れ替えて、
$ grep "" * | tr ':' ' ' | xargs -n 4 | awk '{print $1,$4,$3,$2}' a_curry カレー a_ramen ラーメン b_apple リンゴ b_tomato トマト
awk '{print "echo",$2,">",$1}'
でコマンド文字列を作って、
$ grep "" * | tr ':' ' ' | xargs -n 4 | awk '{print $1,$4,$3,$2}' | xargs -n 2 | awk '{print "echo",$2,">",$1}' echo カレー > a_curry echo ラーメン > a_ramen echo リンゴ > b_apple echo トマト > b_tomato
コマンド文字列を sh
に流し込んで実行する。
$ grep "" * | tr ':' ' ' | xargs -n 4 | awk '{print $1,$4,$3,$2}' | xargs -n 2 | awk '{print "echo",$2,">",$1}' | sh $ grep "" * a_curry:カレー a_ramen:ラーメン b_apple:リンゴ b_tomato:トマト
Q5
Question
各月ごとにtar.gzファイルにしてください。
###こんな感じで### uedambp:20140214USPSTUDY ueda$ ls *.tar.gz 201401.tar.gz 201404.tar.gz 201407.tar.gz 201410.tar.gz 201402.tar.gz 201405.tar.gz 201408.tar.gz 201411.tar.gz 201403.tar.gz 201406.tar.gz 201409.tar.gz 201412.tar.gz
Answer
これ質問は元ネタのファイルはどこにあるのかまったく書いてないけど問題の流れから Q3 で作った日付のファイルをやってみた。
$ ls | cut -c1-6 | uniq | xargs -I{} sh -c "tar cvzf {}.tar.gz {}*"
Q6
Question
次のようなディレクトリ・ファイル操作を行って下さい。
###小問1: ディレクトリを作る### ~/a/a/a/.../a/a/ (aが百個) ###小問2: ファイルを作る### ~/a/a/a/.../a/a/b (aが百個、bはファイル) ###小問3: ~/a/a/a/.../a/a/の底に移動### uedambp:a ueda$ pwd /Users/ueda/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a /a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a /a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a /a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a
Answer
小問1
$ seq 100 | sed 's/.*/a/' | tr '\n' '/' | xargs mkdir -p
小問2
$ seq 100 | sed 's/.*/a/' | tr '\n' '/' | xargs -I{} touch {}b
小問3
$ find . -name a | tail -n 1 | while read f; do cd $f; done
Q7
Question
先ほど作ったファイルbを、50番目のaディレクトリに移動して下さい。
↓うまくできたかどうかの確認方法
###~から50番目のaに移動### uedambp:~ ueda$ for a in {1..50} ; do cd a ; done ###bがあるか確認### uedambp:a ueda$ pwd /Users/ueda/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a /a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a /a/a/a/a uedambp:a ueda$ ls a b ###さらに50個aを下る### uedambp:a ueda$ for a in {1..50} ; do cd a ; done uedambp:a ueda$ pwd /Users/ueda/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a /a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a /a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a /a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a /a/a ###なにもない### uedambp:a ueda$ ls uedambp:a ueda$
Answer
少しちょろっと考えたけど分からなかったので解答例を見た。
こういう問題を考える人って頭がいいなーって思う。
$ seq 150 | sed 's/100/&\nb ./' | sed -E 's/^[0-9][0-9]*/a/' | tr '\n' '/' | sed 's/^/mv /' | sh $ for a in {1..50} ; do cd a ; done $ pwd /path/to/shell-gei-study/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a $ ls a/ b $ for a in {1..50} ; do cd a ; done $ pwd /path/to/shell-gei-study/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a/a $ ls
Q8
Question
先ほど作ったディレクトリについて、rm -rを使わずに~/a以下のディレクトリを消去してください。
Answer
問題自体はそんなに難しくないけどディレクトリをガーッと消す操作をやるのはちょっと怖かった。
$ seq 100 | tail -r | awk '{for(i=0;i<$1;i++){printf("a/")}; print ""}' | sed 's/.*/rm -f &b; rmdir &/' | sh