Skip to content

Instantly share code, notes, and snippets.

@LeeDDHH
Last active December 30, 2023 10:29
Show Gist options
  • Save LeeDDHH/50179e3d99d1328775f6d931d48259ba to your computer and use it in GitHub Desktop.
Save LeeDDHH/50179e3d99d1328775f6d931d48259ba to your computer and use it in GitHub Desktop.
shell scriptに関するgistのまとめ(0_shell_script_link_index)

オプションを含めたコマンド自体が、実行可能かどうかを判定する

$ コマンド オプション | echo $?
  • 戻り値
    • 1 : 実行可能
    • 0 : 実行不可能

参考

【linux】実行したコマンドの戻り値を取得する


コマンドの存在チェック

$ which コマンド
or
$ type コマンド
or
$ where コマンド
or
$ command -v コマンド

参考

Bashでコマンドの存在チェック - LESS IS MORE

Check If a Command/Executable Exists from Shell Script ❚ A Scripter's Notes

How can I check if a program exists from a Bash script? - Stack Overflow


ターミナルでビープ音を鳴らす

$ printf "\a"
or
$ osascript -e 'beep'
or
$ echo -e "\a"
or
tput bel

shellスクリプトのダブルクォーテーション内でコマンドの実行結果を埋め込みたい

open "https://scrapbox.io/camomilecafe/$(date +%Y-%m-%d)"

参考

Bash: Mac OS X10.6でハードウェアのビープ音を鳴らす方法 | Code Hero


現在の端末(ターミナル)のシェルを確認する

$ echo $0

数値の計算をするときのコマンド

  • bc
    • 数字や記号をもとに、計算を処理するコマンド
$ echo 1+1 | bc
2
$ echo '2+2' | bc
4
  • bc 以外の計算方法
$ echo '1+1' | sed 's/.*/echo $((&))/' | zsh
$ echo '1+1' | sed 's/^/puts /' | ruby
$ echo '1+1' | sed 's/./& /g' | xargs expr
$ echo '1+1' | perl -ple '$_=eval'
$ echo '1+1' | bash -c 'echo $(($(cat)))'
$ echo '1+1' | grep -o 1 | wc -l
$ echo '1+1' | perl -e '{printf "%d\n", eval(<STDIN>)}'

結果表示を横に並べる

$ なんかの処理 | xargs

ファイルの種類を特定する

$ file 確認したいファイルパス

time コマンドの表示形式を bash での形式にする

  • zshでは、TIMEFMTという環境変数を使って time の結果を表示する
$ vi ~/.zshrc

# 以下を記載する
TIMEFMT=$'\n\n========================\nProgram : %J\nCPU     : %P\nuser    : %*Us\nsystem  : %*Ss\ntotal   : %*Es\n========================\n'

$ source ~/.zshrc

参考


ページキャッシュ

  • 一度読み書きしたファイルの内容はメモリの上に一時保存され、次に読み書きするときに高速に処理できるようにする仕組み
    • 単にキャッシュとも呼ぶ

並行で走らせられる処理の数を調べる

$ nproc
並行で走らせられる処理の数

コマンド置換

  • $(コマンド)
    • コマンドの出力を、引数の文字列に変換できる

shについて

  • 世界で最初に実装されたシェル
  • どの環境でも共通して動くPOSIXシェルを指すこともある
  • bash以外でも動作するシェルスクリプトを書く場合、shで動くものを書くことが条件となる

使っているshellを確認する

  • echo $SHELL

使用可能なシェルを確認する

  • cat /etc/shells

参考


shellの$RANDOM変数を使って、1~1000のファイルにランダムな値を入れる

  • $RANDOM
    • 0~32767が対象
$ seq 1000 | sed 's/^/echo $RANDOM > /' | zsh

ライフバッファ

  • バッファ
    • 何かデータを受け渡しするときに、ある程度の量のデータをためて、あるタイミングで一度に渡す仕組み

seqの引数

開始の数と間隔を指定する

# 1~5までの数を出力する
$ seq 5

# 5~15までの数を出力する
$ seq 5 15

# 5~15までの数を2ずつ増やしながら出力する
$ seq 5 2 15

数字を逆順で出力する

# 5から1までの数を出力する
$ seq 5 -1 1

# 1~5までの数を出力し、逆順に並べ替える
$ seq 5 | tac

