プロジェクト

全般

プロフィール

ShellScript » 履歴 » バージョン 1

kanata, 2025/04/13 15:54

1 1 kanata
# ShellScript
2
3
目指せシェル芸人
4
5
{{rawhtml(<canvas id="map"></canvas><script src="/javascripts/pagemap.min.js"></script><script>pagemap(document.querySelector("#map"));</script>)}}
6
7
{{toc}}
8
9
主にbashについて書きます
10
11
# Basic
12
13
## if文
14
15
```
16
if [ 条件1 ]
17
then
18
  処理1
19
elif [ 条件2 ]
20
then
21
  処理2
22
else
23
  処理3
24
fi
25
```
26
27
### 数値比較
28
29
|数値評価演算子  | 意味                                   |
30
|----------------|----------------------------------------|
31
|数値1 -eq 数値2 | 数値1と数値2が等しい場合に真           |
32
|数値1 -ne 数値2 | 数値1と数値2が等しくない場合に真       |
33
|数値1 -gt 数値2 | 数値1が数値2より大きい場合に真         |
34
|数値1 -lt 数値2 | 数値1が数値2より小さい場合に真         |
35
|数値1 -ge 数値2 | 数値1が数値2より大きいか等しい場合に真 |
36
|数値1 -le 数値2 | 数値1が数値2より小さいか等しい場合に真 |
37
38
こういう書き方
39
40
```
41
if [ "1" -eq "0" ]
42
```
43
44
<br><br><br><br><br>
45
46
### 文字列比較
47
48
|文字列評価演算子   | 意味                            |
49
|-------------------|---------------------------------|
50
|文字列	            | 文字列の長さが0より大きければ真 |
51
|-n 文字列          | 文字列の長さが0より大きければ真 |
52
|! 文字列           | 文字列の長さが0であれば真       |
53
|-z 文字列          | 文字列の長さが0であれば真       |
54
|文字列1 = 文字列2  | 2つの文字列が等しければ真       |
55
|文字列1 != 文字列2 | 2つの文字列が等しくなければ真   |
56
57
こういう書き方
58
59
```
60
if [ "ABC" = "abc" ]
61
if [ -z "${FILEPATH}" ]
62
if test -z "${FILEPATH}"
63
```
64
65
### ファイルチェック
66
67
|ファイルチェック演算子  | 意味                                 |
68
|------------------------|--------------------------------------|
69
|-d ファイル名           | ディレクトリなら真                   |
70
|-f ファイル名           | 通常ファイルなら真                   |
71
|-L ファイル名           | シンボリックリンクなら真             |
72
|-r ファイル名           | 読み取り可能ファイルなら真           |
73
|-w ファイル名           | 書き込み可能ファイルなら真           |
74
|-x ファイル名           | 実行可能ファイルなら真               |
75
|-s ファイル名           | サイズが0より大きければ真            |
76
|ファイル1 -nt ファイル2 | ファイル1がファイル2より新しければ真 |
77
|ファイル1 -ot ファイル2 | ファイル1がファイル2より古ければ真   |
78
79
こういう書き方
80
81
```
82
if [ -f "${FILEPATH}" ]
83
if test -f "${FILEPATH}"
84
if [ "${FILE1}" -nt "${FILE2}" ]
85
```
86
87
### 論理結合
88
89
|論理結合演算子	| 意味                                   |
90
|---------------|----------------------------------------|
91
|! 条件         | 条件が偽であれば真                     |
92
|条件1 -a 条件2 | 条件1が真、かつ、条件2が真であれば真   |
93
|条件1 -o 条件2 | 条件1が真、または、条件2が真であれば真 |
94
95
こういう書き方
96
97
```
98
# AND(論理積演算子)
99
if [ -f a.txt -a -f b.txt ]
100
if [ "$a" -eq "$b" ] && [ "$c" -eq "$d" ]
101
102
# OR(論理和演算子)
103
if [ -f a.txt -o -f b.txt ]
104
if [ "$a" -eq "$b" or "$c" -eq "$d" ]
105
if [ "$a" -eq "$b" ] || [ "$c" -eq "$d" ]
106
107
# NOT(否定論理演算子)
108
if [ ! "$a" -eq "$b" ]
109
```
110
111
### 変数に任意の文字が含まれているかどうか
112
113
```
114
if echo $WORD | grep "任意の文字" >/dev/null
115
then
116
  echo "文字含まれている"
117
else
118
  echo "文字含まれていない"
119
fi
120
```
121
122
### bashのワイルドカード
123
124
実はif文内でワイルドカードの判定ができる。
125
126
```bash
127
STRING="abc"
128
if [[ $TEST == *"b"* ]]
129
	echo "matched"
130
else
131
	echo "unmatched"
132
fi
133
```
134
135
ワイルドカード部分 * は、ダブルコーテーションで囲んだりすると動かなくなるので注意。
136
137
### bashの正規表現
138
139
そもそもgrepを使わないでも、変数に任意の文字が含まれているかどうか判定できる。
140
141
```bash
142
STRING="abc"
143
if [[ "$STRING" =~ ^ab[cd]$ ]]; then
144
	echo "matched"
145
else
146
	echo "unmatched"
147
fi
148
```
149
150
正規表現部分 \^ab[cd]$ は、ダブルコーテーションで囲んだりすると動かなくなるので注意。
151
152
## 展開
153
154
bash入門
155
https://rat.cis.k.hosei.ac.jp/article/linux/bash_intro.html
156
157
### ブレース展開
158
159
カンマ区切りの文字列をブレースで囲んだものはブレース展開されます。
160
161
```
162
$ echo {a,b,c}
163
a b c
164
```
165
166
ブレースの前後に文字列があるとそれを反映した展開がなされます。
167
168
```
169
$ echo 0{a,b,c}1
170
0a1 0b1 0c1
171
```
172
173
ブレース展開はまとめてファイルやディレクトリを作成するときなどによく使われます。
174
175
```
176
$ find . -type d
177
.
178
179
$ mkdir -p a/b{0,1}/c{0,1}
180
$ find . -type d
181
.
182
./a
183
./a/b0
184
./a/b0/c0
185
./a/b0/c1
186
./a/b1
187
./a/b1/c0
188
./a/b1/c1
189
```
190
191
### チルダ展開
192
193
チルダはユーザーのホームディレクトリに展開されます。
194
195
```
196
$ whoami
197
user1
198
$ echo ~
199
/home/user1
200
```
201
202
### ファイル名展開
203
204
ファイル名展開はグロブ(glob)とも呼ばれます。
205
206
| パターン | 効果                                                                                                    |
207
|----------|---------------------------------------------------------------------------------------------------------|
208
| *        | 0文字以上の任意の文字列にマッチ。                                                                       |
209
| ?        | 1文字の任意の文字列にマッチ。                                                                           |
210
| [...]    | ブラケットで挟まれている文字のうち任意の1文字にマッチ。正規表現におけるブラケットの解釈とほぼ同じです。 |
211
212
213
```
214
$ ls
215
a  adc.txt  aec.txt  b
216
217
$ echo *
218
a adc.txt aec.txt b
219
220
$ echo ?
221
a b
222
223
$ echo a[abcd]*
224
adc.txt
225
226
# マッチするものが無かった場合パターンはそのままの形で残ります
227
$ echo xyz*
228
xyz*
229
```
230
231
### 変数展開
232
233
変数を参照する場合に文字列を加工することができる。
234
235
| パターン                | 効果                                                                                 |
236
|-------------------------|--------------------------------------------------------------------------------------|
237
| ${変数名#word}          | 変数から値を取り出し、その先頭部分がwordと一致した場合その部分を取り除きます。(先頭から前方最短一致した位置まで) |
238
| ${変数名##word}         | 変数から値を取り出し、その先頭部分がwordと一致した場合その部分を取り除きます。(先頭から前方最長一致した位置まで) |
239
| ${変数名%word}          | 変数から値を取り出し、その後方部分がwordと一致した場合その部分を取り除きます。(末尾から後方最短一致した位置まで) |
240
| ${変数名%%word}         | 変数から値を取り出し、その後方部分がwordと一致した場合その部分を取り除きます。(末尾から後方最長一致した位置まで) |
241
| ${変数名/pattern/word}  | 変数から値を取り出し、最初にpatternとマッチする部分だけをwordで置換します。          |
242
| ${変数名/#pattern/word} | 変数から値を取り出し、最初にpatternとマッチする部分だけをwordで置換します。(上と同じ)|
243
| ${変数名/%pattern/word} | 変数から値を取り出し、最後にpatternとマッチする部分だけをwordで置換します。          |
244
| ${変数名//pattern/word} | 変数から値を取り出し、patternとマッチする全ての部分をwordで置換します。              |
245
| ${変数名:=word}         | 変数が NULL の場合 word に置換され、かつ変数に代入される。                           |
246
| ${変数名:-word}         | 変数が NULL の場合 word を出力する。                                                 |
247
| ${変数名:+word}         | 変数が NULL 以外の場合 word に置換され、かつ変数に代入される。                       |
248
| ${変数名:?word}         | 変数が NULL の場合、標準エラー出力にwordを表示し、現在実行中のスクリプトを中止する。 |
249
| ${#変数名}              | 変数に設定されている文字数を返す。                                                   |
250
| ${変数名:offset}        | 変数に設定されているoffset文字+1から出力する。offsetを負の値にすると語尾から数える。 |
251
| ${変数名:offset:length} | 変数に設定されているoffset文字+1からlength分出力する。                               |
252
| ${変数名,}              | 先頭だけ小文字に変換                                                                 |
253
| ${変数名,,}             | 全部小文字に変換                                                                     |
254
| ${変数名\^}              | 先頭だけ大文字に変換                                                                |
255
| ${変数名\^\^}             | 全部大文字に変換                                                                   |
256
257
### 算術式展開
258
259
expr や、bcコマンドを使用しなくても、$((計算式))で計算ができる。
260
261
```
262
$ echo $(( 1 + 2 + 3 ))
263
6
264
```
265
266
Qiita - Bash $((算術式)) のすべて
267
https://qiita.com/akinomyoga/items/2dd3f341cf15dd9c330b
268
269
270
### コマンド置換
271
272
コマンド置換には以下の 2 つの形式があります。
273
274
```
275
$(command)
276
`command`
277
```
278
279
今までは一般的にバッククォート `command` が使われてきたが、$(command) も使えます。
280
$(command)は、入れ子ができるので、$(command)の方を使ったほうが良い。
281
282
~~~
283
$ TEST=`date`;echo $TEST
284
2016年 8月 28日 日曜日 18:04:33 JST
285
$ TEST=$(date);echo $TEST
286
2016年 8月 28日 日曜日 18:04:41 JST
287
~~~
288
289
次のコマンドはカレントディレクトリの中で「abc」を含むすべてのファイルの先頭10行を表示します。
290
291
```
292
$ head -10 $(grep -l abc *)
293
```
294
295
### ヒアドキュメント
296
297
こんな感じ。ftpぐらいなら自動化できる。
298
299
```bash
300
$ command << token
301
text
302
token
303
```
304
305
306
307
```bash
308
#!/bin/bash
309
310
# Script to retrieve a file via FTP
311
312
FTP_SERVER=ftp.nl.debian.org
313
FTP_PATH=/debian/dists/lenny/main/installer-i386/current/images/cdrom
314
REMOTE_FILE=debian-cd_info.tar.gz
315
316
ftp -n << _EOF_
317
open $FTP_SERVER
318
user anonymous me@linuxbox
319
cd $FTP_PATH
320
hash
321
get $REMOTE_FILE
322
bye
323
_EOF_
324
```
325
326
### ヒアストリング
327
328
変数を擬似的にファイルのように扱える。
329
330
```
331
$ cat < hoge
332
-bash: hoge: そのようなファイルやディレクトリはありません
333
 cat <<< hoge
334
hoge
335
$ HOGE=hogefuga
336
$ cat < $HOGE
337
-bash: hogefuga: そのようなファイルやディレクトリはありません
338
$ cat <<< $HOGE
339
hogefuga
340
```
341
342
## 特殊なシェル変数
343
344
| 変数 | 内容                                                 |
345
|------|------------------------------------------------------|
346
| $0   | 実行したスクリプトの文字列                           |
347
| $1, $2, ..., $9 | 第1引数, 第2引数, ..., 第9引数            |
348
| $#   | 与えられた引数の数                                   |
349
| $*   | 与えられたすべての引数. 引数全体が"で囲まれている    |
350
| $@   | 与えられたすべての引数. 引数一つ一つが"で囲まれている|
351
| $-   | シェルに与えられたフラグ                             |
352
| $?   | 最後に行ったコマンドの戻り値                         |
353
| $$   | 現在のシェルのプロセス番号                           |
354
| $!   | 最後にバックグラウンドで行ったコマンドのプロセス番号 |
355
| $_   | 実行シェルの文字列                                   |
356
| IFS  | 区切り文字のリスト                                   |
357
358
359
## シェル変数と環境変数
360
361
|            | 特徴                                   | 確認 | 設定                              | 削除 | 
362
|------------|----------------------------------------|------|-----------------------------------|------|
363
| シェル変数 | シェル変数は子プロセスに引き継がれない | set  | 変数名=値                         | unset # シェル変数、環境変数両方消去される
364
| 環境変数   | シェル変数は子プロセスに引き継がれる   | env  | export 変数名 or export 変数名=値 |unset # シェル変数、環境変数両方消去される
365
366
>ちなみに、環境変数を定義するには export コマンド以外にも declare コマンドに -x オプションを付けて実行する方法もある。
367
368
369
## プロセス置換
370
371
以下の文法に従えば、コマンドの実行結果を一時ファイルに吐いておく必要がなくなる。
372
373
~~~
374
<(command)
375
~~~
376
377
378
379
~~~
380
$ sdiff <(date;sleep 1;date) <(date;date)
381
2016年  8月 28日 日曜日 18:00:15 JST                            2016年  8月 28日 日曜日 18:00:15 JST
382
2016年  8月 28日 日曜日 18:00:16 JST                          | 2016年  8月 28日 日曜日 18:00:15 JST
383
~~~
384
385
## 連想配列
386
387
連想配列が使える。
388
389
>ver4系から使える機能です。Macのbashはver3系のため、brew等でアップデートしましょう。
390
391
```
392
$ declare -A hashTable
393
$ hashTable["hoge"]="fuga"
394
$ echo ${hashTable["hoge"]}
395
fuga
396
```
397
398
# bash開発機能
399
400
## 文法チェック
401
402
```
403
$ bash -n test.sh
404
```
405
406
## 実行内容をトレース
407
408
```
409
$ bash -x test.sh
410
```
411
412
## ステップ実行
413
414
擬似シグナルDEBUGを使う。擬似シグナルDEBUGはシェルが文を実行するたびに発行される。
415
416
```
417
#!/bin/bash
418
419
trap 'read -p "next(LINE:$LINENO)>> $BASH_COMMAND"' DEBUG
420
```
421
422
## 未定義の変数をエラーとして扱う
423
424
```
425
#!/bin/bash
426
set -u
427
428
# ${TEST}が未定義ならエラーになる
429
echo ${TEST}
430
```
431
432
## 終了ステータスが0以外ならその時点で終了
433
434
```
435
#!/bin/bash
436
set -e
437
438
#ls -yでエラーになり、後続の処理は実行されない
439
ls -y
440
ls -l
441
```
442
443
444
445
# Sample
446
447
## ファイルを一行ずつ読み込んで処理する
448
449
```
450
#!/bin/sh
451
# ファイルを1行ずつ読み込んで表示
452
453
TESTFILE=./hoge.txt
454
while read LINE
455
do
456
    echo $LINE
457
done < $TESTFILE
458
```
459
460
処理単位も行にしたい時は、冒頭に以下を書く
461
462
```
463
IFS='
464
'
465
```
466
467
## 区切り文字(デミリタ)で区切られた文字列の任意のフィールドを取得する
468
469
cut方式とawk方式がある
470
471
```
472
echo "abc:def:ghi:jkl"|cut -d: -f3
473
echo "abc:def:ghi:jkl"|awk -F':' '{print $3}'
474
```
475
476
ダブルクォーテーションで囲まれている場合
477
478
```
479
$ grep 設定項目名 設定ファイル | sed 's/[^"]*"\([^"]*\)"[^"]*/\1/g'
480
```
481
482
シングルクォーテーションで囲まれている場合
483
484
```
485
$ grep 設定項目名 設定ファイル | sed "s/[^']*'\([^']*\)'[^']*/\1/g"
486
```
487
488
イコールで定義している場合
489
490
```
491
$ grep 検索する設定項目 設定ファイルPATH | sed 's/ //g' | awk -F'=' '{ print $2}'
492
```
493
494
## 標準入力とファイルの内容を結合
495
496
497
~~~
498
$ cat test
499
efg
500
$ echo -n abcd |cat - test
501
abcdefg
502
~~~
503
504
>大抵のコマンドには - (標準入力の内容をファイル相当として扱う)を実装している
505
506
507
## サーバと連続でやりとりするシェルスクリプト
508
509
以下は、サーバからもらった計算式を計算して、送り返している例
510
511
```
512
#!/bin/sh
513
514
exec 5<>/dev/tcp/[host]/[port]
515
516
for I in {1..101}
517
do
518
  cat 0<&5>test.txt &
519
  sleep 1
520
  pkill cat
521
  WORD=`cat test.txt|tail -1|sed 's/=//g'`
522
  ANSWER=`ecgo ${WORD}|bc`
523
  echo ${ANSWER} > &5
524
  echo Debug [${I}] ${WORD} '=' ${ANSWER}
525
done
526
527
exit 0
528
```
529
530
## 文字列⇔16進数表現⇔2進数表現
531
532
文字列→16進数表現
533
534
```
535
echo "ABC"|xxd
536
```
537
538
文字列→2進数表現
539
540
```
541
echo "ABC"|xxd -b
542
```
543
544
16進数表現→文字列
545
546
```
547
echo 4142434445464748 | xxd -ps -r
548
```
549
550
16進数表現→2進数表現
551
552
```
553
echo 4142434445 | xxd -ps -r|xxd -b
554
```
555
556
2進数表現→16進数表現
557
558
```
559
echo "obase=16; ibase=2; 010000010100001001000011010001000100010100001010" | bc
560
```
561
562
2進数表現→文字列
563
564
```
565
echo "obase=16; ibase=2; 010000010100001001000011010001000100010100001010" | bc|xxd -ps -r
566
```
567
568
----
569
570
bcコマンドなくても、このぐらいはできる。
571
572
2進数表現→10進数表現
573
574
```
575
echo $((2#101010))
576
echo "obase=10; ibase=2; 101010" | bc
577
```
578
579
8進数表現→10進数表現
580
581
```
582
echo $((04567))
583
echo "obase=10; ibase=8; 4567" | bc
584
```
585
586
16進数表現→10進数表現
587
588
```
589
echo $((0xABCD))
590
echo "obase=10; ibase=16; ABCD" | bc
591
```
592
593
## 指定した相対PATHからの、絶対パス・ディレクトリ名・ファイル名の取得
594
595
```
596
$ cd /tmp/trash
597
$ readlink  -f test.txt 
598
/tmp/trash/test.txt
599
$ dirname `readlink  -f test.txt`
600
/tmp/trash
601
$ basename `readlink  -f test.txt`
602
test.txt
603
```
604
605
606
## IPアドレスの取得
607
608
いろんな方法があるが、NICのインタフェース名が不定だし、複数NICが刺さっていたりもするので、これという方法が無い。
609
610
```
611
$ ifconfig eth1 | grep -w 'inet' | cut -d: -f2 | awk '{ print $1}'
612
$ ip addr list venet0 | grep "inet " | cut -d' ' -f6 | cut -d/ -f1
613
$ ip a s | grep "inet " | cut -d' ' -f6 | cut -d/ -f1
614
$ hostname -i
615
$ hostname -I
616
```
617
618
実は[こういう技](https://ex1.m-yabe.com/archives/4638)がある
619
素晴らしい
620
621
```
622
$ curl ifconfig.io
623
```
624
625
626
627
## IPアドレスのソート
628
629
```
630
$ cat ip_list.txt | sort -n -t'.' -k1,1 -k2,2 -k3,3 -k4,4
631
```
632
633
## 特定のファイルのバックアップファイルを作成する
634
635
```
636
$ find PATH -type f -name 'ファイル名' | xargs -n 1 -I{} cp {} {}.bk
637
```
638
639
## 特定のファイルをバックアップディレクトリにコピーする
640
641
```
642
$ find PATH -type f -name 'ファイル名' | xargs -n 1 -I{} cp {} バックアップ先PATH
643
```
644
645
## 特定のファイルに対してのみgrepを行う
646
647
```
648
$ find PATH -type f -name 'ファイル名' | xargs -n 1 -I{} grep '検索キーワード' {}
649
```
650
651
## 指定したファイルの合計サイズを取得する
652
653
```
654
$ du -bhc [ファイルPATH等条件] | tail -n 1
655
```
656
657
または
658
659
```
660
$ ls -la [ファイルPATH等条件] | awk '{ total += $5 }; END { print total }'
661
```
662
663
## 対話型コマンドを自動実行する
664
665
対話中に判断や計算が入るとexpectでやるしかない。
666
667
```
668
#!/bin/expect -f
669
670
spawn ./121-calculation
671
672
set i 0
673
while {$i <= 100000} {
674
675
puts "\n#### $i ######"
676
677
expect "*="
678
set quest $expect_out(buffer)
679
680
set answer [exec echo $quest | tail -1 | tr -d '=' | tr -d '\r' | bc]
681
send "$answer\n"
682
683
incr i 1
684
}
685
```
686
687
命令解説
688
689
* spawn コマンド実行
690
* puts 画面表示(実行したコマンドには影響しない)
691
* expect コマンド出力する文字にマッチングするまでここで止まる
692
* set 変数 定数 変数設定
693
* set 変数 $expect_out(buffer) コマンド実行結果を変数に(ただし、直前のではなく、全部入るっぽい)
694
* set 変数 [外部コマンド] 外部コマンドの実行結果を変数に(最新行を選択するtail -1 と 変な改行コード入ってエラーになるので tr -d '\r' を入れるのがコツ)
695
* send "変数名\n" コマンドに実行結果を送る(データ終端を\nにしているコマンドが多いので、明示的に\nを書く)
696
* incr 変数名 定数 加算
697
698
## どういう形式で圧縮されているか判別して展開する
699
700
環境合わせて、適当に修正が必要
701
702
```
703
#!/bin/sh
704
705
LIST=`ls|grep -v flatting.sh`
706
707
for WORD in ${LIST}
708
do
709
  FILE_TYPE=`file ${WORD}`
710
711
  if echo ${FILE_TYPE}|grep "Zip archive data" >/dev/null
712
  then
713
    unzip ${WORD}
714
    rm ${WORD}
715
  elif echo ${FILE_TYPE}|grep "shell archive text" >/dev/null
716
  then
717
    sh ${WORD}
718
    rm ${WORD}
719
  elif echo ${FILE_TYPE}|grep "bzip2 compressed data" >/dev/null
720
  then
721
    bunzip2 -c ${WORD} > ${WORD}_out
722
    rm ${WORD}
723
  elif echo ${FILE_TYPE}|grep "POSIX tar archive" >/dev/null
724
  then
725
    tar xvf ${WORD}
726
    rm ${WORD}
727
  elif echo ${FILE_TYPE}|grep "gzip compressed data" >/dev/null
728
  then
729
    gunzip -c ${WORD} > ${WORD}_out
730
    rm ${WORD}
731
  elif echo ${FILE_TYPE}|grep "xz compressed data" >/dev/null
732
  then
733
    xz -d -c ${WORD} > ${WORD}_out
734
    rm ${WORD}
735
  elif echo ${FILE_TYPE}|grep "ASCII cpio archive" >/dev/null
736
  then
737
    cpio -idv < ${WORD}
738
    rm ${WORD}
739
  elif echo ${FILE_TYPE}|grep "ASCII text" >/dev/null
740
  then
741
    if cat ${WORD} |grep "This is dummy file" >/dev/null
742
    then
743
      rm ${WORD}
744
    else
745
      base64 -d ${WORD} > ${WORD}_out  
746
      if [ ${?} = 0 ]
747
      then
748
        rm ${WORD}
749
      fi
750
    fi
751
  else
752
    :
753
  fi
754
755
done
756
757
exit 0
758
```
759
760
## 一定時間ごとに任意のコマンドを実行する
761
762
もちろんwatchコマンドを使ってもいいが、以下の方法でも可能。
763
764
```
765
$ yes '[任意のコマンド]; sleep [任意のインターバル秒数];' | sh
766
```
767
768
## 文字列への色つけ
769
770
```
771
txtund=$(tput sgr 0 1)    # Underline
772
txtbld=$(tput bold)       # Bold
773
txtred=$(tput setaf 1)    # Red
774
txtgrn=$(tput setaf 2)    # Green
775
txtylw=$(tput setaf 3)    # Yellow
776
txtblu=$(tput setaf 4)    # Blue
777
txtpur=$(tput setaf 5)    # Purple
778
txtcyn=$(tput setaf 6)    # Cyan
779
txtwht=$(tput setaf 7)    # White
780
txtrst=$(tput sgr0)       # Text reset
781
782
echo "${txtbld}This is bold text output from shell script${txtrst}"
783
```
784
785
## プログレスバーの表示
786
787
```
788
show_progress() {
789
    echo -ne '#####                     (33%)\r'
790
    sleep 1
791
    echo -ne '#############             (66%)\r'
792
    sleep 1
793
    echo -ne '#######################   (100%)\r'
794
    echo -ne '\n'    
795
}
796
```
797
798
## スピナーの表示
799
800
```
801
show_spin () {
802
  rotations=3
803
  delay=0.1
804
  for i in `seq 0 $rotations`; do
805
    for char in '|' '/' '-' '\'; do
806
      echo -n $char
807
      sleep $delay
808
      printf "\b"
809
    done
810
  done
811
}
812
```
813
814
## あらゆるファイルの解凍
815
816
```
817
function extract {
818
 if [ -z "$1" ]; then
819
    # display usage if no parameters given
820
    echo "Usage: extract <path/file_name>.<zip|rar|bz2|gz|tar|tbz2|tgz|Z|7z|xz|ex|tar.bz2|tar.gz|tar.xz>"
821
 else
822
    if [ -f $1 ] ; then
823
        # NAME=${1%.*}
824
        # mkdir $NAME && cd $NAME
825
        case $1 in
826
          *.tar.bz2)   tar xvjf ../$1    ;;
827
          *.tar.gz)    tar xvzf ../$1    ;;
828
          *.tar.xz)    tar xvJf ../$1    ;;
829
          *.lzma)      unlzma ../$1      ;;
830
          *.bz2)       bunzip2 ../$1     ;;
831
          *.rar)       unrar x -ad ../$1 ;;
832
          *.gz)        gunzip ../$1      ;;
833
          *.tar)       tar xvf ../$1     ;;
834
          *.tbz2)      tar xvjf ../$1    ;;
835
          *.tgz)       tar xvzf ../$1    ;;
836
          *.zip)       unzip ../$1       ;;
837
          *.Z)         uncompress ../$1  ;;
838
          *.7z)        7z x ../$1        ;;
839
          *.xz)        unxz ../$1        ;;
840
          *.exe)       cabextract ../$1  ;;
841
          *)           echo "extract: '$1' - unknown archive method" ;;
842
        esac
843
    else
844
        echo "$1 - file does not exist"
845
    fi
846
fi
847
}
848
```
849
850
> このコマンドも有用と思われ
851
852
俺的備忘録 〜なんかいろいろ〜 - ファイルの圧縮方式に合わせて自動的に解凍してくれる『dtrx』コマンド
853
https://orebibou.com/2016/07/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E5%9C%A7%E7%B8%AE%E6%96%B9%E5%BC%8F%E3%81%AB%E5%90%88%E3%82%8F%E3%81%9B%E3%81%A6%E8%87%AA%E5%8B%95%E7%9A%84%E3%81%AB%E8%A7%A3%E5%87%8D%E3%81%97%E3%81%A6/
854
855
## POSIX仕様の中でif文中でパターンマッチングする
856
857
```
858
if [ -n "$X" -a -z "${X%%pattern}" ] ; then echo "true"; else echo "false"; fi
859
```
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
# KnowHow
877
878
シェルスクリプトを極める
879
http://www.slideshare.net/bsdhack/ss-43064758
880
881
## バッファリングさせない
882
883
stdoutがttyじゃないとbufferingするコマンドは多い
884
stdbufで解決できる
885
886
887
```
888
$ command | stdbuf -oL command | command
889
```
890
891
>行単位でバッファを吐き出す
892
893
```
894
$ command | stdbuf -oO command | command
895
```
896
897
>バッファリングさせない
898
899
900
以下が神解説です
901
902
俺的備忘録 〜なんかいろいろ〜 - sedやawk、grepをバッファさせずに動作させる
903
https://orebibou.com/2017/06/sed%e3%82%84awk%e3%80%81grep%e3%82%92%e3%83%90%e3%83%83%e3%83%95%e3%82%a1%e3%81%95%e3%81%9b%e3%81%9a%e3%81%ab%e5%8b%95%e4%bd%9c%e3%81%95%e3%81%9b%e3%82%8b/
904
905
906
## ファイルディスクリプタ
907
908
* 標準入力は 0
909
* 標準出力は1
910
* 標準工ラー出力は2
911
912
シエルではリダイレクト記号の前に数字
913
914
```
915
 &> ファイル  # 標準出力・標準エラー出力を共にリダイレクト
916
 3> ファイル  # fd3をファイルにリダイレクト
917
 4< ファイル  # fd4をファイルからリダイレクト
918
 5<&-         # fd5をクローズ 
919
 6>&7         # fd7をfd6に複製(fd6をfd7にリダイレクト)
920
```
921
922
---
923
実装
924
925
926
```
927
command > file
928
```
929
930
>結果、fdが標準出力として使用される
931
932
```
933
command > file 2>&1
934
```
935
936
>結果、fdが標準出力、および、標準エラー出力として使用される
937
938
```
939
commandA |commandB
940
```
941
942
>結果、commandA の標準出力が commandB の標準入力になる
943
944
945
```
946
$ ./echo "test" 3>&1 1>&2 2>&3
947
```
948
949
>標準エラー出力と標準出力を入れ替える
950
951
952
953
## プロセスの出力同士をdiffで比較
954
955
```
956
$ diff <(commandA)<(commandB)
957
958
$ commandA|(commandB|diff /dev/fd/3 -) 3<&0
959
```
960
961
> 3<&0 で標準入力(fd0) を fd3 に複製
962
> commandA の出力が fd3 に出力される
963
> /dev/fd/3 からの入力が commandA の出力
964
965
## パイプの途中のプロセスの終了コード
966
967
```
968
$ exec 3>&1 
969
ret1=`{ { commandA 1; echo $? 1>&3; } | commandB 2; } 3>&1` 
970
ret2=$?
971
```
972
973
> コマンド 1 の $? を 1>&3 で fd3 に出力
974
> fd3 の内容を 3>&1 で ret1 に格納
975
976
## ループ処理でのファイルディスクリプタの活用
977
978
```
979
exec 3<&0 0<<EOF
980
 `command` 
981
EOF 
982
while read line 
983
do
984
  : 
985
done 
986
exec 0<&3 3<&-
987
```
988
>exec 3<&0 で fd0 を fd3 に複製 
989
>exec 0<<EOF でヒアドキュメントを fd0 として使用 
990
>exec 0<&3 で複製した fd0 を復帰 
991
>exec 3<&- で fd3 をクローズ 
992
>パイプを使わないのでプロセスが生成されない
993
994
995
## evalを用いた配列的な変数アクセス
996
997
```
998
# $1: 配列名 
999
# $2: インデックス 
1000
# $3-: 値 
1001
1002
setarray() 
1003
{
1004
  _index_="__`echo $1 $2 | sed -e 's/ //g'`"
1005
  shift 2
1006
  eval $_index_'='"$@" 
1007
}
1008
1009
getarray() 
1010
{
1011
  eval echo '${'__`echo $* | sed -e 's/ //g'`'}' 
1012
}
1013
```
1014
1015
## ファイルの上書き
1016
1017
元ファイルを書き替える
1018
1019
```
1020
$ コマンド < ファイル > ファイル # 絶対ダメ!
1021
$ (rm ファイル ; コマンド > ファイル ) < ファイル # ファイルの中身が消えない方法
1022
        ②                     ③          ①
1023
```
1024
1025
>① でファイルが読み込みモードでオープンされる。
1026
>② でオープンされたファイルが削除される。
1027
>③ で新しい(別な)ファイルが書き込みモードでオープンされる。 
1028
1029
①のファイルと③のファイルは同じファイル名ですが、 inode が異なっているため、別のファイルとして扱われる。
1030
1031
1<>を使う方法もある
1032
1033
```
1034
cat srcfile 1<> srcfile
1035
```
1036
1037
## シェルスクリプトでの排他処理
1038
1039
```
1040
lockfile="/var/tmp/`basename ${0}`" 
1041
test -f ${lockfile} || touch ${lockfile}
1042
```
1043
1044
>test(1) と touch(1) の間に他のプロセスの test(1) が実行されると 排他処理が失敗する 
1045
1046
```
1047
lockfile="/var/tmp/`basename ${0}`" 
1048
if ln -s $$ ${lockfile} 2> /dev/null 
1049
then
1050
  : # メイン処理を実行 
1051
else
1052
  echo "${0}: exist another instance" 1>&2 
1053
  exit 255 
1054
fi 
1055
trap 'rm -f ${lockfile}; exit' 0 1 2 3 11 15
1056
```
1057
1058
>シンボリックリンクの作成は atomic
1059
>PID をリンクする事でロックしたプロセスが特定できる 
1060
1061
## 英単語の先頭文字を大文字に変換する
1062
1063
```
1064
$ echo "foo" | awk '{ print toupper(substr($0, 1, 1)) substr($0, 2, length($0) - 1) }'
1065
```
1066
1067
>awk 組み込みの substr() 、 toupper() を使用する 
1068
1069
```
1070
$ eval eval `echo "foo" | sed 's/(.)(.*)//bin/echo -n 1 | tr "[a-z]" "[A-Z]"; echo 2/g'`
1071
```
1072
1073
>sed 単体では実現できないのでコマンド列を出力して eval
1074
1075
```
1076
$ echo foo bar buz|sed 's/\b\([a-z]\)/\U\1/g'
1077
```
1078
1079
>実はsed単体でできました
1080
1081
## IF文のような分岐処理をワンライナーで行わせる
1082
1083
```
1084
$ コマンド && TRUE || FALSE
1085
```
1086
1087
「&&」以降はコマンドがTRUEの場合に、「||」以降はFALSEの場合に実行されるコマンドとなる。
1088
1089
1090
1091
```
1092
$ cat test.log 
1093
abc [31/Oct/2015:20:36:51 +0900] ghi [20]
1094
jkl [31/Oct/2015:20:36:55 +0900] pqr [0]
1095
stu [31/Oct/2015:20:37:00 +0900] yz  [40]
1096
000 [31/Oct/2015:20:37:05 +0900] ghi [5]
1097
$ grep abc test.log >/dev/null && echo 1 || echo 2
1098
1
1099
$ grep azc test.log >/dev/null && echo 1 || echo 2
1100
2
1101
```
1102
1103
1104
## exの活用
1105
1106
ファイルの行操作に ex を活用する
1107
1108
- 直接行の追加や削除が可能(一時ファイル不要)
1109
– 実は vi なので正規表現など強力な編集操作が可能
1110
– echo やヒアドキュメントで編集コマンドを指定可能
1111
1112
### exで行の追加
1113
1114
```
1115
$ /bin/ex -s ファイル << EOF
1116
行番号 a
1117
コンテンツ
1118
コンテンツ
1119
    :
1120
.
1121
w!
1122
EOF
1123
```
1124
1125
>行番号で指定した行の下にコンテンツを挿入する
1126
> . で挿入モードを終了し w! で内容をファイルに出力する
1127
1128
### exで行削除処理
1129
1130
```
1131
$ /bin/ex -s ファイル << EOF
1132
行番号 d
1133
w!
1134
EOF
1135
```
1136
1137
>行番号で指定した行を削除する
1138
> w! で内容をファイルに出力する
1139
1140
### exで行番号を指定した行置換処理
1141
1142
```
1143
$ /bin/ex -s ファイル << EOF
1144
行番号 s/パターン/置換文字列/
1145
w!
1146
EOF
1147
```
1148
1149
>行番号で指定した行のパターンを置換文字列に置換する
1150
> w! で内容をファイルに出力する
1151
1152
### exでパターンを指定した行置換処理
1153
1154
```
1155
$ /bin/ex -s ファイル << EOF
1156
/パター /s/ 置換文字 /
1157
w!
1158
EOF
1159
```
1160
1161
>最初に発見したパターンを置換文字列に置換する
1162
>s の後ろの連続した // は直前の正規表現(パターン)を示す
1163
> w! で内容をファイルに出力する
1164
1165
### exでファイルから指定された行を削除する 
1166
1167
```
1168
$ cat basefile
1169
first line
1170
second line
1171
third line
1172
    :
1173
    :
1174
1175
1176
$ cat target
1177
2
1178
3
1179
5
1180
:
1181
:
1182
1183
1184
```
1185
1186
>元のファイル (basefile) には複数の行が含まれている
1187
>削除する行は別なファイル (target) に格納されている
1188
>target ファイルには行番号が 1 行ずつ格納されている
1189
1190
パターンを指定した行置換処理
1191
1192
```
1193
$ sort target | awk '{ printf "%ddn", $1-(NR-1); } END { print "w!" }' | ex -s basefile
1194
```
1195
1196
>削除済みの行を考慮する必要がある
1197
>>→ 2 行目を削除すると今までの 3 行目が 2 行目になる
1198
>ex で行を削除する
1199
1200
>awk で削除した行を考慮した行番号に対する行削除を出力 
1201
1202
1203
1204
## シェルスクリプトで標準入力を受け取る
1205
1206
3パターンある。
1207
1208
### cat - で 標準入力を受け取り、利用できる。
1209
1210
```
1211
#!/bin/sh
1212
if [ -p /dev/stdin ] ; then
1213
    a=$(cat -)
1214
    echo "input string is ${a}"
1215
else
1216
    echo "nothing stdin"
1217
fi
1218
```
1219
1220
### readを使うと、標準入力を読むことができる
1221
1222
```
1223
#!/bin/bash
1224
while read i ; do
1225
    #数字を読み込んで1足して出力する。
1226
    echo $((i+1))
1227
done
1228
```
1229
1230
他の方法に比べて高コストになりがち
1231
1232
### /dev/stdinを使う
1233
1234
```
1235
#!/bin/bash
1236
awk '{print $1+1}' < /dev/stdin
1237
```
1238
1239
## 一時ファイルを作らずにコマンドの出力結果を利用する(Command Substitution)
1240
1241
<(コマンド)記法、ksh、bash、zsh限定。
1242
1243
コマンドに対する入出力を、ファイルのように指定することが出来る。
1244
1245
* コマンドの標準出力を入力ファイルとして <(コマンド)
1246
* コマンドの標準入力を出力ファイルとして >(コマンド)
1247
1248
例えば、
1249
1250
```
1251
$ diff <(ls) <(ls -a)
1252
```
1253
1254
これと同義
1255
1256
```
1257
$ ls > a
1258
$ ls -a > b
1259
$ diff a b
1260
```
1261
1262
## パイプラインに任意の値を入れ込む
1263
1264
### 文字列入れ
1265
1266
```
1267
$ seq 2 | (echo 'Header'; cat; echo 'Footer')
1268
Header
1269
1
1270
2
1271
Footer
1272
```
1273
1274
```
1275
$ seq 5 | (echo 'obase=2'; cat) | bc
1276
1
1277
10
1278
11
1279
100
1280
101
1281
```
1282
1283
### ファイル入れ
1284
1285
"-"が標準入力のこと
1286
1287
```
1288
$ seq 11 12 | cat data -
1289
1
1290
2
1291
11
1292
12
1293
```
1294
1295
## パイプで渡したコマンドの終了ステータスを得る
1296
1297
```
1298
$ cat test.txt |cat|cat|cat|cat|cat
1299
$ echo ${PIPESTATUS[@]}
1300
0 0 0 0 0 0
1301
```
1302
1303
## bashでパイプで受け取った値を自動エスケープして出力する
1304
1305
俺的備忘録 〜なんかいろいろ〜 - bashでパイプで受け取った値を自動エスケープして出力する
1306
https://orebibou.com/2017/07/bash%e3%81%a7%e3%83%91%e3%82%a4%e3%83%97%e3%81%a7%e5%8f%97%e3%81%91%e5%8f%96%e3%81%a3%e3%81%9f%e5%80%a4%e3%82%92%e8%87%aa%e5%8b%95%e3%82%a8%e3%82%b9%e3%82%b1%e3%83%bc%e3%83%97%e3%81%97%e3%81%a6/
1307
1308
## echo、printfでパイプから受けた値を出力する
1309
1310
俺的備忘録 〜なんかいろいろ〜 - echo、printfでパイプから受けた値を出力する
1311
https://orebibou.com/2017/07/echo%e3%80%81printf%e3%81%a7%e3%83%91%e3%82%a4%e3%83%97%e3%81%8b%e3%82%89%e5%8f%97%e3%81%91%e3%81%9f%e5%80%a4%e3%82%92%e5%87%ba%e5%8a%9b%e3%81%99%e3%82%8b/
1312
1313
## サロゲートペアやUnicode結合文字を考慮して文字単位に分解する
1314
1315
NG
1316
1317
```
1318
$ echo そのチェㇷ゚は𩸽🇯🇵 | grep -o .|tr '\n' '/'
1319
そ/の/チ/ェ/ㇷ/゚/は/𩸽/🇯/🇵
1320
```
1321
1322
OK
1323
1324
```
1325
$ ruby -e 'puts "そのチェㇷ゚は𩸽🇯🇵".scan(/\X/).join("/")'
1326
そ/の/チ/ェ/ㇷ゚/は/𩸽/🇯🇵
1327
```
1328
1329
1330
1331
1332
1333
1334
1335
1336
# awk KnowHow
1337
1338
AWK 一行野郎百裂拳 - 日本 GNU AWK ユーザー会 - No-ip.org
1339
http://gauc.no-ip.org/awk-users-jp/material/100_one_liners_20131223.pdf
1340
1341
> 神
1342
1343
## 特定の N 行を表示する
1344
1345
```
1346
$ awk 'NR==N'
1347
```
1348
1349
## 空行を削除する
1350
1351
```
1352
$ awk 'NF'
1353
$ awk '$0'
1354
$ awk '/./'
1355
```
1356
1357
## 文字数をカウントする(wc -c)
1358
1359
```
1360
$ awk '{n+=length($0)} END{print n}'
1361
```
1362
1363
## 単語数をカウントする(wc -w)
1364
1365
```
1366
$ awk '{n+=NF} END{print n}'
1367
```
1368
1369
## 行数をカウントする(wc -l)
1370
1371
```
1372
$ awk 'END{print NR}'
1373
```
1374
1375
## 行末の空白やタブを削除する
1376
1377
```
1378
$ awk '{sub(/[ \t]+$/, "")}1'
1379
```
1380
1381
## Unix の改行コードに変換する
1382
1383
```
1384
$ awk 'sub(/\r$/,"")'
1385
```
1386
1387
## Windows の改行コードに変換する
1388
1389
```
1390
$ awk 'sub(/$/,"\r")'
1391
```
1392
1393
## 逆順出力をする(tac)
1394
1395
```
1396
$ awk '{a[i++]=$0} END{for(j=i-1; j>=0;) print a[j--]}'
1397
```
1398
1399
## 重複するレコードを削除する(uniq)
1400
1401
```
1402
$ awk '!a[$0]++'
1403
```
1404
1405
## ソートしないで重複行を削除する
1406
1407
```
1408
awk '!a[$0]++' FILE
1409
```
1410
1411
## 行番号を付ける(nl)
1412
1413
```
1414
$ awk '$0 = NR OFS $0'
1415
```
1416
1417
## 標準出力にそのまま出力する(cat -)
1418
1419
```
1420
$ awk '1'
1421
```
1422
1423
## 正規表現にマッチした行を表示する(grep)
1424
1425
```
1426
$ awk '/hogehoge/'
1427
```
1428
1429
## 正規表現にマッチしない行を表示する(grep -v)
1430
1431
```
1432
$ awk '! /hogehoge/'
1433
```
1434
1435
## コメント行を削除する
1436
1437
```
1438
$ awk '! /^#/'
1439
```
1440
1441
## C言語のように複数行にまたがったコメント行を削除する
1442
1443
/* と */ に囲まれた行の場合
1444
1445
```
1446
$ cat file | awk '/\/\*/, /\*\//{next}{print}'
1447
```
1448
1449
## 指定行から指定行までを表示する
1450
1451
```
1452
$ awk 'NR==10,NR==20'
1453
```
1454
1455
## 偶数行を表示する
1456
1457
```
1458
$ awk 'NR%2==0'
1459
```
1460
1461
## 奇数行を表示する
1462
1463
```
1464
$ awk 'NR%2'
1465
```
1466
1467
## 特定のフィールド数を持つ行のみを抜き出す
1468
1469
```
1470
$ cat file
1471
りんご 100円 192個
1472
ばなな 170円 210個
1473
爽健美茶 150円
1474
グラタン ソース マカロニ チーズ じゃがいも
1475
$ cat file | awk 'NF>=2 && NF<=3'
1476
りんご 100円 192個
1477
ばなな 170円 210個
1478
爽健美茶 150円
1479
```
1480
1481
## awkで\[\]\(カギカッコ\)内の値に応じて行を抽出する
1482
1483
```
1484
$ awk -F '[][]' '$4 >= 〇〇' ログファイルPATH
1485
```
1486
1487
1488
1489
```
1490
$ cat log.txt
1491
abc [31/Oct/2015:20:36:51 +0900] ghi [20]
1492
jkl [31/Oct/2015:20:36:55 +0900] pqr [0]
1493
stu [31/Oct/2015:20:37:00 +0900] yz  [40]
1494
000 [31/Oct/2015:20:37:05 +0900] ghi [5]
1495
$ awk -F '[][]' '$4 >= 20' log.txt
1496
abc [31/Oct/2015:20:36:51 +0900] ghi [20]
1497
stu [31/Oct/2015:20:37:00 +0900] yz  [40]
1498
```
1499
1500
## 異なるデミリタを指定して、要素を取り出す
1501
1502
```
1503
$ cat log.txt
1504
abc [31/Oct/2015:20:36:51 +0900] ghi [20]
1505
jkl [31/Oct/2015:20:36:55 +0900] pqr [0]
1506
stu [31/Oct/2015:20:37:00 +0900] yz  [40]
1507
000 [31/Oct/2015:20:37:05 +0900] ghi [5]
1508
$ awk -F '[][]' '{print $1}' log.txt
1509
abc 
1510
jkl 
1511
stu 
1512
000 
1513
$ awk -F '[][]' '{print $2}' log.txt
1514
31/Oct/2015:20:36:51 +0900
1515
31/Oct/2015:20:36:55 +0900
1516
31/Oct/2015:20:37:00 +0900
1517
31/Oct/2015:20:37:05 +0900
1518
$ awk -F '[][]' '{print $3}' log.txt
1519
 ghi 
1520
 pqr 
1521
 yz  
1522
 ghi 
1523
$ awk -F '[][]' '{print $4}' log.txt
1524
20
1525
0
1526
40
1527
5
1528
```
1529
1530
## awkの編集結果をファイルにリダイレクトで出力して保存する
1531
1532
awkでリダイレクトを行う場合、たとえば「tail -F」などと組み合わせて利用する場合、単純に「>」で指定してもリダイレクトが行われない場合がある。
1533
1534
* 例:tail -F で「/work/test」というファイルを常時監視し、その内容に日付を付け足して「/work/test_log」ファイルに出力しようとしている。
1535
1536
```
1537
$ tail -F /work/test | awk '{ print strftime("%Y/%m/%d %H:%M:%S") " " $0 }' > /work/test_log
1538
```
1539
1540
tail -Fとawkを組み合わせてファイルに出力する場合、以下のようにリダイレクトの前に「{ system (" ")}」と記述する必要がある。
1541
1542
```
1543
$ tail -F /work/test | awk '{ print strftime("%Y/%m/%d %H:%M:%S") " " $0 } { system (" ") }' > /work/test_log
1544
```
1545
1546
## Excelのフィルタのように、ファイルから〇〇以上、〇〇以下で行を抽出する
1547
1548
```
1549
$ awk '列 >= 条件 && 列 <= 条件' 対象ファイル
1550
$ awk '列 == 条件' 対象ファイル
1551
$ awk '列 ~ /条件/ 対象ファイル'
1552
```
1553
1554
```
1555
$ cat test.txt 
1556
1 abc 10
1557
2 def 15
1558
3 ghi 0
1559
$ awk '$3 >=5 && $3 <=10' test.txt
1560
1 abc 10
1561
$ awk '$2=="def"' test.txt
1562
2 def 15
1563
$ awk '$2 ~ /abc|ghi/' test.txt
1564
1 abc 10
1565
3 ghi 0
1566
```
1567
1568
1569
## ◯◯時からXX時までの間のログを抽出
1570
1571
```
1572
$ awk -F - '"Apr  5 14:30:00" < $1 && $1 <= "Apr  5 15:00:00"' /var/log/messages
1573
```
1574
1575
## grepのように行を抽出
1576
1577
```
1578
$ awk '/文字列/' 対象ファイル
1579
```
1580
1581
## 任意の行を抽出
1582
1583
```
1584
$ awk 'NR==行' 対象ファイル
1585
$ awk 'NR==最初の行,NR==終わりの行' 対象ファイル
1586
```
1587
1588
## 文字列で範囲を指定して出力
1589
1590
```
1591
$ awk '/任意の開始文字列/,/任意の終了文字列/' 対象ファイル
1592
```
1593
1594
例えば、ファイル2カラム目の「00:00:02」~「00:00:04」までを抽出する場合
1595
1596
```
1597
$ awk '$2 >= "00:00:02" && "00:00:04" >= $2' /tmp/sample.log
1598
```
1599
1600
複数条件(1行目、文字列XXを含む行を抽出とか)で抽出する場合は、awk内でifを使用できる。
1601
1602
```
1603
$ awk '{if(条件1||条件2) print}' 対象ファイル # or条件
1604
$ awk '{if(条件1&&条件2) print}' 対象ファイル # and条件
1605
```
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
# sed KnowHow
1616
1617
## 基本的な使い方
1618
1619
```
1620
$ sed 's/置換前文字列/置換後文字列/g' ファイルPATH
1621
$ コマンド | sed 's/置換前文字列/置換後文字列/g'
1622
```
1623
1624
## ◯行目~◯行目にある特定の文字列を置換する場合
1625
1626
```
1627
$ sed '4,8s/line/gyou/g' test.txt
1628
```
1629
1630
## 特定の文字列を含む行のみ置換する場合
1631
1632
```
1633
$ sed '/検索文字列/s/置換前文字列/置換後文字列/g'
1634
```
1635
1636
## その行に表示される◯番目の文字列を置換する
1637
1638
```
1639
$ sed 's/置換前文字列/置換後文字列/何文字目か'
1640
```
1641
1642
## マッチした文字列を再利用する
1643
1644
1個だけと複数指定可能な2つの方法がある
1645
1646
### 1個だけの方法
1647
1648
置換後文字列の所に&を書くと、その部分がマッチした文字列に置き換わる。
1649
1650
~~~
1651
$ echo "abc def hij klm" |sed -e 's/a[a-z]c/XXX & XXX/g'
1652
XXX abc XXX def hij klm
1653
~~~
1654
1655
### 複数指定可能な方法
1656
1657
\\( と \\) で囲んだものがマッチした場合、順番に \1 \2 \3 と指定できる。
1658
1659
~~~
1660
$ echo "abc def hij klm" |sed -e 's/^\([a-z][a-z][a-z]\) \(d[a-z][a-z]\) \([a-z][a-z][a-z]\) \([a-z][a-z][a-z]\)/XXX \4 \3 \2 \1 XXX/g'
1661
XXX klm hij def abc XXX
1662
~~~
1663
1664
## 指定した行番号の箇所に行を挿入する
1665
1666
```
1667
$ sed '◯i 挿入する行'
1668
$ sed '4i testline' test.txt
1669
```
1670
1671
## 指定した行の後ろに行を挿入する
1672
1673
```
1674
$ sed '◯a 挿入する行'
1675
$ sed '4a testline' test.txt
1676
```
1677
1678
## 指定した複数の各行の前、後ろに行を挿入する
1679
1680
```
1681
$ sed '◯,◯i 挿入する行' #前に挿入する場合
1682
$ sed '◯,◯a 挿入する行' #後に挿入する場合
1683
```
1684
1685
## 指定したキーワードを持つ行の前・後に行を挿入する
1686
1687
```
1688
$ sed '/キーワード/i 挿入する行' #前に挿入する場合
1689
$ sed '/キーワード/a 挿入する行' #後に挿入する場
1690
```
1691
1692
## ◯行目~◯行目を削除する
1693
1694
```
1695
$ sed '◯,◯d'
1696
$ sed '1,4d' test.txt
1697
```
1698
1699
## 決まったキーワードを持つ行を除外する
1700
1701
```
1702
$ sed '/キーワード/d'
1703
$ sed '/line2/d' test.txt
1704
```
1705
1706
## ◯行目の内容を上書きする
1707
1708
```
1709
$ sed '◯c 置き換え後の行'
1710
$ sed '4c testline' test.txt
1711
```
1712
1713
## 特定のキーワードを持つ行を上書きする
1714
1715
```
1716
$ sed '/キーワード/c 置き換え後の行'
1717
$ sed '/line3/c testline' test.txt
1718
```
1719
1720
## ファイルの内容を上書きする
1721
1722
```
1723
$ sed -i '置換条件' ファイルPATH
1724
$ sed 's/line 1/line #/g' test.txt
1725
```
1726
1727
## 複数の置換条件を適用する
1728
1729
```
1730
$ sed -e '置換条件' -e '置換条件' ...
1731
$ sed -e 's/line/gyou/g' -e '5c aaaaaaa' test.txt
1732
```
1733
1734
## ファイルに書いた置換条件を読み込む
1735
1736
```
1737
$ sed -f スクリプトファイルPATH
1738
$ cat /root/sed_script
1739
s/line/gyou/g
1740
5c aaaaaaa
1741
$ sed -f ./sed_script test.txt
1742
```
1743
1744
## 小文字→大文字の変換をする
1745
1746
```
1747
$ sed 's/\(.*\)/\U\1/'
1748
$ sed 's/\(.*\)/\U\1/' test.txt
1749
```
1750
1751
## 大文字→小文字の変換をする
1752
1753
```
1754
$ sed 's/\(.*\)/\L\1/'
1755
$ sed 's/\(.*\)/\L\1/' test.txt
1756
```
1757
1758
## ダブルコーテーション、シングルコーテーションに囲まれた文字列を抽出する
1759
1760
~~~
1761
$ sed 's/^.*"\(.*\)".*$/\1/' # ダブルクォーテーションの場合
1762
$ sed "s/^.*'\(.*\)'.*$/\1/" # シングルクォーテーションの場合
1763
~~~
1764
1765
ちな、grepやawkでもできる
1766
1767
~~~
1768
$ grep -Po '(?<=")[^",]+(?=")' # ダブルクォーテーション
1769
$ grep -Po "(?<=')[^',]+(?=')" # シングルクォーテーション
1770
$ awk -F'['\''"]' '{print $2}'
1771
~~~
1772
1773
## ダブルクォーテーションで囲まれた文字列に対して処理を行う
1774
1775
```
1776
$ sed 's/"\([^"]*\)"/"置換後の値"/' 対象のファイルPATH
1777
$ sed 's/"\([^"]*\)"/"replace"/' test.txt
1778
```
1779
1780
## シングルクォーテーションで囲まれた文字列に対して処理を行う
1781
1782
```
1783
$ sed 's/"\([^"]*\)"/"置換後の値"/' 対象のファイルPATH
1784
$  sed "s/'\([^']*\)'/'replace'/" test.txt
1785
```
1786
1787
## メールアドレスを『○○○@●●●●●』というようにマスキング置換する
1788
1789
```
1790
$ sed "s/[^@ ]*@[^@]*\.[^@ ]*/○○○@●●●●●/g" ファイルPATH
1791
```
1792
1793
## sedで行頭の置換を指定する
1794
1795
```
1796
$ sed 's/^置換前文字列/置換後文字列/g' ファイルPATH
1797
$ sed 's/^test/aaaa/g' test.txt
1798
```
1799
1800
## sedで行頭以外の置換を指定する
1801
1802
```
1803
$ sed 's/\([^^]\)置換前文字列/置換後文字列/g' ファイルPATH
1804
$ sed 's/\([^^]\)test/aaaa/g' test.txt
1805
```
1806
1807
## 指定したディレクトリ内のファイルを再帰的に置換する
1808
1809
```
1810
$ sed 's/置換前/置換後/g' $(find 対象のフォルダPATH -type f)
1811
```
1812
1813
## 特定の文字列~文字列間を置換する
1814
1815
```
1816
$ sed '/文字列(開始)/,/文字列(終了)/s/○○○/●●●/g' 対象のファイルPATH
1817
$ sed '/line2/,/line3/s/test/aaaa/g' test.txt  # line2とline3という文字の間にあるtestをaaaaに置換
1818
$ sed '/line2/,/line3/caaaa' test.txt          # line2がある行とline3がある行、及びその間の行をaaaaに入れ替え
1819
```
1820
1821
## 日本語(マルチバイト文字)のみを置換する
1822
1823
```
1824
$ LANG=C sed 's/[\x80-\xFF]//g' ファイルPATH
1825
$ sed 's/[\x80-\xFF]/●/g' test.txt
1826
```
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
# Other Command KnowHow
1840
1841
## grep
1842
1843
### or検索
1844
1845
```
1846
$ grep -e 検索したい文字列1 -e 検索したい文字列2 検索したいテキストファイル
1847
```
1848
1849
### 大文字・小文字を区別しない
1850
1851
```
1852
$ grep -i 検索したい文字列 検索したいテキストファイル
1853
```
1854
1855
### 検索にヒットする前後の行を出力
1856
1857
検索にヒットした前の行を出力する場合
1858
1859
```
1860
$ grep 検索したい文字列 -B 出力したい行数 検索したいテキストファイル
1861
```
1862
1863
検索にヒットした後の行を出力する場合
1864
1865
```
1866
$ grep 検索したい文字列 -A 出力したい行数 検索したいテキストファイル
1867
```
1868
1869
前後の行を同時に出力したい場合は、Cオプションを付与する。
1870
1871
```
1872
$ grep 検索したい文字列 -C 出力したい行数 検索したいテキストファイル
1873
```
1874
1875
### 検索にヒットする行数を取得する
1876
1877
```
1878
$ grep -c 検索したい文字列 検索したいテキストファイル
1879
```
1880
1881
### ファイルの行番号を出力する
1882
1883
```
1884
$ grep -n 検索したい文字列 検索したいテキストファイル
1885
```
1886
1887
### 単語で検索する
1888
1889
例えばadで検索するとaddressやmadといった、別の意味を持つ単語も引っかかってしまうので
1890
1891
```
1892
$ grep -w 検索したい文字列 検索したいファイル
1893
```
1894
1895
### 複数のキーワードをパターン化したファイルを読込み、合致した行を出力する
1896
1897
```
1898
$ grep -f 検索パターンを記述したファイル 検索したいファイル
1899
```
1900
1901
### 検索キーワードを持つファイルのリストを取得する
1902
1903
```
1904
$ grep -l 検索パターンを記述したファイル 検索したいフォルダ/*
1905
```
1906
1907
## find
1908
1909
ここ読んでよく勉強すること!!1
1910
1911
俺的備忘録 〜なんかいろいろ〜 - findコマンドで覚えておきたい使い方12個
1912
http://orebibou.com/2015/03/find%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%81%A7%E8%A6%9A%E3%81%88%E3%81%A6%E3%81%8A%E3%81%8D%E3%81%9F%E3%81%84%E4%BD%BF%E3%81%84%E6%96%B912%E5%80%8B/
1913
1914
## date
1915
1916
### 今日を基準として、任意の時間経過された日時を求める
1917
1918
```
1919
$ date
1920
2015年 10月 31日 土曜日 21:29:13 JST
1921
$ date -d '-1day'
1922
2015年 10月 30日 金曜日 21:29:05 JST
1923
$ date -d '1day'
1924
2015年 11月  1日 日曜日 21:29:08 JST
1925
$ date -d '1hour'
1926
2015年 10月 31日 土曜日 22:31:33 JST
1927
$ date -d '1month'
1928
2015年 12月  1日 火曜日 21:31:38 JST
1929
$ date -d '1year'
1930
2016年 10月 31日 月曜日 21:31:42 JST
1931
```
1932
1933
### 来月の月初、3ヶ月先の月末日付を求める
1934
1935
```
1936
$ date +%Y/%m/%d
1937
2015/10/31
1938
$ date +%Y/%m/01
1939
2015/10/01
1940
$ date +%Y/%m/01 -d '+1 month'
1941
2015/12/01
1942
$ date +%Y/%m/%d -d "-1day `date +%Y%m01 -d '+4 month'`"
1943
2016/02/29
1944
```
1945
1946
### 閏年の判断
1947
1948
閏年じゃない場合はエラーになる。
1949
1950
```
1951
$ date -d 2012-02-29 +%Y
1952
2012
1953
$ date -d 2013-02-29 +%Y
1954
date: `2013-02-29' は無効な日付です
1955
```
1956
1957
-f-とすれば標準入力から複数の日付を受け付けてくれる。
1958
こうすれば閏年だけ抜き出せる。
1959
1960
```
1961
$ seq 1900 2400 | sed 's/$/-02-29/' | date -f- +%Y 2> /dev/null
1962
```
1963
1964
1965
1966
1967
## sort
1968
1969
### バイト数(KB、MB、GB etc…)を並び替えする
1970
1971
```
1972
$ du -h /home | sort -hr
1973
```
1974
1975
1976
## uniq
1977
1978
### 重複した行・重複していない行だけを表示させる
1979
1980
重複している行のみを出力させる場合は「-d」を、重複のない(ユニークな)行をのみを出力させる場合は「-u」オプションを付与する。
1981
1982
```
1983
$ cat test.txt | uniq -d
1984
$ cat test.txt | uniq -u
1985
```
1986
1987
## watch
1988
1989
### 実行コマンドがエラーになった場合、watchコマンドを終了する
1990
1991
```
1992
$ watch -e 実行コマンド
1993
```
1994
1995
なお、「-e」の場合はコマンドの実行は停止するが何かキーを押下するまで画面はそのままとなる。
1996
1997
### 実行コマンドに変化があった場合、watchコマンドを終了する
1998
1999
```
2000
$ watch -g 実行コマンド
2001
```
2002
2003
## xargs
2004
2005
### 基本的な使い方
2006
2007
以下のようにパイプでつなぐことで前のコマンド(コマンド1)で取得した値(標準出力)を利用してxargsで指定した別のコマンド(コマンド2)に引数として渡して実行させる事ができるコマンド
2008
2009
```
2010
$ コマンド1 | xargs コマンド2
2011
```
2012
2013
### 実行されるコマンド内容を表示させる
2014
2015
```
2016
$ コマンド1 | xargs -t コマンド2
2017
```
2018
2019
### コマンドライン一行に引数をいくつ渡すか指定する
2020
2021
```
2022
$ コマンド1 | xargs -n 引数の数 コマンド2
2023
```
2024
2025
### 引数の値を明示的に利用する
2026
2027
```
2028
$ コマンド1 | xargs -I{} コマンド2 {}
2029
```
2030
2031
例えば「/work」フォルダ配下にあるファイルに対し、元のファイル名の後ろに「.bk」という文字列を付け足してコピーする場合
2032
2033
```
2034
$ find /work -type f | xargs -t -I{} cp {} {}.bk
2035
```
2036
2037
### コマンドの実行をするかどうか確認する
2038
2039
```
2040
$ コマンド1 | xargs -p コマンド2
2041
```
2042
2043
### 複数プロセスを同時に実行させる
2044
2045
```
2046
$ コマンド1 | xargs -P 最大プロセス数 コマンド2
2047
```
2048
2049
### 引数の区切り文字を指定する
2050
2051
```
2052
$ コマンド1 | xargs -d区切り文字 コマンド2
2053
```
2054
2055
## eval
2056
2057
### 変数の2重展開
2058
2059
```
2060
#!/bin/sh
2061
# 変数 VARに値が入力されている
2062
VAR="test"
2063
 
2064
# 変数 EVAL_VARに、変数名である「VAR」という文字列を入力
2065
EVAL_VAR="VAR"
2066
 
2067
# 変数 EVAL_VARを呼び出す
2068
# echo $EVAL_VAR
2069
eval echo '$'$EVAL_VAL
2070
```
2071
2072
### 変数が配列の場合の2重展開
2073
2074
```
2075
#!/bin/sh
2076
# 変数「VAL」を配列として値を代入していく
2077
VAL[0]="line 1"
2078
VAL[1]="line 2"
2079
VAL[2]="line 3"
2080
 
2081
# 変数 EVAL_VALに、変数名である「VAL」という文字列を入力
2082
EVAL_VAL="VAL"
2083
 
2084
# 変数 EVAL_VALを呼び出す
2085
eval echo '$'$EVAL_VAL
2086
 
2087
# 変数 EVAL_VALを配列として全行呼び出す
2088
eval echo '${'$EVAL_VAL'[@]}'
2089
 
2090
# 変数 EVAL_VALを配列として[1]を呼び出す
2091
eval echo '${'$EVAL_VAL'[1]}'
2092
```
2093
2094
## ssh
2095
2096
### ローカルのbashの設定使ってssh接続
2097
2098
```
2099
$ ssh -t user@host 'bash --rcfile <( echo ' $(cat ~/.bashrc ~/.bash_function_etc... | base64 ) ' | base64 -d)'
2100
```
2101
2102
>環境によってはbase64に-w0オプションが必要かも?
2103
2104
### Dockerにローカルのbashrcやvimrcを使って接続
2105
2106
```
2107
$ docker run --rm -it コンテナ bash -c '/bin/bash --rcfile <(echo -e '$(cat ~/.bashrc ~/.bash_function_etc... |base64)'|base64 -d)'
2108
```
2109
2110
今のセッションの環境変数をそのまま使う場合は
2111
2112
```
2113
$ docker run --rm -it コンテナ bash -c '/bin/bash --rcfile <(echo -e '$(cat <(set) <(alias)|base64 -w0)'|base64 -d)'
2114
```
2115
2116
### ローカルにあるスクリプトを配布せずにリモート先で実行させる
2117
2118
```
2119
$ ssh リモート先のユーザ名@リモート先のホスト名(IPアドレス) 'sh ' < 実行させたいスクリプトのパス
2120
$ 
2121
```
2122
2123
### 直接リモート先のマシン上でコマンドを用いたsedを行わせる
2124
2125
```
2126
$ sed ユーザ名@リモートホスト sed s/aaaaa/`hostname`/g 対象ファイル   # ローカル側でhostnameが実行される
2127
$ sed ユーザ名@リモートホスト 'sed s/aaaaa/`hostname`/g' 対象ファイル # リモート側でhostnameが実行される
2128
```
2129
2130
### リモート側のファイルにローカルファイルの内容を追記する方法
2131
2132
```
2133
$ ssh ユーザ名@リモートホスト "cat >> /追記するリモートファイルのパス" < /追記させるローカルファイルのパス
2134
$ ssh test@192.168.0.240 "cat >> /tmp/test_remote.text" < test_local.txt
2135
```
2136
2137
ローカル側のコマンドの実行結果(ローカル側のファイルに対し、sedを実行した後の内容を追記するなど)を追記する場合は、以下のようにする。
2138
2139
```
2140
$ ローカル側で標準出力を行うコマンド | ssh ユーザ名@リモートホスト "cat >> /追記するリモートファイルのパス"
2141
$ sed 's/test/AAAA/g' text_local.txt | ssh test@192.168.0.240 "
2142
```
2143
2144
### ssh経由でディレクトリにあるファイル一覧をdiffする
2145
2146
公開鍵認証じゃないとダメかもしれない
2147
2148
```
2149
$ diff <(ssh ユーザ名@ホスト名 'find /確認するPATH -type f | sort') <(find /確認するPATH -type f | sort)
2150
$ diff <(ssh test@192.168.0.240 find /work -type f | sort) <(find /work -type f | sort)
2151
```
2152
2153
rsyncコマンドで代用できる
2154
2155
```
2156
$ rsync --checksum --dry-run -rvce "ssh -p ポート番号" ユーザ名@ホスト名:/対象ディレクトリ /対象ディレクトリ
2157
```
2158
2159
### ssh経由でファイル内容をdiffする
2160
2161
```
2162
$ diff <(ssh ユーザ名@ホスト名 'cd /確認するPATH; grep -Rn "" ./ | sort') <(cd /確認するPATH; grep -Rn "" ./ | sort)
2163
$ diff <(ssh test@192.168.0.240 'cd /work;grep -Rn "" ./ | sort') <(cd /work;grep -Rn "" ./ | sort)
2164
```
2165
2166
### ローカルでファイル・フォルダを圧縮し、リモートでアーカイブファイルとして保持させる
2167
2168
```
2169
$ tar zcvf - /アーカイブ化したいディレクトリのパス | ssh ユーザ名@リモートホスト "cat > /リモート側で作成するアーカイブ・ファイルのパス
2170
$ tar zcvf - /wort_test | ssh test@192.168.0.240 "cat > /wort_test.tar.gz"
2171
```
2172
2173
### ローカルのアーカイブファイルをリモートで解凍する
2174
2175
```
2176
$ ssh ユーザ名@リモートホスト "tar zxvf -C リモート側で展開させたいフォルダ -" < /リモート側で解凍させたいアーカイブファイルのパス
2177
$ ssh test@192.168.0.240 "tar zxvf - -C /test1234" < test.tar.gz
2178
```
2179
2180
### リモートでファイル・フォルダを圧縮し、ローカルでアーカイブファイルとして保持させる
2181
2182
```
2183
$ ssh ユーザ名@リモートホスト "tar -cf - /アーカイブ化したいディレクトリのパス" | gzip > /ローカル側で作成するアーカイブ・ファイルのパス
2184
$ ssh test@192.168.0.240 "tar -cf - /work/test" | gzip > /root/test.tar.gz
2185
```
2186
2187
### リモートのアーカイブファイルをローカルで解凍する
2188
2189
```
2190
$ ssh ユーザ名@リモートホスト "cat /ローカル側で解凍させたいアーカイブファイル" | tar zxvf - -C ローカル側で展開させたいフォルダ
2191
$ ssh test@192.168.0.240 "cat /work.tar.gz" | tar zxvf - -C /test/
2192
```
2193
2194
### 踏み台サーバ経由でログインを行う
2195
2196
```
2197
$ ssh ユーザ名@接続先のホスト名(IPアドレス) -o 'ProxyCommand ssh 踏み台サーバのユーザ@踏み台サーバのホスト名 nc %h %p'
2198
$ ssh test1@192.168.0.2-o 'ProxyCommand ssh test2@192.168.0.3 nc %h %p'
2199
```
2200
2201
### ssh接続時に直接コマンドを実行する
2202
2203
```
2204
$ ssh ユーザ名@接続先のホスト名(IPアドレス) '接続先で実行させるコマンド'
2205
```
2206
2207
### sshでsuによるユーザ切り替え時に自動でパスワード入力をさせる
2208
2209
```
2210
$ ssh login@host "su - user -c 'command' << EOF
2211
password
2212
EOF"
2213
```
2214
2215
## dd
2216
2217
### バイナリファイルでのcutコマンド相当
2218
2219
```
2220
$ cat file | dd bs=1 skip=100
2221
```
2222
2223
### バイナリファイルでのheadコマンド相当
2224
2225
固定長レコード限定
2226
2227
```
2228
$ dd bs=[レコード長] count=[抜き出したい行数] skip=[読み飛ばしたい行数] if=[入力ファイル] of=[出力ファイル]
2229
```
2230
2231
2232
2233
2234
2235
2236
# シェル芸
2237
2238
UPS友の会
2239
https://www.usptomo.com/
2240
2241
上田ブログ
2242
https://blog.ueda.asia/
2243
2244
シェル芸人達
2245
https://daichkr.hatelabo.jp/collection/960679194075891200
2246
2247
日々是迷歩
2248
http://papiro.hatenablog.jp/
2249
2250
slideshare シェル芸
2251
http://www.slideshare.net/search/slideshow?searchfrom=header&q=%E3%82%B7%E3%82%A7%E3%83%AB%E8%8A%B8&ud=&ft=&lang=&sort=
2252
2253
## Open usp Tukubai
2254
2255
基本的に、以下からダウンロードして展開すれば使える
2256
https://uec.usp-lab.com/TUKUBAI/CGI/TUKUBAI.CGI?POMPA=DOWNLOAD
2257
2258
コマンドの説明は以下
2259
https://uec.usp-lab.com/TUKUBAI_MAN/CGI/TUKUBAI_MAN.CGI?POMPA=LIST
2260
2261
Github - ShellShoccar-jpn/Parsrs
2262
https://github.com/ShellShoccar-jpn/Parsrs
2263
2264
## egzact
2265
2266
シェルの弱点を補おう!"まさに"なCLIツール、egzact
2267
http://qiita.com/greymd/items/3515869d9ed2a1a61a49 https://github.com/greymd/egzact
2268
2269
## エクシェル芸
2270
2271
俺的備忘録 〜なんかいろいろ〜 - Excelファイル(~.xls/~.xlsx)をLinuxコンソール上でCSV方式に変換する方法
2272
https://orebibou.com/2016/12/excel%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab%ef%bd%9e-xls%ef%bd%9e-xlsx%e3%82%92linux%e3%82%b3%e3%83%b3%e3%82%bd%e3%83%bc%e3%83%ab%e4%b8%8a%e3%81%a7csv%e6%96%b9%e5%bc%8f%e3%81%ab%e5%a4%89%e6%8f%9b/
2273
2274
2275
2276
2277
2278
2279
2280
2281
## ワンライナー
2282
2283
### アクセスログから、接続元IPをカウントし、合計で100回以上アクセスしているIPのみアクセスが多い順に表示する
2284
2285
```
2286
cat /var/log/httpd/access_log* |   awk '/\"GET .*\" 200 / || /\"CONNECT .*\" 200 /   {cnt[$1]+=1};END{for (key in cnt)  {if(cnt[key] > 100)  {print cnt[key],key}}}' |  sort -nr
2287
```
2288
2289
### アクセスログから、リファラなしの閲覧先ドメインをカウントし、合計で100回以上アクセスがあるURLのみアクセスが多い順に表示する
2290
2291
```
2292
cat /var/log/httpd/access_log*  |  awk -F\" '/GET .* 200 / || /CONNECT .* 200 /  {cnt[$2]+=1};END{for (key in cnt)  {if(cnt[key] > 100)  {print cnt[key],key}}}' |  sed -e 's% HTTP/...%%g' | sort -nr
2293
```
2294
2295
2296
2297
# シェル芸化
2298
2299
シェル芸の定義はこうだ。
2300
2301
>マウスも使わず、ソースコードも残さず、GUI ツールを立ち上げる間もなく、あらゆる調査・計算・テキスト処理を CLI 端末へのコマンド入力一撃で終わらすこと。
2302
2303
では、やってみましょう。
2304
全ての手続きを1行に。
2305
2306
## 1行化の基本
2307
2308
```
2309
$ echo "a"
2310
$ echo "b"
2311
$ echo "c"
2312
```
2313
2314
;で区切ることで1行化できる。
2315
2316
```
2317
$ echo "a";echo "b";echo "c"
2318
```
2319
2320
### && の利用
2321
2322
```
2323
$ hoge ; date
2324
-bash: hoge: コマンドが見つかりません
2325
2016年  6月 19日 日曜日 11:01:32 JST
2326
```
2327
2328
&&で区切ると、hogeが正常終了した場合のみ、後続のコマンドを実行する。
2329
正常終了とは、コマンドの復帰値($?)が0の場合を指す。
2330
2331
```
2332
$ hoge && date
2333
-bash: hoge: コマンドが見つかりません
2334
```
2335
2336
これは以下と等価である。
2337
2338
```
2339
#!/bin/bash
2340
2341
hoge
2342
2343
RET=$?
2344
if [ $RET -eq "0" ]
2345
then
2346
  date
2347
else
2348
  exit $RET
2349
fi
2350
```
2351
2352
### || の利用
2353
2354
```
2355
$ hoge ; date
2356
-bash: hoge: コマンドが見つかりません
2357
2016年  6月 19日 日曜日 11:01:32 JST
2358
```
2359
2360
||で区切ると、hogeが異常終了した場合のみ、後続のコマンドを実行する。
2361
異常終了とは、コマンドの復帰値($?)が0以外の場合を指す。
2362
2363
```
2364
$ hoge || date
2365
-bash: hoge: コマンドが見つかりません
2366
2016年  6月 19日 日曜日 11:17:16 JST
2367
```
2368
2369
これは以下と等価である。
2370
2371
```
2372
#!/bin/bash
2373
2374
hoge
2375
2376
RET=$?
2377
if [ $RET -nq "0" ]
2378
then
2379
  date
2380
fi
2381
```
2382
2383
## if文の1行化
2384
2385
これが
2386
2387
```
2388
RET="0"
2389
if [ $RET -eq "0" ]
2390
then
2391
  echo "true"
2392
else
2393
  echo "false"
2394
fi
2395
```
2396
2397
こうじゃ!
2398
2399
```
2400
RET="0";if [ $RET -eq "0" ]; then echo "true"; else echo "false"; fi
2401
```
2402
RETの値を変えれば、分岐されていることを確認できる。
2403
2404
### && , || の利用
2405
2406
以下のように、&& や || を利用することもできます。
2407
2408
```
2409
$ RET="0";[ $RET -eq "1" ] || echo "It work"
2410
It work
2411
$ RET="0";[ $RET -eq "0" ] && echo "It work"
2412
It work
2413
```
2414
2415
## for文の1行化
2416
2417
これが
2418
2419
```
2420
for I in {1..10}
2421
do
2422
  echo $I
2423
done
2424
```
2425
2426
こうじゃ!
2427
2428
```
2429
for I in {1..10} ; do echo $I ; done
2430
```
2431
2432
これでもいけるらしい
2433
2434
~~~
2435
for I in {1..5};{ echo $I;}
2436
for((;;)){ echo 1;sleep 1; } # 無限ループ
2437
~~~
2438
2439
## while文の1行化
2440
2441
これが
2442
2443
```
2444
I=1
2445
while [ $I -le 10 ]
2446
do
2447
  echo $I
2448
  I=$((I+1))
2449
done
2450
```
2451
2452
こうじゃ!
2453
2454
```
2455
I=1; while [ $I -le 10 ]; do echo $I;I=$((I+1)); done
2456
```
2457
2458
## case文の1行化
2459
2460
これが
2461
2462
```
2463
case $I in
2464
  a ) echo "a1"
2465
      echo "a2";;
2466
  b ) echo "b1"
2467
      echo "b2";;
2468
  * ) echo "default";;
2469
esac
2470
```
2471
2472
こうじゃ!
2473
2474
```
2475
case $I in a ) echo "a1"; echo "a2";; b ) echo "b1"; echo "b2";; * ) echo "default";; esac
2476
```
2477
2478
## 関数の1行化
2479
2480
これが
2481
2482
```
2483
funcSample(){
2484
echo "It work"
2485
}
2486
funcSample
2487
```
2488
2489
こうじゃ!
2490
2491
```
2492
funcSample(){ echo "It work"; };funcSample
2493
```
2494
2495
## シェル芸ブブ化
2496
2497
シェルスクリプトをシェル芸化(ワンライナー)するスクリプト
2498
2499
[[シェル芸ブブ化]]
2500
2501
2502
2503
# Test/Lint
2504
2505
POSTD - Bashアプリケーションをテストする
2506
http://postd.cc/bash%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%82%92%e3%83%86%e3%82%b9%e3%83%88%e3%81%99%e3%82%8b/
2507
2508
ShellCheck - ShellCheck, a static analysis tool for shell scripts
2509
http://www.shellcheck.net/ https://github.com/koalaman/shellcheck
2510
2511
Batsを使ったシェルスクリプトのテスト
2512
https://rcmdnk.com/blog/2019/10/11/computer-github-bash/
2513
2514
pre-commitでShellCheckを使う
2515
https://rcmdnk.com/blog/2023/01/09/computer-shell-python/
2516
2517
AWK commands equivalent to SQL query – Data manipulation
2518
https://subhadip.ca/unixlinux/awk-commands-equivalent-to-sql-query-data-manipulation/4/
2519
2520
>SQLのINNER JOINやLEFT OUTER JOINをawkで実現
2521
2522
シェルスクリプトにxUnitを使ってみる
2523
https://qiita.com/filunK/items/aa067383aaa317594d17
2524
2525
2526
2527
2528
2529
2530
# Memo
2531
2532
## sed
2533
2534
Qiita - sedのパターンスペース・ホールドスペースの動作を図で学ぶ
2535
http://qiita.com/gin_135/items/773fec1343a69c9f90d6
2536
2537
俺的備忘録 〜なんかいろいろ〜 - sedのデバッグやHTML化ができる『sedsed』
2538
https://orebibou.com/2016/11/sed%E3%81%AE%E3%83%87%E3%83%90%E3%83%83%E3%82%B0%E3%82%84html%E5%8C%96%E3%81%8C%E3%81%A7%E3%81%8D%E3%82%8B%E3%80%8Esedsed%E3%80%8F/
2539
2540
2541
## Misc
2542
2543
Defensive BASH Programming
2544
http://www.kfirlavi.com/blog/2012/11/14/defensive-bash-programming
2545
2546
Perl1行野郎
2547
http://web.archive.org/web/20020203071153/www13.cds.ne.jp/~ohsaru/perl/oneline.html
2548
2549
WICKED COOL SHELL SCRIPTS
2550
http://www.intuitive.com/wicked/wicked-cool-shell-script-library.shtml
2551
2552
Bourne Shell自習テキスト
2553
http://lagendra.s.kanazawa-u.ac.jp/ogurisu/manuals/sh-text/sh/
2554
2555
Qiita - richmikan@github
2556
http://qiita.com/richmikan@github
2557
2558
glot.io - 様々な言語のコードスニペットが共有できるサイト
2559
https://glot.io/
2560
2561
CMD-CHALLENGE - なんかCTFのようなシェル芸勉強サイト!!
2562
https://cmdchallenge.com/
2563
2564
Thanks Driven Life - shellcheck を Docker で実行する
2565
http://gongo.hatenablog.com/entry/2017/03/28/223757
2566
2567
Command Challenge - ★CTFみたい!!★
2568
https://cmdchallenge.com/
2569
2570
explainshell.com - 難しめのシェル芸の解析
2571
https://explainshell.com/
2572
2573
Yakst - 私が他人のシェルスクリプトから学んだこと
2574
https://yakst.com/ja/posts/31
2575
2576
Qiita - bashの組込みコマンド自作によるスクリプトの高速化
2577
https://qiita.com/satoru_takeuchi/items/7d424fa5ef1e33ace4df
2578
2579
Qiita - ネットワーク越しでパイプしたり、あらゆるデバイス間でデータ転送したい!
2580
https://qiita.com/nwtgck/items/78309fc529da7776cba0
2581
2582
ble - Bashを動的にシンタックスハイライトする
2583
https://github.com/akinomyoga/ble.sh
2584
2585
if 文と test コマンド | UNIX & Linux コマンド・シェルスクリプト リファレンス
2586
https://shellscript.sunone.me/if_and_test.html
2587
2588
bashスクリプティング研修の資料を公開します
2589
https://www.m3tech.blog/entry/2018/08/21/bash-scripting
2590
2591
set -eのもとで特定のコマンドの終了ステータスを変数に入れるシェルスクリプトのスニペット
2592
https://gfx.hatenablog.com/entry/2021/12/15/153937
2593
2594
bash スクリプトの実行中上書き動作について
2595
https://zenn.dev/mattn/articles/5af86b61004bdc
2596
2597
pure sh bible (📖 A collection of pure POSIX sh alternatives to external processes). - コマンドを使わずpure bashであれやこれやする一覧
2598
https://github.com/dylanaraps/pure-bash-bible#get-the-directory-name-of-a-file-path
2599
2600
shell.how - インタラクティブにコマンド内容やオプションを解説してくれる
2601
https://www.shell.how/
2602
2603
なぜシェルスクリプトで高度なデータ管理にSQLiteを使うべきなのか? ~ UNIX/POSIXコマンドの欠点をSQLで解決する
2604
https://qiita.com/ko1nksm/items/33ab7ced0f9f8acdff28
2605
2606
onceupon/Bash-Oneliner
2607
https://github.com/onceupon/Bash-Oneliner
2608
2609
シェルスクリプトの表示を豊かにするgumの使い方
2610
https://dev.classmethod.jp/articles/eetann-gum-suburi/
2611
2612
GoogleのShell Style Guideの邦訳
2613
https://qiita.com/yabeenico/items/72b904d4bb0b6d732a86
2614
2615
シェルスクリプトでgetoptsで解析する引数をポジショナルな引数と混合して使えるようにする
2616
https://rcmdnk.com/blog/2023/11/01/computer-bash/
2617
2618
Bashの文字列で特殊文字を使う方法
2619
https://azisava.sakura.ne.jp/programming/0010.html
2620
2621
シェルスクリプトで関数をそのままサブコマンドとして使う
2622
https://rcmdnk.com/blog/2024/09/08/computer-bash/#google_vignette
2623
2624
改めて整理する、コンソール・ターミナル・仮想コンソール・端末エミュレータ・擬似端末
2625
https://www.kanzennirikaisita.com/posts/what-is-console-terminal-etc
2626
2627
ASCII control characters in my terminal (端末の ASCII 制御文字)
2628
https://jvns.ca/blog/2024/10/31/ascii-control-characters/
2629
2630
yassinebenaid/bunster(シェルスクリプトをバイナリ形式に変換するツール:Go実装にトランスパイル)
2631
https://github.com/yassinebenaid/bunster
2632
2633
2634
2635
# 謝辞
2636
2637
以下のサイトを中心に参考にさせて頂いております。
2638
ありがとうございます。
2639
2640
日々是迷歩
2641
http://papiro.hatenablog.jp/
2642
2643
俺的備忘録 〜なんかいろいろ〜
2644
http://orebibou.com/
2645
2646
Qiita - 実用 awk ワンライナー
2647
http://qiita.com/b4b4r07/items/45d34a434f05aa896d69
2648
2649
SlideShare - 検索:シェル芸
2650
http://www.slideshare.net/search/slideshow?searchfrom=header&q=%E3%82%B7%E3%82%A7%E3%83%AB%E8%8A%B8