似てるけど違う挙動: grep
grep コマンドと除外
行頭が数字で始まらない行を表示するが,「空行」についての挙動が異なるコマンド
## 空行も出力
grep -v '^[0-9]' file
## 空行は出力されない
grep '^[^0-9]' file正規表現の観点
| パターン | 要求 |
|---|---|
^[0-9] |
行頭に数字が ある |
^[^0-9] |
行頭に数字以外の文字が 1文字ある |
空行は正規表現では ^$ なので,「行頭に数字以外の文字が 1文字ある」という条件には引っかからないため,挙動差が生まれます.
実務では,「数字で始まらない行 = 空行も該当する」というケースのほうが多いと思うので,基本は grep -v '^[0-9]' file のほうが良いと思われる.
例 1
$ cat file1.txt
123abc
abc123
# 空行あり
9xyzのとき,
$ grep -v '^[0-9]' file1.txt
abc123
# 空行あり一方,
$ grep '^[^0-9]' file1.txt
abc123
# 空行あり13日の金曜日を見つける
2020年から2025年までの期間で「13日の金曜日」を見つけるコマンド
printf "%s\n" {2020..2025}/{1..12}/13 | xargs -I{} date -d {} | grep Fri基礎に忠実に実行するならば
$ for i in $(seq 2020 2025) ;do for j in $(seq 1 12) ;do date -d $i/$j/13;done;done| grep Fri
Fri Mar 13 12:00:00 AM JST 2020
Fri Nov 13 12:00:00 AM JST 2020
Fri Aug 13 12:00:00 AM JST 2021
Fri May 13 12:00:00 AM JST 2022
Fri Jan 13 12:00:00 AM JST 2023
Fri Oct 13 12:00:00 AM JST 2023
Fri Sep 13 12:00:00 AM JST 2024
Fri Dec 13 12:00:00 AM JST 2024
Fri Jun 13 12:00:00 AM JST 2025となりますが,より短く,二重ループをわかりやすくしたのが上記となります.
1. ブレース展開(Brace Expansion)
{2020..2025}/{1..12}/13{2020..2025}は2020,2021,2022,2023,2024,2025に展開{1..12}は1,2,3, …,12に展開- 結果として
2020/1/13,2020/2/13, …,2025/12/13のような日付文字列が生成
- ブレース展開:
{...}で指定されたパターンを展開(シェルが実行前に展開) - ワイルドカード:
*,?,[...]などでファイル名のパターンマッチング(グロビング)
2. printf で改行区切りで出力
printf "%s\n" {展開結果}- 各日付文字列を改行(
\n)付きで出力 - 例:
2020/1/13,2020/2/13, …が各行に出力
3. xargs でdate コマンドに渡す
xargs -I{} date -d {}-I{}は置換文字列を定義({}が標準入力の各行に置き換わります)date -dは日付文字列をパースして標準形式で出力- 例:
Fri Jan 13 00:00:00 JST 2023
4. grep で金曜日のみを抽出
grep Fri- 出力から「Fri」(金曜日)を含む行のみを抽出
loginctlセッション情報を整形して表示
Linuxのログインセッション情報を詳細に整形して表示するコマンド
printf "%-8s %-8s %-18s %-8s %-8s %-6s %-10s %-10s\n" "SESSION" "UID" "USER" "TYPE" "SERVICE" "REMOTE" "SEAT" "TTY"; loginctl list-sessions --no-legend | while read -r sid uid user seat tty; do eval "$(loginctl show-session "$sid" -p Type -p Service -p Remote -p Seat -p TTY --value | awk '{print "v[++i]=\""$0"\""}')"; printf "%-8s %-8s %-18s %-8s %-8s %-6s %-10s %-10s\n" "$sid" "$uid" "$user" "${v[1]:--}" "${v[2]:--}" "${v[3]:--}" "${v[4]:--}" "${v[5]:--}"; unset v i; done処理の流れ
- ヘッダー行を表示
loginctl list-sessionsでセッション一覧を取得- 各セッションについて:
- 基本情報(SESSION, UID, USER)を取得
loginctl show-sessionで詳細情報を取得awkとevalで情報を配列に格納- 整形して1行ずつ表示
- 変数をクリーンアップ
改行を入れた読みやすい形式:
# ヘッダー行を出力
printf "%-8s %-8s %-18s %-8s %-8s %-6s %-10s %-10s\n" \
"SESSION" "UID" "USER" "TYPE" "SERVICE" "REMOTE" "SEAT" "TTY"
# セッション一覧を取得してループ処理
loginctl list-sessions --no-legend | while read -r sid uid user seat tty; do
# セッション詳細情報を取得して配列に格納
eval "$(loginctl show-session "$sid" \
-p Type -p Service -p Remote -p Seat -p TTY --value | \
awk '{print "v[++i]=\""$0"\""}')"
# 整形して出力
printf "%-8s %-8s %-18s %-8s %-8s %-6s %-10s %-10s\n" \
"$sid" "$uid" "$user" \
"${v[1]:--}" "${v[2]:--}" "${v[3]:--}" "${v[4]:--}" "${v[5]:--}"
# 変数をクリーンアップ
unset v i
doneこのコマンドは loginctl を使用して,現在のシステムにログインしているセッション情報を表形式で見やすく表示します
SESSION UID USER TYPE SERVICE REMOTE SEAT TTY
1 1000 kirby tty login - seat0 tty1
2 1000 kirby x11 gdm-x - seat0 -
c1 1000 kirby wayland - - - -1. ヘッダー行の出力
printf "%-8s %-8s %-18s %-8s %-8s %-6s %-10s %-10s\n" "SESSION" "UID" "USER" "TYPE" "SERVICE" "REMOTE" "SEAT" "TTY"printfで左揃え(-),固定幅のカラムヘッダーを表示%-8sは8文字幅で左揃えの文字列を意味します
2. セッションリストの取得
loginctl list-sessions --no-legendloginctl list-sessions: systemdのログインセッション一覧を取得--no-legend: ヘッダー行を表示しない(パイプ処理しやすくするため)
出力例:
1 1000 kirby seat0 tty1
2 1000 kirby seat0 -3. while readループで各セッションを処理
while read -r sid uid user seat tty; do
# 処理内容
doneread -r: バックスラッシュをエスケープ文字として扱わない- 各行を5つの変数(
sid,uid,user,seat,tty)に分割して読み込み
4. loginctl show-sessionの実行
loginctl show-session "$sid" -p Type -p Service -p Remote -p Seat -p TTY --value-p: 特定のプロパティのみを取得--value: プロパティ名を省略し,値のみを出力
出力例:
tty
login
no
seat0
tty15. awkで配列代入文を生成
awk '{print "v[++i]=\""$0"\""}'- 各行を配列代入文に変換
++iで配列インデックスを1から順に増やす
出力例:
v[1]="tty"
v[2]="login"
v[3]="no"
v[4]="seat0"
v[5]="tty1"6. evalで配列代入を実行
eval "$(上記の出力)"evalで生成された代入文を実際に実行- 結果として配列
v[1],v[2], …,v[5]に値が格納
evalは文字列をシェルコマンドとして実行する強力なコマンドですが,セキュリティリスクもあります- 信頼できる入力(この場合は
loginctlの出力)に対してのみ使用してください。
7. フォーマット済み出力
printf "%-8s %-8s %-18s %-8s %-8s %-6s %-10s %-10s\n" "$sid" "$uid" "$user" "${v[1]:--}" "${v[2]:--}" "${v[3]:--}" "${v[4]:--}" "${v[5]:--}"- 取得した情報を整形して表示
${v[1]:--}: 変数が空または未定義の場合は-を表示(デフォルト値置換)
8. 変数のクリーンアップ
unset v i- ループの次の反復のために配列
vとカウンタiをクリア - これにより次のセッション処理時に前の値が残らないようにする
応用例
特定のユーザーのセッションのみ表示:
printf "%-8s %-8s %-18s %-8s %-8s %-6s %-10s %-10s\n" "SESSION" "UID" "USER" "TYPE" "SERVICE" "REMOTE" "SEAT" "TTY"; loginctl list-sessions --no-legend | grep dedede_daioh | while read -r sid uid user seat tty; do eval "$(loginctl show-session "$sid" -p Type -p Service -p Remote -p Seat -p TTY --value | awk '{print "v[++i]=\""$0"\""}')"; printf "%-8s %-8s %-18s %-8s %-8s %-6s %-10s %-10s\n" "$sid" "$uid" "$user" "${v[1]:--}" "${v[2]:--}" "${v[3]:--}" "${v[4]:--}" "${v[5]:--}"; unset v i; doneTSV形式で出力(表計算ソフトで使いやすい):
echo -e "SESSION\tUID\tUSER\tTYPE\tSERVICE\tREMOTE\tSEAT\tTTY"; loginctl list-sessions --no-legend | while read -r sid uid user seat tty; do eval "$(loginctl show-session "$sid" -p Type -p Service -p Remote -p Seat -p TTY --value | awk '{print "v[++i]=\""$0"\""}')"; echo -e "$sid\t$uid\t$user\t${v[1]:--}\t${v[2]:--}\t${v[3]:--}\t${v[4]:--}\t${v[5]:--}"; unset v i; done期間内の日付・曜日・週番号をCSV出力
指定期間の日付情報(日付,年始からの経過日数,曜日番号,ISO週番号)をCSV形式で出力するコマンド
start=2026-02-01 end=2026-02-10; for d in $(seq 0 $((($(date -d "$end" +%s)-$(date -d "$start" +%s))/86400))); do date -d "$start +$d day" +'%F,%j,%u,%V'; done出力例:
2026-02-01,032,7,05
2026-02-02,033,1,06
2026-02-03,034,2,06
2026-02-04,035,3,06
2026-02-05,036,4,06
2026-02-06,037,5,06
2026-02-07,038,6,06
2026-02-08,039,7,06
2026-02-09,040,1,07
2026-02-10,041,2,07
改行を入れた読みやすい形式:
start=2026-02-01
end=2026-02-10
for d in $(seq 0 $((($(date -d "$end" +%s) - $(date -d "$start" +%s)) / 86400))); do
date -d "$start +$d day" +'%F,%j,%u,%V'
doneこのコマンドは指定した期間の各日付について,日付・年始からの経過日数(通日)・曜日番号・ISO週番号を取得します。
1. 開始日と終了日の設定
start=2026-02-01
end=2026-02-10- 変数に開始日と終了日を設定
dateコマンドが認識できる形式であれば任意の日付が使用可能
2. 期間の日数を計算
$((($(date -d "$end" +%s) - $(date -d "$start" +%s)) / 86400))date -d "$end" +%s: 終了日をUnixエポック秒に変換date -d "$start" +%s: 開始日をUnixエポック秒に変換- 差分を計算し,86400(1日の秒数)で割ることで日数を取得
3. seq で日数分のループ
for d in $(seq 0 日数); doseq 0 日数: 0から日数までの連番を生成- 各数値をループ変数
dに代入
4. 日付の計算と整形
date -d "$start +$d day" +'%F,%j,%u,%V'date -d "$start +$d day": 開始日からd日後の日付を計算+%F: 日付(YYYY-MM-DD形式)+%j: 年始からの経過日数(001-366,通日・Day of Year)+%u: 曜日番号(1=月曜日 … 7=日曜日)+%V: ISO週番号(年の第何週か)- カンマ区切りでCSV形式として出力
%F: 日付(YYYY-MM-DD)%j: 年始からの経過日数(001-366,通日)%u: 曜日番号(1-7,月曜始まり)%w: 曜日番号(0-6,日曜始まり)%V: ISO週番号%A: 曜日名(full name)%a: 曜日名(abbreviated)
応用例
ヘッダー付きCSV出力
echo "日付,通日,曜日番号,週番号"; start=2026-02-01 end=2026-02-10; for d in $(seq 0 $((($(date -d "$end" +%s)-$(date -d "$start" +%s))/86400))); do date -d "$start +$d day" +'%F,%j,%u,%V'; done曜日名を含めた出力
start=2026-02-01 end=2026-02-10; for d in $(seq 0 $((($(date -d "$end" +%s)-$(date -d "$start" +%s))/86400))); do date -d "$start +$d day" +'%F,%j,%A,%V'; done出力例:
2026-02-01,032,Sunday,05
2026-02-02,033,Monday,06
平日のみを抽出
start=2026-02-01 end=2026-02-10; for d in $(seq 0 $((($(date -d "$end" +%s)-$(date -d "$start" +%s))/86400))); do x=$(date -d "$start +$d day"); u=$(date -d "$x" +%u); [ "$u" -le 5 ] && date -d "$x" +'%F,%j,%u,%V'; doneタブ区切りで出力(TSV形式)
## ANSI-C quotingを使用
start=2026-02-01 end=2026-02-10; for d in $(seq 0 $((($(date -d "$end" +%s)-$(date -d "$start" +%s))/86400))); do date -d "$start +$d day" +$'%F\t%j\t%u\t%V'; done先頭のゼロを削除して整数形式で出力
通日(036)を整数(36)として出力する方法:
方法1: 算術展開を使用
start=2026-02-01 end=2026-02-10; for d in $(seq 0 $((($(date -d "$end" +%s)-$(date -d "$start" +%s))/86400))); do x=$(date -d "$start +$d day"); printf "%s,%d,%d,%d\n" "$(date -d "$x" +%F)" "$((10#$(date -d "$x" +%j)))" "$((10#$(date -d "$x" +%u)))" "$((10#$(date -d "$x" +%V)))"; done出力例:
2026-02-01,32,7,5
2026-02-02,33,1,6
2026-02-03,34,2,6
$((10#変数)): 10進数として解釈し,先頭のゼロを削除printf "%d": 整数フォーマットで出力(すべての数値フィールドに適用)
方法2: AWKを使用
start=2026-02-01 end=2026-02-10; for d in $(seq 0 $((($(date -d "$end" +%s)-$(date -d "$start" +%s))/86400))); do date -d "$start +$d day" +'%F,%j,%u,%V'; done | awk -F',' '{printf "%s,%d,%d,%d\n", $1, $2, $3, $4}'- AWKの
%dフォーマット指定子が自動的に先頭のゼロを削除 -F',': カンマ区切りで入力を分割
リモートサーバーのログをクリップボードにコピー
SSHでリモートサーバーに接続し,aptのインストール履歴をローカルのクリップボードにコピーするコマンド
ssh <username>@<remote-server> "cat /var/log/apt/history.log" | xclip -selection clipboardこのコマンドは,リモートサーバー(<remote-server>)上のapt履歴ログファイルをローカルマシンのクリップボードに直接コピーします
1. SSH でリモートサーバーにアクセス
ssh <username>@<remote-server> "cat /var/log/apt/history.log"ssh <username>@<remote-server>: リモートホスト「<username>@<remote-server>」にSSH接続"cat /var/log/apt/history.log": リモートサーバー上でaptの履歴ログを表示/var/log/apt/history.log: パッケージのインストール・削除・アップグレードの履歴が記録されているファイル
このファイルには以下の情報が記録されています:
- パッケージのインストール(
apt install) - パッケージの削除(
apt remove) - パッケージのアップグレード(
apt upgrade) - 実行日時とコマンドラインオプション
2. xclip でクリップボードにコピー
| xclip -selection clipboardxclip: X Window Systemのクリップボード操作ツール-selection clipboard: 標準的なクリップボード(Ctrl+Vで貼り付け可能)を使用- パイプ(
|)でSSHの出力をxclipに渡す
clipboard: 標準クリップボード(Ctrl+C/V)primary: プライマリセレクション(マウス中ボタンで貼り付け)secondary: セカンダリセレクション(あまり使用されない)
応用例
dpkg.log を取得
ssh <username>@<remote-server> "cat /var/log/dpkg.log" | xclip -selection clipboard- dpkgのより詳細なログを取得
最新N行のみをコピー
ssh <username>@<remote-server> "tail -n 50 /var/log/apt/history.log" | xclip -selection clipboard- 最新50行のみをクリップボードにコピー
複数のログファイルを結合
ssh <username>@<remote-server> "cat /var/log/apt/history.log /var/log/apt/history.log.1.gz | zcat" | xclip -selection clipboard- 現在のログとローテーションされた圧縮ログを結合
特定期間のログを抽出
ssh <username>@<remote-server> "grep -A 5 'Start-Date: 2026-02' /var/log/apt/history.log" | xclip -selection clipboard- 2026年2月のインストール履歴のみを抽出
ローカルファイルに保存しつつクリップボードにもコピー
ssh <username>@<remote-server> "cat /var/log/apt/history.log" | tee apt-history-backup.log | xclip -selection clipboardteeコマンドを使用して,ローカルファイルへの保存とクリップボードへのコピーを同時に実行
xclipがインストールされていない場合はsudo apt install xclipでインストールが必要ですWaylandを使用している場合は,
wl-clipboardパッケージのwl-copyを使用します:ssh <username>@<remote-server> "cat /var/log/apt/history.log" | wl-copySSHの公開鍵認証を設定しておくと,パスワード入力が不要で便利です
テキストファイルの小文字を大文字に変換
ファイルの内容を小文字から大文字に変換して表示するコマンド
tr a-z A-Z < fileA例:
入力ファイル(fileA):
hello world
this is a test出力:
HELLO WORLD
THIS IS A TESTこのコマンドは tr(translate)コマンドを使用して,ファイル内のすべての小文字を大文字に変換します
1. tr コマンド(translate characters)
tr a-z A-Ztr: 文字を変換(translate)または削除するコマンド- 第1引数(
a-z): 変換元の文字セット(小文字 a から z) - 第2引数(
A-Z): 変換先の文字セット(大文字 A から Z) - 標準入力から読み込んだ文字列の各文字を,小文字なら対応する大文字に1対1で変換
- 標準入力からのみデータを読み込む(ファイル名を直接引数に取れない)
- そのため,リダイレクトやパイプを使用する必要があります
- 文字単位で処理(単語や行単位ではない)
2. リダイレクト(<)で入力
< fileA<: 入力リダイレクト演算子fileAの内容をtrコマンドの標準入力にリダイレクト- これにより,ファイルの内容が
trコマンドに渡される
リダイレクト (<):
tr a-z A-Z < fileAパイプ (|) + cat:
cat fileA | tr a-z A-Zどちらも同じ結果を得られますが,リダイレクトの方が効率的です(cat プロセスが不要)
応用例
大文字を小文字に変換
tr A-Z a-z < fileA- 逆方向の変換(大文字→小文字)
数字を削除
tr -d 0-9 < fileA-d: delete オプション- すべての数字(0-9)を削除
連続する空白を1つにまとめる
tr -s ' ' < fileA-s: squeeze オプション- 連続する空白を1つにまとめる
改行をスペースに変換(1行にまとめる)
tr '\n' ' ' < fileA- 改行文字をスペースに変換
- 複数行のテキストを1行にまとめる
特定の文字を置換
tr ',' '\t' < fileA- カンマをタブに変換
- CSV → TSV 変換に使用可能
補集合を使用した変換
tr -c 'a-zA-Z0-9\n' '_' < fileA-c: complement(補集合)オプション- 英数字と改行以外のすべての文字をアンダースコアに変換
- ファイル名の正規化などに便利
文字列の削除と圧縮を組み合わせ
tr -cd 'a-zA-Z ' < fileA | tr -s ' '- 英字とスペース以外を削除(
-cd) - その後,連続するスペースを1つにまとめる(
-s)
trコマンドは基本的な文字変換に特化しており,正規表現は使用できない- マルチバイト文字(日本語など)の処理には制限があり
- 複雑なパターンマッチングには
sedやawkの使用推奨
for loopとモデル評価
model ディレクトリにある全モデルを順番に評価してログを保存するコマンド
mkdir -p logs
for MODEL_PATH in models/*.pkl
do
NAME=$(basename "$MODEL_PATH" .pkl)
poetry run python evaluate_cli.py \
--model_path "$MODEL_PATH" \
--target cv \
--make_pdp \
> "logs/eval_${NAME}.log" 2>&1
donefor loopの基本
for シェル変数 in 値のリスト
do
コマンド
doneinの後のリストの要素が順番にforの後のシェル変数に格納され,そのたびにコマンドが実行
evaluate_cli.py の実装方針
Typer CLIを用いる場合を想定しています.pakcage構成はsrc layoutを採用し,以下のような構成にします.
project/
│
├─ cli/
│ ├─ evaluate_cli.py
│ └─ ...
├─ src/
│ └─ package/
│ ├─ __init__.py
│ └─ evaluate.py ← ロジック
├─ models/
└─ pyproject.tomlこのとき,evaluate_cli.py の例として以下
import typer
from package.evaluate import run_evaluation
app = typer.Typer()
@app.command()
def evaluate(
model_path: str,
target: str = "cv",
make_pdp: bool = False,
):
result = run_evaluation(model_path, target, make_pdp)
print(result)
if __name__ == "__main__":
app()