grep
テキスト検索に使用され、ファイルの内容をマッチングします。構文形式は grep pattern filename です。例:
# forを含むすべての行を抽出
grep 'for' test.sh
# 複数のファイルを検索
grep 'for' test.sh bak.sh
# マッチした部分をハイライト
grep 'for' test.sh --color=auto
デフォルトはワイルドカードマッチングで、正規表現を使用するには E(extended) オプションを有効にする必要があります:
# echoで始まるすべての行を抽出
grep -E '^\s*echo' test.sh
または、デフォルトで正規表現を許可する egrep コマンドを使用します:
# 同上
egrep '^\s*echo' test.sh
その他のオプションと機能:
# マッチした部分のみを出力
grep -o -E '\s[a-zA-Z]\s' test.sh
# マッチしない行のみを出力(反転選択)
grep -v -E '\s[a-zA-Z]\s' test.sh
# マッチした行数をカウント
grep -c -E '\s[a-zA-Z]\s' test.sh
# マッチした項目数をカウント
grep -o -E '\s[a-zA-Z]\s' test.sh | wc -l
# マッチした行と行番号を出力
grep -n -E '\s[a-zA-Z]\s' test.sh
# マッチした項目が含まれるファイル名を出力(反転選択はL)
grep -l 'return' test.sh bak.sh return.sh
# ディレクトリを再帰的に検索し、ファイル名と行番号を出力
grep -n -R 'echo' .
# 大文字小文字を区別しない
grep -i "ECho" test.sh
# ディレクトリ検索でファイル名の形式を限定
# 注意:includeパラメータの値は引用符で囲む必要があります。findコマンドとは異なります
grep -R '=>' . --include '*.jsx}'
# ディレクトリ検索で特定のファイル形式やディレクトリを除外
grep -R '' . --exclude '*.md' --exclude-dir 'node_modules'
# 終端記号として \0 を出力。通常、-l でファイル名のみを出力し、xargs -0 で後続のコマンドに渡す際に使用
grep "echo" . -R -l -Z | xargs ls -l
# サイレントマッチング。標準出力には何も出力せず、マッチ成功時に 0 を返す
if echo ' abcd' | grep -q -E '^\s*abc'; then echo 'starts with abc'; fi
マッチ項目の特定以外に、コンテキストを出力することもできます:
# マッチした行とその後ろの2行を出力
seq 10 | grep '4' -A 2
# マッチした行とその前の2行を出力
seq 10 | grep '4' -B 2
# マッチした行とその前後の各2行を出力
seq 10 | grep '4' -C 2
cut
3つの切り出し方法があります:-c(文字単位)、-f(フィールド単位)、-b(バイト単位)
文字単位での切り出し:
# 各行の3文字目から5文字目までを切り出す
echo $'1 2 3 4\n5 6 7 8' | cut -c 3-5
# 3文字目から行末まで
echo $'1 2 3 4\n5 6 7 8' | cut -c 3-
# 5文字目まで
echo $'1 2 3 4\n5 6 7 8' | cut -c -5
フィールド(列)単位での切り出し。1列を1つのフィールドとして扱い、awk のように特定の列を抽出します:
echo $'1 2 3 4\n5 6 7 8' | cut -d ' ' -f 1,3
注意:非常に重要な問題は区切り文字です。デフォルトはタブ(Ctrl + v の後に tab)です。-d オプションで他の文字を指定できますが、単一文字しか指定できず、あまり使い勝手が良くありません(複数のスペースがある場合に対応できず、単一文字で区切られたコンテンツにのみ適しています)。
例えば、ps の結果から PID 列と CMD 列を切り出す場合:
# awkなら完璧に解決
ps | awk '{print $1,$4}'
# cutは使いにくい
# デフォルトのタブ区切りでは無効
ps | cut -f 1,4
# スペースを指定しても結果が正しくない
ps | cut -d ' ' -f 1,4
バイト単位での切り出し。デフォルトではマルチバイト文字の境界が無視されます:
# デフォルトでは文字をまたいで切り出され、漢字が壊れる
echo "想做个好人" | cut -b 2-4
# -n オプションを使用するとマルチバイト文字を分割せず、`想` が得られる
echo "想做个好人" | cut -n -b 2-4
sed
stream editor。非対話型のエディタで、よく使われるテキスト処理ツールです。最も一般的な機能はテキストの置換です:
# 行頭の空白文字を削除
echo $' \t 我想左对齐' | sed $'s/^[[:space:]]*\t*//g'
もう一つの便利な機能は、ファイルの上書き保存(置換結果を元のファイルに書き込む)です:
# test.txt 内のすべての単語を [word] に置換
echo $'this is a new file\nnext line' > test.txt
sed -i '' -E 's/[[:alpha:]]{1,}/[word]/g' test.txt
P.S. Mac の sed -i による上書き保存では、バックアップファイル名を指定する必要があります(空文字列でも可)。また、Mac の sed は GNU sed と大きく異なり、例えば + や ?、\b などが使えません。詳細な違いについては Differences between sed on Mac OSX and other “standard” sed? を参照してください。
通常、区切り文字は / ですが、任意の記号を使用することもできます:
# セミコロン
echo $'\t\t\t我想左对齐' | sed $'s;^\t*;;'
# Mac では `|` さえ使用可能
echo $'\t\t\t我想左对齐' | sed $'s|^\t*||'
# 区切りとしての意味を持たない記号はエスケープが必要
echo '&c' | sed -E 's;&[[:alpha:]]{1,}\;;\&;'
その他の一般的なオプション:
# /pattern/d でマッチした行を削除
sed '/^$/d' test.sh
# & は今回のマッチ部分を表す
echo 'abc de' | sed -E 's/[[:alpha:]]{1,}/[&]/g'
# \123.. 後方参照
echo 'aabcc' | sed 's/\([[:alpha:]]\)\1/[\1x2]/g'
# sed 'expr1; expr2...' 複数の正規表現を順番に適用。パイプと同じ効果
echo 'aabcc' | sed 's/\([[:alpha:]]\)\1/[\1x2]/g;s/\].*\[/][/'
注意:後方参照の例におけるキャプチャの括弧はエスケープする必要があります。
awk
通常、列単位の抽出に使用されます。例:
# ファイル名
ps | awk '{print $1, $4}'
非常に強力で、列や行に対して操作を行えます。一般的な形式は以下の通りです:
awk 'BEGIN{ print "start" } pattern1{ command } END{ print "end" }' file
BEGIN、END、およびパターンブロックはすべて任意です。まず BEGIN ブロックが実行され、次に入力内容から1行ずつ読み込まれ、各パターンブロックが順番に実行されます。すべての内容を読み終えると、最後に END ブロックが実行されます。
pattern も任意です。指定しない場合は、各行に対して無条件にブロック内のステートメントが実行されます。例:
# そのまま出力
echo $'1 2\n3 4' | awk '{print}'
# 行数をカウント
echo $'1 2\n3 4' | awk 'BEGIN{lineCount=0} {let lineCount++} END{print lineCount}'
print は少し特殊で、スペースで区切られた引数は連結して出力され、カンマで区切られた引数はスペースで区切られて出力されます。例:
# 123 を出力
echo '' | awk '{print 1 2 3}'
# 1 2 3 を出力
echo '' | awk '{print 1,2,3}'
# 1-2-3 を出力
echo '' | awk '{print 1"-"2"-"3}'
内置变量
awk にはいくつかの特殊な組み込み変数があります:
-
NR:number of records、現在の行番号
-
NF:number of fields、現在の行のフィールド数
-
$0:現在の行のテキスト内容
-
$123...:現在の行のn番目のフィールドの内容
そのため、より簡単な行数のカウント方法があります:
echo $'1 2\n3 4' | awk 'END{print NR}'
1行読むごとに NR が更新され、END ブロックに達したときが総行数になります。
注意:awk では、組み込み変数であってもユーザー定義変数であっても、変数名の前に $ を付ける必要はありません。
传递外部变量
awk 内で外部変数を直接使用することはできず、渡す必要があります:
# 空を出力
x=3; echo '' | awk '{print x}'
# 3 を出力
x=3; echo '' | awk -v x=$x '{print x}'
複数の外部変数を渡すより簡単な方法があります:
# 3 4 5 を出力
x=3; y=4; z=5; echo '' | awk -v x=$x -v y=$y -v z=$z '{print x,y,z}'
# 簡単な方法
x=3; y=4; z=5; echo '' | awk '{print x,y,z}' x=$x y=$y z=$z
ステートメントブロックの直後にキーと値のペアを記述し、コマンドライン引数として渡します。
getline
通常、次の行を読み込むために使用されます。使い方は以下の通りです:
# 1行目を出力
echo $'1 2\n3 4' | awk 'BEGIN{getline line; print line}'
# 1行目をスキップ(1行目の total xxx を破棄)
ls -l | awk 'BEGIN{getline} {print $0}'
引数なしの getline は $0 や $1, $2... を更新しますが、引数ありの場合は更新しません。例:
# 引数ありの場合はフィールド変数を更新しない
echo $'1 2\n3 4' | awk 'BEGIN{print $0; getline line; print $0}'
# 引数なしの場合はフィールド変数を更新する
echo $'1 2\n3 4' | awk 'BEGIN{print $0; getline; print $0}'
执行其它命令
awk 内で他のコマンドを実行する方法も特殊です:
# $0 は md5 test.sh の出力結果
echo '' | awk '{"md5 test.sh" | getline; print $0}'
# または
echo '' | awk '{"md5 test.sh" | getline md5; print md5}'
循环、条件
awk では、C言語スタイルのループや条件分岐などの構造を使用できます:
# while ループ
seq 10 | awk 'BEGIN{while (getline){print $0}}'
# for ループ
seq 10 | awk 'BEGIN{for(i=0; i<10; i++){getline; print $0}}'
# 条件文
seq 10 | awk 'BEGIN{for(i=0; i<10; i++){getline; if ($1 % 2) {print $0}}}'
これらの機能により awk は非常に強力になり、ファイルを1行ずつ処理するのが非常に便利になります。
P.S. その他の構文構造や組み込み関数については man awk を参照してください。
其它选项
よく使われるオプション:
# 区切り文字を指定。デフォルトはスペース
echo 'a;b;c' | awk -F ';' '{print $2}'
# または
echo 'a;b;c' | awk 'BEGIN{FS=";"} {print $2}'
# 出力時の区切り文字を指定
echo 'a b c' | awk 'BEGIN{OFS="\t"} {print $1,$2,$3}'
# パターンフィルタリング
# 行番号が2未満
echo $'1 2\n3 4' | awk 'NR < 2{print $0}'
# 行番号が2から4の間
seq 10 | awk 'NR==2,NR==4{print $0}'
# 正規表現にマッチ
echo $'1 2\n3 4' | awk '/^3/{print $0}'
处理文件内容
1行ずつ読み込む:
# 入力リダイレクト
while read line; do echo $line; done < test.sh
# またはサブシェル
cat test.sh | (while read line; do echo $line; done)
1行の中の各フィールドを読み込む:
line='1 2 3 4'; IFS=' '; for field in $line; do echo $field; done
フィールド内の各文字を読み込む:
field='word'; for ((i=0;i<${#field};i++)) do echo ${field:i:1}; done
ここで、文字列の一部を切り出すテクニック ${field:i:1} を使用しています。形式は ${var:開始インデックス:長さ} です。開始位置に負の数を指定すると、末尾からの位置になります:
# 最後の2文字を切り出す
field='abcdef'; echo ${field:(-2):2}
P.S. シェルのこれらの文字列処理のサポートは、本当に驚くほど強力です。
paste
テキスト内容を列単位で結合します。cat は行単位で結合しますが、paste は列単位で結合できます:
seq 3 > no.txt
echo $'吃饭\n睡觉\n打豆豆' > action.txt
# 行単位で結合
cat no.txt action.txt
# 列単位で結合
paste no.txt action.txt
paste の結果は以下のようになります:
# paste no.txt action.txt | sed -n l
1\t吃饭$
2\t睡觉$
3\t打豆豆$
デフォルトの区切り文字はタブですが、-d オプションで他の区切り文字を指定できます:
# 結合結果をセミコロンで区切る
paste -d ';' no.txt action.txt | sed -n l
コメントはまだありません