先日実施された第45回シェル芸勉強会に出席してきたので、その復習。 前回の45回はawkでゴリゴリ解いていくような問題が多かったのだけど、今回はいろんなコマンドを組み合わせて解いていくような問題が多めになっているらしい。

問題および模範解答はこちら。あと、問題を解くに当たって必要になるファイルは以下のコマンドで取得してくる。

git clone https://github.com/ryuichiueda/ShellGeiData

Q1.

csvファイル「data.csv」に、日別のトマト・バナナ・ピーマンの売れた個数が書かれているので、それぞれが記録されている最後の日の日付...


たまにlogを見ながら作業をしたりすることがあるのだけど、そういうときサーバによってはログの日付がGMTで記録されてたりする。 ここは日本であり、やはり自分もJSTで普段生活しているので、いちいち脳内で+9時間して読み取るのはちょっとだるい。できればJSTになってる時刻でログを読みたいところ・・・。

というわけで、取得したログをJSTに変換して出力する方法について考えてみることにした

1. awkで日付を変換する(外部コマンド使用)

apacheのログなんかだと、デフォルトでは日付の表記が「28/Nov/2019:09:03:19」のようになってしまっている。 これだとちょっと...


先日のシェル芸勉強会において、awkで巨大な数字を取り扱うという内容があった。 awkでは、巨大な数字を扱おうとすると以下のようなエラーが出力されることがあるのだけど、これを回避する方法について考えるという内容だった。

$ echo|awk '{print(999^300)}'
+inf

Stack Overflowとかを漁っていると、どうも53bit分までのデータしか扱えないため、それ以上の数字になるとinfとして扱われてしまっている模様。

c++ - Does big integer in AWK only have 53 bits? - Stack Overflow

...

先日行われたシェル芸勉強会の中で、別のファイルから1行ずつデータを取得させたいということがあった。 いろいろとやり方を調べたりしていたのだが、その中でくんすとさんがいい感じの回答をしていた。

cat speech | awk 'NF==0{getline< "speech2"}{print}'#シェル芸

実行例を書いてみる。 以下の例では、「seq.txt」の空行に「aiueo.txt」というファイルの中身を差し込んでいっている。


Typoしたものが大半なのだけど、指定した文字列とN文字違ったり、1文字ずれている(「あいうえお」→「いあうえお」みたいな)文字列をヒットさせたいということがごくごくたま~にあって、それをgrepでときどきやっている。 個人のマシンではfunctionを作ってあるのだけど、たま~にリモートマシンで使うとき(+ローカルのrcファイルを読み込ませてない時)なんかにはその場でコマンドを組み合わせたりしているので、備忘で残しておく。

1文字違う文字列をgrepする

指定した文字列から1文字違う文字列をgrepする場合、以下のようにする。


ターミナル上で、特定の列の値の件数を集計して、その集計した数が指定した値の行だけを出力させたい事がある。 …のだけど、調べたところ楽に一発で取得するようなコマンドが無いみたいなので、一応残しておく。

いろいろとやり方は有ると思うけど、以下のようにawkで処理を書いてやるほうが楽だろう。 以下の例では、3列目に2個同じ値がある行のみを出力している。


ちょっと前に意図的に脆弱なシステムを作る機会があって、そこでブラインドSQLインジェクションを利用して値を取得させるように作った。 で、前に 常設のCTFで似たような内容をシェル芸で解いてた のでちゃちゃっと対応できるだろうと思ってたのだけど、結構前に書いてたコードだったのでテスト時にうまくできなかったので、次に手こずらないようそこだけ抜き出して書いてみる。(サンプルに必要なURLは前述のCTFのサイトを利用)

ブラインドSQLでは、まず最初に対象の文字数を調べる必要があるので、以下のようなコマンドを実行する。


ふと、コンソール上で指定した値に近い順で数字をsortしたい場合、どうすればいいかなーと思ったので調べてみた。 指定した値との差分さえ絶対値にすればいいので、そんなに難しい話ではない。とりあえずawkを使ってみる前提(perlとかだとabs関数あるようなので)。で、awkにはabsの関数が無いのだけど、以下のようにすることで絶対値を取得してsortさせることができる。

awk '{x=-$0;print $0,(x < 0) ? -x : x}' | sort -k2n | cut -d' ' -f1
blacknon@BS-PUB-UBUNTU-01:~$ # 5に近い値順...

先日実施された、第36回シェル芸勉強会に参加してきたので、その復習。なんか、ブログの記事自体をすごく久しぶりに書いた気がする(個人的にRustで簡単なツール作ってるのだけど、難しすぎてそっちにリソース全振り中…。ある程度動くようにはなったけどいつ終わるのやら…簡単なはずだったのに…)。 大体いつもむずかしめの問題が多いのだけど、今回も難しかった。

問題及び模範解答はこちら。最初に、問題等に使用するファイルをgitからcloneしておくといい。

Q1.

welcome.txt」というファイルに隠されたメッセージを読み取れ、という内容。 単純にcatしただけだとアンダーバーし...