書式を指定して出力する

  • -f オプションで数字の形式を指定できる
  • 使える書式
    • %e
      • 指数形式
    • %E
      • 大文字で出力する
    • %f
      • 小数点形式
      • 小数点以下2桁の表示までする → %0.2f
    • %g
      • 一般的な数値の形式
      • 桁数のしていができる
      • 4桁幅 → %4g
      • 4桁で前方を0で埋める → %04g
$ seq -f "img%04g.img" 5
img0001.img
img0002.img
img0003.img
img0004.img
img0005.img

参考


左へ余白を入れてprintfで出力する

  • %数字s
    • 文字列の指定した数になるように左へ余白が入る
$ printf "%5s\n" xx yyy zzzz
   xx
  yyy
 zzzz

シェル変数を表示する

  • シェル変数
    • 動作中のシェル内でのみ有効な変数
  • set

参考


環境変数を表示する

  • 環境変数
    • シェルとシェル上で実行されるアプリケーションで有効な変数
  • printenv

参考


ヒアストリングとヒアドキュメント

ヒアストリング

  • 変数を擬似的にファイルのように取り扱える
    • cat <<< $SHELL
    • cat 時に現在使っているshellを書き込む

ヒアドキュメント

  • 複数行をコマンドに渡したり、出力したりできる
    • cat << EOT
    • 複数行を書き込んで、終了する際に EOT と入力

参考


シェル芸

# 以下のような表現
    x
   x
  x
 x
x

# tac catの反対で入力した行を逆順で出力する
# 出力時に改行を入れる場合はprint
# 出力時に改行を入れない場合はprintf
$ seq 5 | awk '{for(i=1;i<$1;i++){printf " "};print "x"}' | tac

$ seq 5 -1 1 | awk '{for(i=1;i<$1;i++){printf " "};print "x"}'

$ seq 5 -1 1 | awk '{printf "%*s\n", $1, "x"}'

文字の置換

最初に見つかった検索対象の文字列を置換する

$ sed 's/探す文字列/置換する文字列/' ファイル名

該当する検索対象の文字列すべてを置換する

$ sed 's/探す文字列/置換する文字列/g' ファイル名

該当する検索対象の文字列すべてを、正規表現で置換する

  • -E オプションは、「拡張正規表現を使う」という宣言
$ sed -E 's/正規表現式/置換する文字列/g' ファイル名

検索対象の文字列を再利用する

  • 文字列の再利用機能
    • 広報参照
# %は検索対象の文字列を再利用するときに使う
$ sed 's/探す文字列/&/g' ファイル名
# ()で囲むと順番に再利用可能な番号(\数字)が与えられる
# \1,\2…
$ sed -E 's/(文字列1)(文字列2)/\2\1/g' ファイル名

[以前の文字列]/ を削除する

  • 前提
    • 区切り文字を@にする
      • s@置換前@置換後@g
    • | は拡張正規表現におけるOR記号
    • [] は正規表現で使う記号なのでエスケープ
# s@.*\[|\]|/@@g
# [以前の文字列 → .*\[
# \ → \]
# / → /
# s/:/ / → 最初に現れた:を空欄する
$ sed -E 's@.*\[|\]|/@@g;s/:/ /' access.log

一定期間の間のログを抽出する

  • -n オプションで特定のパターンの行を抽出する
    • sed -n '/正規表現/p'
      • 正規表現にマッチした行を抽出する
    • sed -n '/正規表現1/,/正規表現2/p'
      • 正規表現1でマッチした行から正規表現2にマッチする行までを抽出する
# 2016/12/24 21時から2016/12/25 3時までのログを抽出
$ cat log_range.log| sed -n '/24\/Dec\/2016 21:..:../,/25\/Dec\/2016 03:..:../p'

Mac環境で置換後に改行を入れる

  • 前提
    • ファイルに以下の内容が入っている
      • aaa
        bbb
        ccc
        
    • 各行の後ろに改行コードを入れる
# 方法1
$ cat xxx.txt | sed -E $'s/$/\\\n/'
# 方法2
# trコマンドで文字の置き換えをする
$ cat xxx.txt | sed -E 's/$/|/' | tr '|' '\n'
# 方法3
$ cat xxx.txt | sed -E 's/$/\'$'\n/'

パイプで受けたデータの先頭のN文字を削除する

cat dummy.txt | sed -e "s/^.\{2\}//"

参考

正規表現