だいぶ前に、ターミナル上で指定列だけを除外して出力したいという事があったので、備忘で残しておく。 真っ先に思いつくのがawkでの処置なのだが、awkだと指定した列だけを削除するという、そのものズバリな機能はないので、ちょっと冗長になってしまう。


最近、ほぼ毎日何らかの形でTwitterのシェル芸botで遊んでるのだけど、そこで文字列からひし形の模様を作って遊んでたので、そのシェル芸について備忘で残しておく。 (こういう遊び、ちゃんと名前付いてるのかもしれないけど、わからないのでこの書き方で。)

1. 文字列でひし形にする

文字列を使って、以下のような模様を作る(例:焼き肉が食べたい)。 縦横ともに、真ん中の列・行はオリジナルの文が読める状態。

   焼
  焼肉が
 焼肉が食べ
焼肉が食べたい
 が食べたい
  べたい
   い

最初、jqコマンドだけで実現できるかと思ったのだけど、ちょっと計算の仕方があわず。 a...


たまに、awkでパースした要素を使ってOSのコマンドを実行させたりすることがある。 で、さらにOSのコマンドだけではなく、さらにawkの処理を組み合わせてやりたいことがある。そういう時、ただ単にOSのコマンドを実行後に処理を記述すると、実行されるタイミングの違いによって意図した結果が得られない場合がある。

blacknon@BS-PUB-UBUNTU-01:~$ # 先にrevを実行しているが、処理速度の違いから後から実行しているprintが先に出力されている
blacknon@BS-PUB-UBUNTU-01:~$ echo TEST123 | awk '{print $0|"rev";print $0}'
TEST123
321TSET

先日実施された、第35回シェル芸勉強会に参加してきたのでその復習(第34回は腰をやって参加してないので、1個空いちゃったなー…)。 今回は、前半はそこまで厳しく無かったのだが、後半が結構難しかったので中々疲れた。

問題及び模範解答はこちら。最初に、問題等に使用するファイルをgitからcloneしておくといい。

git clone https://github.com/ryuichiueda/ShellGeiData.git

Q1.

ちょっと前に話題になっていた、curlでアクセスするとアスキーアートのParty Parrotがターミナル上で動き出すサービスをの出力をファイル...


ふと、行単位で重複した要素のない行のみを抽出してみようということがあったのでやってみた。 どんな方法があるかと考えたけど、困ったときはやはりawkで対応してやるのが楽だ。

command | awk '{for(i=1;i<=NF;i++){if(!x[$i]){x[$i]++;y++};printf (NF==y) ? $0"\n" : ""}y=0}'

シェル芸とかで遊んでいると、よく以下のような数字でできた四角形を生成することがある。

blacknon@BS-PUB-UBUNTU-01:~$ echo {01..16} | xargs -n4
01 02 03 04
05 06 07 08
09 10 11 12
13 14 15 16

で、この時の数字の順番を斜め順、ジグザグ順でできないかと思ったのでやってみることにした。 一応できたはできたけど、エライ汚い…。

斜め順の場合

echo x{,,,}|fmt -1|awk '{a=a$0;printf("%4s\n",a)}'|tee >(tac|rev)|uniq|sed...

gnomon』という、出力に処理の経過時間を付与してくれるというツールを見かけたのだが、似たような事をシェル芸なりでできないかと思ったのでやってみることにした。 元々がmoreutilsのtsコマンドを発想元にしているということなので、以下のようにtsコマンドを利用すれば同様の事ができるようだ。

cmd | ts -s # 作業全体の経過時間
cmd | ts -i # 直前の出力からの経過時間
blacknon@BS-PUB-UBUNTU-01:~$ yes 'echo test_$(date +%Y%m%d_%H%M%S);sleep $((RANDOM%5))' | hea...

コンソール上で文字列を斜めに表示させたいなと思ったので、やってみることにした。 想定ではもっと簡単、かつ短く記述できると思っていたのだけど、意外とそうでもなかった。

とりあえずawkでやった場合。grepで一度各文字を1行づつに出力し直している。

echo qwerty | grep -o . | awk '{printf("%0"NR"s\n",""$1)}'
[root@BS-PUB-CENT7-01 ~]# echo qwerty | grep -o . | awk '{printf("%0"NR"s\n",""$1)}'
q
 w
  e
   r
    t
     y...

コンソール上で、サブドメインを後ろから順に階段状に出力させたいということがあったので、備忘で残しておく。 前にこちらで書いてた内容をベースに、以下のように記述することで、区切り文字(今回の場合は「.」)ごとに階段状に出力をしている。


ターミナル上で「n~mまでの列を出力」といった処理をする場合、個人的には以下のようにawkで処理をする場合が多かった。

awk '{for(i=n;i<=m;i++){a=a" "$i}gsub("^ ","",a);print a}'
[root@BS-PUB-CENT7-01 ~]# echo {01..10} | awk '{for(i=3;i<=8;i++){a=a" "$i}gsub("^ ","",a);print a}'
03 04 05 06 07 08