メタ文字 意味
^ 先頭が〇〇で始まる
$ 末尾が〇〇で終わる
. 何か1文字
* 0個以上の文字
[] []の中の字のどれか1字
[^] []の中の字以外
\ メタ文字を文字列として扱う

grepによる検索

拡張正規表現

  • -E オプションは、「拡張正規表現を使う」という宣言
$ seq 100 | grep -E "^(11|22)" | xargs
11 22

マッチしたものだけを表示する

  • -o オプションは、その行でマッチしたもののみを表示する
$ echo 中村 山田 田代 上田 | grep -o '[^ ]田'
山田
上田

ディレクトリ内のファイルを再帰的に読み込む

grep -R

検索結果を表示せずにファイル名だけを出力する

grep -l

rg(ripgrep)による検索

  • デフォルトで並列処理をする
    • カレントディレクトリ内のファイルすべてを検索する
# 基本的な使い方
$ rg '正規表現'

# 検索結果を表示せずにファイル名だけを出力する
$ rg -l '正規表現'

参考

awk

  • grep にプログラム機能をつけたようなコマンド
  • awk はプログラミング言語
  • さまざまな亜種がある
    • GNU Awk(gawk
  • 処理を書くときの名称
    • 条件 → パターン
    • 処理 → アクション
    • パターンとアクションの組 → ルール
  • 読み込んだ行の各列の文字列、数字を指定する方法
    • $1、$2、$3…
  • データのn列目を第nフィールドと呼ぶ
  • BEGIN、ENDパターン
    • 事前、事後に awk でやってほしい処理を書くとき
    • BEGIN
      • awk が1行目の処理を始まる前
    • END
      • awk が最終行の処理を終えた後
  • C言語と同じく非ゼロの値は真を指す
  • 変数の宣言をせず、いきなりつかうこともできる
    • 宣言されていない変数は実行直前に初期化される
  • awk に慣れて、 awk でプログラムするのが面倒になったらショートカットのようにコマンドを覚える
  • awk の変数
    • NR(Number of Record)行数
      • 行番号
    • NF(Number of Fields)列数
      • 列数
  • $数字
    • $0
      • 行全体
    • $1$2、…
      • 各列目の値
  • $数字~/正規表現/
    • 指定した列で正規表現にマッチしたものを対象とする
  • $数字!~/正規表現/
    • 指定した列で正規表現にマッチしなかったものを対象とする
  • -F識別子
    • 列の区切り文字を空白やタブから変更する

# BEGINで0の変数を宣言
# 奇数、偶数を判定し、出力する
# アクションごとに値を合算する
# アクションの終了時に合算した値を出力
$ seq 5 | awk 'BEGIN{a=0}$1%2==0{print $1,"偶数"}$1%2{print $1,"奇数"}{a+=$1}END{print "合計",a}'
1 奇数
2 偶数
3 奇数
4 偶数
5 奇数
合計 15

正規表現

  • awk '/正規表現/'

AWKとawkについて

  • awk の亜種
    • gawk、nawk、mawkなど
  • Linuxの場合、単に awk とコマンドを実行したとき、たいていオリジナルのawkではないawkの亜種のどれかが呼ばれる
    • なので、実行前に環境内の awk の種類を確認する必要がある
    • awk --version

awkで集計する

  • 行数が多く、出現する文字列の種類が少ない場合に使える
  • 連想配列
    • a[数字] = 値
    • a["文字列"] = 値
# a[$1]で入力された1列目をキーに指定できる
# a["奇数"]、a["偶数"]で入る
# a[$1]++によって、値が0に初期化され、++で1にできる
# 行が入力されるごとに1増える
# for(キー in 連想配列)でfor文が使える
$ seq 5 | awk '{print $1%2 ? "奇数":"偶数"}' | awk '{a[$1]++}END{for(k in a)print k,a[k]}'
奇数 3
偶数 2

1列目の文字列でマッチした行だけを抽出する

$ cat ファイルパス | awk '$1=="検索したい文字列"'

数字の集計

$ awk '{s += $1} END{print s}' < 数字が入っているファイルのパス

記録されたログの時間帯を午前/午後で分けて出力する

  • 前提
    • 時間表示は : で区切られている
    • 各行で区切られる列数は必ずしも一致するとは限らない
$ awk -F: '{print $(NF-2)}' access.log | awk '$1<"12"{print "午前"} $1>"12"{print "午後"}' | sort | uniq -c | awk '{print $2,$1}'
午前 3
午後 2

sortとuniqによる集計

  • データの中に何がいくつ存在するか集計するときに使える
  • sort
    • 並び替える
  • uniq
    • 重複する行を消す
    • -c オプションをつけ、消したい行を連続して並べることで、数える
      • 同じ行がいくつ連続で存在しているのかをカウント

奇数、偶数を判定し、並び替える

# sortは辞書順(abc順)に並べる
$ seq 5 | awk '{print $1%2 ? "奇数":"偶数"}' | sort
偶数
偶数
奇数
奇数
奇数

奇数、偶数を判定し、それぞれの数をカウントする

$ seq 5 | awk '{print $1%2 ? "奇数":"偶数"}' | sort | uniq -c
   2 偶数
   3 奇数
$ seq 5 | awk '{print $1%2 ? "奇数":"偶数"}' | sort | uniq -c | awk '{print $2,$1}'
偶数 2
奇数 3

奇数、偶数を判定し、それぞれのカウントした数を数字の順でソートする

  • -k 数字 のオプジョンで何列目を並べ替える基準にするのかを決める
    • -k 2 は2列目を基準に並べ替える
    • -k 2,2 は2列目から2列目を基準に並べ替える
    • -k 2,2n は2列目から2列目を基準に数値として並べ替える
    • -k 2n は2列目を数値として並べ替える
    • -k 2nr は2列目を数値として逆順に並べ替える
  • -n
# 2列目から2列目を基準に数値として並べ替える
$ seq 19 | awk '{print $1%2 ? "奇数":"偶数"}' | sort | uniq -c | awk '{print $2,$1}' | sort -k2,2n
偶数 9
奇数 10
# 2列目から2列目を基準に辞書順で並べ替える
# ただし、左から文字列が比較される
# 10と9の比較では、最初に1と9のひかくになるため、10が先になる
$ seq 19 | awk '{print $1%2 ? "奇数":"偶数"}' | sort | uniq -c | awk '{print $2,$1}' | sort -k2,2
奇数 10
偶数 9

xargsによる一括処理

  • コマンドに引数を渡して実行してもらう役割
  • xargs は渡された値を入力ではなく引数として扱う
  • -n数字
    • 数字分の個数の入力値をコマンドに渡す
    • -n2 なら入力値を2つずつにして渡す
  • I識別子となる記号
    • 識別子となる記号で xargs が受け取った文字列を扱う
  • -P数字
    • 並列実行をする
    • 数字の数だけ並列実行をする

# 1,2,3,4という名前のディレクトリを作成する
# mkdir 1 2 3 4
$ seq 4 | xargs mkdir

# ディレクトリ1,2,3,4を消去
# rmdir 1 2 3 4
$ seq 4 | xargs rmdir

# 1,3というディレクトリを事前に作って、それぞれを2,4という名前にする
# mv 1 2
# mv 3 4
$ mkdir 1 3
$ seq 4 | xargs -n2 mv

# dir_という接頭詞をつけた連番のディレクトリを作成する
$ seq 4 | xargs -I@ mkdir dir_@

xargsのコマンドなしの用途

  • コマンドを指定されなかったときは echo を実行する
$ seq 10 | xargs
1 2 3 4 5 6 7 8 9 10
  • 決められた列数で横に並べる
$ seq 10 | xargs -n5
1 2 3 4 5
6 7 8 9 10
  • 指定する文字列によって、オプションとして解釈される場合がある
    • 実行する処理の安全性の点ではあまり良いことではない
# -eをechoのオプションとして 解釈する
$ awk 'BEGIN{print "-e 1 2 3"}' | xargs
1 2 3

xargsで、引数を任意の場所に埋め込む

cat list.txt | xargs -IXXXX echo aXXXXa
  • -I
    • コマンドの途中に挟むように入れたいときに使う
  • -IXXXX とすると、 XXXX が引数に置き換わる

参考

bashによるメタプログラミング

  • 処理の中で () を書くことで前後の値とくっつかないようにする

# 奇数の場合はodd_、偶数の場合はeven_をつけてディレクトリを作る
$ seq 4 | awk '{print "mkdir " ($1%2 ? "odd_" : "even_") $1}' | zsh
  • シェルスクリプトとして保存後、シェルコマンドで呼ぶ
$ seq 4 | awk '{print "mkdir " ($1%2 ? "odd_" : "even_") $1}' > a
$ zsh ./a

シバン(shebang)

  • シェルスクリプトのいち行目に書く
    • #!すプリプと言語のコマンドの絶対パス
    • #!/bin/zsh
    • OSが絶対パスにあるコマンドを呼び出し、そのコマンドが2行目以降を読み込むように手配してくれる
  • 実行できるように chmod +x でパーミッションを与える
    • ex) chmod +x ./a

ファイルを操作する

findとgrepで出力したいパスを絞り込む

$ find 探したいディレクトリのパス | grep 探したいファイルの文字列

ファイル内から特定の文字列を検索する

$ grep '探したい文字列' ファイルパス
# catの分だけマシーンの負担は増えるが、少ない検索料であれば使える
$ cat ファイルパス | grep '探したい文字列'
# grep以外の方法
# sed -n で各行を自動的に出力しない
# sedの正規表現時に/pをつけることで、正規表現にマッチする行だけ出力する
$ cat ファイルパス | sed -n '/正規表現/p'
$ cat ファイルパス | awk '正規表現'
$ cat ファイルパス | perl -ne '正規表現 and print'

画像ファイルを一括変換

  • imagemagickconvert を使う
$ convert 画像ファイルパス 別の画像拡張子を使ったファイルパス

png形式のファイルをjpg形式に変換する

$ ls *.png | sed 's/\.png$//' | xargs -I@ convert @.png @.jpg
# xargs -Pでコマンドを並列実行をする
# 「-P数字」で並列実行する数を決める
$ ls *.png | sed 's/\.png$//' | xargs -P2 -I@ convert @.png @.jpg
# nprocで並行で走らせられる処理の数だけ並列実行する
$ ls *.png | sed 's/\.png$//' | xargs -P$(nproc) -I@ convert @.png @.jpg
# imagemagickのmogrifyを使った方法
# xargsなしでファイルを一括処理できる
$ mogrify -format jpg *.png
# parallelコマンドを使った方法
# parallel 処理 ::: 操作対象1 操作対象2 …
# 操作対象に対して処理を並列に実行する
# {} ファイル名
# {.} ファイル名から拡張子を除去したもの
$ parallel 'convert {} {.}.jpg ::: *.png'