ただやはりこれだと長くてわかりにくいため、もうちょっと短く書けないかなと調べてみたので、備忘として残しておくこと...


ちょっとした処理で、拡張子より前で同名の名称が使われていて、かつ指定した拡張子が全て揃っているファイルのみを抽出する必要があったので、備忘で残しておく。 awkだけで処理させればもうちょっと短く書けそうな気もするが、ひとまず以下のように記述すれば対応可能だ。

ls -1 *.{txt,jpg,csv} | awk -F. '{a[$1]++}END{for(k in a)if(a[k]>2){print k}}' | xargs -I@ sh -c 'ls -la @*'

コンソール上で文字列を反転する場合というと、基本的にはrevコマンドを利用する。 で、もしそれを使わない場合どんな感じにできるかなと思ったので、ちょっとやってみた。 awkなら結構楽にできるのだが、個人的なマイブームからまずbashの変数展開を利用してみる。

で、残念ながら1行の出力にしか対応してないけど、以下のようにコマンドを実行することで出力を反転させることができる。 (forでreadlineにすれば複数行もいけると思うけど、ワンライナーでそれやるのはちょっとアレかなと思ったのでそれ以上は追求していない)

コマンド|(set -a;read i;l=${#i};echo eval...

ここでそんな感じの処理について見かけたので、シェル芸で文章中からの単語の集計(例:「ABCDandABCDorABCDsoABCD」だと、ABCDが4回使われているなど)をシェル芸で一発でできないかなと思ったので、試しにやってみることにした。 まず、文章の中から単語を取得する必要があるのだが、形態素解析だと対象が文章ではなくバラバラな羅列だった場合に使えないので、文章中から1文字づつ範囲をずらして文字を抽出し、それを集計することで使われている文字列を取得することにした。

で、ちょっと長いけど以下のようにすることで、複数回出現した文字列の組み合わせが取得できる。

echo ABCDan...

第30回シェル芸勉強会に行ってきたので、その復習。

問題、解説はこちら。 今回は、前半の問題は対象の知識があるかどうかに重きを置いていた様子。 後半はいつものような内容の問題だったので、個人的には後半のほうが取っ付きやすい印象だった。

Q1.

特定ディレクトリ配下から、「main.md」ファイルのみを対象に2行目の「Keywords:」行を抽出するという問題。 最初、2行目のみを抽出するためにawkを利用する方法を取っていた。

find ./posts/ -name main.md | xargs grep -n Keywords | awk -F: '$2==2{OFS=...

コンソール上で特定の文字列をワンライナーで指定回数リピートさせたい(例えば、0を10回リピート等)ということがあったので、備忘で残しておく。 いくつか方法があるようなので、それぞれ分けて残しておく。

1. ブレース展開を利用する

真っ先に利用する方法として思いつくのはブレース展開だと思う。 ただ、以下のように普通にechoにくっつけて利用すると展開時にはスペース区切りになってしまう。

echo 0{,,,,,}
blacknon@BS-PUB-UBUNTU-01:~$ echo 0{,,,,,}
0 0 0 0 0 0

一応、前にブレース展開の出力区切り文字を変更するの...


Qiitaでそんな感じの内容を見かけたので、awkでもできるんじゃないかと思ったのでやってみた。
特に難しいこともなく、gsubで削除するだけでいけるかと思ったが、ただ削除するだけだと「0」が消えてしまうのでNG。

awk '{gsub("^0*","",$n);print}' # 特定の列のみ対象
awk '{for(i=1;i<=NF;i++){gsub("^0*","",$i)};print}' # 全列を指定
blacknon@BS-PUB-UBUNTU-01:~$ cat pad.list
a01 00961 00046
a02 00127 00001
a03 00323...

ふと、ターミナル上で短歌っぽい横書きの文章を縦書きにして表示させることができないかなと思ったので、ちょっとやってみることにした。 rsコマンドがあるとある程度短く書けるのだが、それがないとちょっと長くなってしまった。 一番最初の行が最も長い行の場合、rsコマンドでスペース埋めができるのでちょっと短く記述できる。

rsコマンドを使った場合

# 最初の行が一番長い行ではない場合
echo -e 'あいうえお\0\0かきくけこから\0さしすせそ\0'|xargs -n1 -0 -I@ printf "%-21s\n" @|sed 's/ \{3\}/x/g;s/\B/,/g;s/x/ /g'...

ちょっとテキストファイルをいじってたところ、以下のようなファイルがあった。

blacknon@BS-PUB-UBUNTU-01:~$ cat test.list
a01 a02@aaa.com a03 a04 a05
b01 b02 @bbb.com b03 b04 b05
c01 c 02 @ccc.com c03 c04 c05
d01 d02@ddd.com d03 d04 d05
e01 e 0 2 @eee.com e03 e04 e05

空白区切りのファイルなのに、ダブルクォーテーション等で囲みもせずに、文字列内に空白を含めているようなファイル。 このようなファイルから、2...