ファイルの一括変更

# 1~1000という名前のファイルがあるとする
$ seq 1000 | xargs -P2 touch

# 各ファイルの先頭に0をつけて4桁を揃える
# ls -Uで、ソートを省略してファイル一覧を出力する
# renameコマンドはsedのように正規表現(Perlの正規表現の書き方)でファイル名を変更する
# s/^/0000/はファイル名の先頭に一律で4つの0を付与する
# s/0*([0-9]{4})/$1/は余計な先頭の0を除去して、後ろ4桁の数字を抽出する

$ ls -U | xargs -P2 rename 's/^/0000/;s/0*([0-9]{4})/$1/'

現在のディレクトリにある全ファイルを削除する

# 並列処理で削除する
$ ls -U | xargs -P2 rm

# 対象のディレクトリごと削除する
$ rm -rf 削除するディレクトリ

特定のファイルの削除

# ディレクトリ内のファイルを再帰的に読み込んで、中身が10と書かれたファイルのみを削除する
$ grep -l '^10$' -R . | xargs rm

全ディレクトリを別々のzipに圧縮する

以下のような構成があるとする

.
├── a
├── b
└── c

この各ディレクトリをzipでまとめる

.
├── a
├── a.zip
├── b
├── b.zip
├── c
└── c.zip

コマンド

find . \! -name '*.zip' \! -name '.' -type d -exec zip -r {}.zip {} \;

参考

現在のディレクトリ配下のディレクトリサイズを計算し、降順に並べる

  • du
    • ストレージのディレクトリの容量を調べる
    • オプションを指定しないと、ディレクトリ配下のファイル単位までサイズを調べる
  • オプション
    • d
      • 表示する階層を指定する
    • h
      • KBやMBなど人が分かり易い単位で表示する
# 現在の階層のディスク使用量を確認する
du -h -d 1

# ディスク使用量を昇順で並び変える
du -h -d 1 | sort -h

# ディスク使用量を降順で並び変える
du -h -d 1 | sort -h -r

参考

gitのリモートリポジトリのアドレスに変換して取得する

git ls-remote --get-url | sed -e "s/^git@github.com:\([a-zA-Z0-9-]*\)\/\([a-zA-Z0-9-]*\)\.git/https:\/\/github.com\/\1\/\2/g"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment