shell備忘録
特定の文字列に一致するものを取得
grep -rho 'hoge' ./* --exclude=fuga
- r:再帰
- h:ファイル名除外
- o:--only-matching。マッチした文字列のみ抽出
- --exclude:指定ファイルを除外する
特定の文字列から始まるものを列挙する
たとえば、特定フォルダ下でどんなファイルをよく参照しているかなど
grep -rho '/hoge/.*' ./* | awk '{print $1}' | LANG=C sort -k1,1 -u
forとwhileとサブシェル
fotとwhileはどちらともサブシェル扱いだと思っていたけど、どうやら違うっぽい。
for文で途中で抜ける場合は、breakを使用する。
※zshの場合は、whileでexitしたらターミナルがクローズしたので、サブシェルで動いてないのかも
参考
for
$ for times in $(seq 1 3); do echo 1; exit 2; done ; echo $?
1
logout
while
seq 1 3 | while read line; do echo $line ; exit 100 ; done ; echo $?
1
100
aws cli のリトライ処理をwhileで実装する
seq 1 3 | while read times; do
SEND_SUCCEED=10
aws s3 cp hoge s3://${s3_bucket}/hoge
[ $(plus ${PIPESTATUS[@]}) -eq 0 ] && exit ${SEND_SUCCEED}
sleep 10
done
# while終了時ステータスチェック
[ $? != ${SEND_SUCCEED} ] && exit 1
[[
testコマンドの拡張 bashの拡張記述
日付や数値比較などが、-lt
や -gt
じゃなくて、不等号が使える。
<=
と >=
は使えないので注意。
[[ 文字列 =~ 正規表現 ]]
文字列が正規表現に一致すれば真。
[[ 文字列1 < 文字列2 ]]
現在のロケールの辞書順で文字列1が文字列2よりも前なら真。
[[ 文字列1 > 文字列2 ]]
現在のロケールの辞書順で文字列1が文字列2よりも後なら真。
参考:
if [[ "${today}" < 20210101 ]]; then
サブシェル バックグラウンド実行
シェル、コマンドの後ろに &
$ (for times in $(seq 2 2 10); do echo ${times}; sleep 1; done) & (for times in $(seq 1 2 9); do echo ${times}; sleep 1; done) ;
[1] 7838
2
1
3
4
6
5
7
8
10
9
[1]+ Done ( for times in $(seq 2 2 10);
do
echo ${times}; sleep 1;
done )
seqコマンド
スタート、ステップ エンド
$ seq 2 2 10
2
4
6
8
10
seqコマンドのフォーマット
-f
を付けることで、文字列と連結も可能
$ seq -f 'user_%g' 1 10
user_1
user_2
user_3
user_4
user_5
user_6
user_7
user_8
user_9
user_10
xargs
いろいろ忘れがちなので。
$ cat << FIN | xargs -I{} sh -c 'echo hello {}'
> Alice
> Bob
> Cathy
> FIN
hello Alice
hello Bob
hello Cathy
-
I : replace-str。オプションの後に指定した文字列を、標準入力から受け取った値に置き換える
-
sh -c <文字列> : 指定した文字列のコマンドを実行する
以下は結果は同じ
$ echo alice
alice
$ sh -c 'echo alice'
alice
xargsに一連のパイプしたコマンドを渡したい場合には、sh -c <文字列> で実行する
ヒアドキュメント
いろいろ勘違いしていたけど、ヒアドキュメントは、 << 終端文字列
。標準入力として扱われる。
なので、標準入力先のコマンドは、受け取れればなんでも良い。
cat << FIN
heredoc> Alice
heredoc> Bob
heredoc> Cathy
heredoc> FIN
Alice
Bob
Cathy
cut -d. -f1 << FIN
heredoc> a.bc
heredoc> bfdfd.ww
heredoc> 123.4666
heredoc> FIN
a
bfdfd
123
grep 'a' << FIN
heredoc> alice
heredoc> bob
heredoc> cathy
heredoc> FIN
alice
cathy
標準入力したコマンドが何かしら標準出力するなら、パイプだったりリダイレクトを繋げても良い。
参考
aws s3 api で存在確認
aws s3api head-object --bucket ${bucket} --key ${filepath} &> /dev/null
&>
は、標準出力と標準エラーのどちらも同じ場所にリダイレクトする意味
存在している場合は、jsonが標準出力で返ってきて、存在していない場合は、標準エラーが返ってくる。
ステータスコードは存在していたら、0。存在してなかったらそれ以外。
標準出力、標準エラーどちらとも要らないので、&> /dev/null
で捨てる。
|&
で標準出力、標準エラーどちらとも標準入力として渡すことも可能
aws s3api head-object --bucket ${bucket} --key ${filepath} |& cat > /dev/null
パイプ、リダイレクト参考
aws s3 api参考
プロセスをN秒ごとに監視
hoge.sh がちゃんと動いているか監視したい
`watch -d -n 5 'ps auxw | grep hoge.sh'`
- n:数字を指定すると、その秒数ごとにコマンドを実行
- d:前回のコマンド実行結果とdiffが出ている場合はハイライト表示
1つ前の引数を扱う
$_
が直前のコマンドの最後の引数を格納している特殊変数
lsしたものをcatとかviしたい時や、mkdirしたものにcdしたい時などで使える
$ ls tmp-file
tmp-file
$ echo $_
tmp-file
$ mkdir hoge ; cd $_
shellというか、vimだけど。
数字にカーソル合わせた後、control + x → デクリメント
数字にカーソル合わせた後、control + a → インクリメント
dateコマンド
$ date "+%Y%m%d"
20210120
$ date "+%y%m%d"
210120
ファイルの一部を修正して上書き
$ cat memo
りんご 100
ばなな 200
オレンジ 300
$ cat memo | sed 's/オレンジ/グレープ/' > memo_edit
$ cat memo_edit
りんご 100
ばなな 200
グレープ 300
$ mv memo_edit memo
確認手順すっ飛ばすなら、sedのiオプションで上書き可能
$ sed -i 's/オレンジ/グレープ/' memo
$ cat memo
りんご 100
ばなな 200
グレープ 300
複数の文字列を検索したい
パイプで繋ぐのが一番はやい
grep hoge in.txt | grep moga
量が多い場合(従業員リストからIDで検索したいなど)、パイプで繋げなくても、検索したい文字列をファイルに列挙しておき、f
オプションで指定することが可能。
指定日より古いファイルを削除する
/var/tmp
などにゴミが溜まってきているときによくやる
# 前日分までのファイルの詳細を列挙する
$ find /var/tmp/ -mtime +1 -ls
# 怖いので一回期待する値がxargsに渡っているか確認
$ find /var/tmp/ -mtime +1 | xargs echo
# 前日分までのファイルを削除する
$ find /var/tmp/ -mtime +1 | xargs rm
リダイレクトとパイプ
-
>
:標準出力を指定ファイルに書き出す(ファイルが無かったら新規作成。ファイルがあったら0バイトにした上で上書き)- zshの場合はデフォルトで上書き防止となっている。問答無用で上書きしたい場合は、
>|
で指定。
$ echo "d e f" > hogefile zsh: file exists: hogefile $ echo "d e f" >| hogefile $ cat hogefile d e f
- zshの場合はデフォルトで上書き防止となっている。問答無用で上書きしたい場合は、
-
>>
:標準出力を指定ファイルに書き出す(ファイルが無かったら新規作成。ファイルがあったら追記) -
2>>
: 標準エラーを指定ファイルに書き出す(ファイルが無かったら新規作成。ファイルがあったら追記) -
<
:標準入力を指定する。ファイルじゃなくても、コマンドの結果を渡すこともできる
cut -d" " -f 1 <(ls -lt)
合計
-rw-rw-r--
-rw-rw-r--
-rw-rw-r--
# 以下コマンドと一緒
ls -lt | cut -d" " -f
-
&>
:標準出力、標準エラー出力を指定ファイルに書き出す(ファイルが無かったら新規作成。ファイルがあったら0バイトにした上で上書き)
aws s3 cp s3://hoge hoge &> /dev/null
-
|&
:標準出力、標準エラー出力を次のコマンドの標準入力とする。2>&1 |
と一緒 -
1>&2
:標準出力を標準エラーに変更する -
2>&1
:標準エラー出力を標準出力に変更する
コマンド置換
$()
で囲ったコマンドの結果を展開してくれる。
# crontabのバックアップの名前で現在の日付使いとか
crontab -l > crontab.$(date "+%Y%m%d%H%M")
自分は<()
でコマンドを囲ったときの違いと混乱する時があるけど、
コマンドの結果をただ展開するだけなのが、$()
で囲うパターン。
コマンドの結果を標準入力として渡すのが、<()
で囲うパターンという認識。
順位付け
awk使う。
[root@67d6d46b0970 linux]# ls -lt | awk 'BEGIN{num=0} {num++; print num, $0}'
1 total 116
2 -rw-r--r-- 1 root root 576 Feb 2 14:40 README.md
3 -rwxr-xr-x 1 root root 8880 Feb 2 14:30 head
4 -rw-r--r-- 1 root root 731 Feb 2 14:30 head.c
5 -rwxr-xr-x 1 root root 8656 Jan 24 09:11 catstd
6 -rw-r--r-- 1 root root 404 Jan 24 09:11 catstdio.c
7 -rw-r--r-- 1 root root 30 Jan 24 09:10 data3
8 -rw-r--r-- 1 root root 16 Jan 24 09:07 data2
9 -rwxr-xr-x 1 root root 8712 Jan 24 09:00 cat
10 -rw-r--r-- 1 root root 4096 Jan 24 07:56 data
11 -rwxr-xr-x 1 root root 8784 Jan 16 02:19 wc
12 -rw-r--r-- 1 root root 980 Jan 16 02:19 wc.c
13 -rw-r--r-- 1 root root 1018 Jan 16 01:41 cat.c
14 -rwxr-xr-x 1 root root 8448 Jan 15 14:44 bell
15 -rw-r--r-- 1 root root 104 Jan 15 14:44 bell.c
16 -rwxr-xr-x 1 root root 8448 Jan 15 12:59 args
17 -rw-r--r-- 1 root root 195 Jan 15 12:59 args.c
18 -rw-r--r-- 1 root root 95 Jan 15 12:50 hello.c
nlコマンドでいい
$ echo echo "aaa bbb ccc" | xargs -n1 | nl -n ln
1 aaa
2 bbb
3 ccc
標準エラー出力をログに書き込む
exec コマンドを使うことで、以降の標準エラー出力が指定したファイルへ書き出される。
exec 2> /var/tmp/logfile
バッチで途中処理ファイルをtmpデータに吐き出しておき、その中身をログで確認したい場合には、
上記のexecと組み合わせつつ、cat で標準エラー出力すればログに書き出せる。
cat ${tmp}-data_log 1>&2
文字列数を比較する
hoge="hoge"
# -n で改行コードを無くさないと駄目
echo -n ${hoge} | wc -m
# もしくは
echo ${#hoge}
行の操作
複数行を一行にまとめる
% cat test
ばなな 100
りんご 120
ぶどう 200
% cat test| xargs
ばなな 100 りんご 120 ぶどう 200
○行ごとに一行にまとめる
% cat test2
Aさん
1/1うまれ
男
Bさん
2/1生まれ
女
Cさん
3/1生まれ
男
% cat test2 | xargs -L3
Aさん 1/1うまれ 男
Bさん 2/1生まれ 女
Cさん 3/1生まれ 男
複数列を一列にする
% cat test
ばなな 100
りんご 120
ぶどう 200
% cat test | xargs -n1
ばなな
100
りんご
120
ぶどう
200
yesコマンドで欲しい文字列の繰り返し
$ yes hoge
hoge
hoge
hoge
...
awkで区切り文字をカンマ区切りにしたい
$ echo "a b c" | awk 'BEGIN{OFS=","}{print $1, $2, $3}'
a,b,c
{print}
だと {print $0}
と解釈されるから、OFSが利かないっぽい
○行おきにレコード出力
$ cat test
aaa,bbb,ccc
ddd,eee,fff
ggg,hhh,iii
jjj,kkk,lll
$ cat test | awk 'BEGIN{FS=","} NR%2==0 {print $1}'
ddd
jjj
awkでシングルクォート、ダブルクォートを扱う
参考した記事で出来たけど、ぱっと見て自分がすぐに理解できなかったので、
分解しながら考えていく
awk で ダブルクォートで囲む
ちなみに、awk のprint() は()自体が省略可能
$ cat amaimono.txt | awk '{print $1}'
apple
banana
choco
$ cat amaimono.txt | awk '{print($1)}'
apple
banana
choco
"
で囲うと、文字列扱いとなる(変数として展開されない)
$ cat amaimono.txt | awk '{print "$1"}'
$1
$1
$1
"
を文字列として扱いたいので、"
で囲いつつ\
でエスケープする
"\""
→ "
$ cat amaimono.txt | awk '{print "\"" $1}'
"apple
"banana
"choco
先頭と行末でそれぞれ囲もうとすると、こうなる
$ cat amaimono.txt | awk '{print "\"" $1 "\""}'
"apple"
"banana"
"choco"
awk で シングルクォートで囲む
シングルクォートの場合若干複雑。
$ cat amaimono.txt | awk '{print "'\''" $1 }'
'apple
'banana
'choco
文字列で扱いたい為、"
で囲みつつ、'\''
でエスケープしたシングルクォートをシングルクォートで囲むことをしている。
これはawkに渡すprogram text自体がシングルクォートで囲んでいる為、\'
だけだとprogram textを囲っているシングルクォートだと認識されてしまう。
そのため、program自体をシングルクォートで一旦囲み、文字としてのシングルクォートをエスケープして書いてから、更にprogramの続きを書くためのシングルクォートを書く必要がある
"'\''"
→ '
$ cat amaimono.txt | awk '{print "'\''" $1 "'\''"}'
'apple'
'banana'
'choco'
表示するテキストの幅合わせをする
うまくインデントを合わせたい
$ cat amaimono.txt
商品 金額 在庫数
apple 10 100
banana 200 30
choco 300 99999
$ cat amaimono.txt | column -t
商品 金額 在庫数
apple 10 100
banana 200 30
choco 300 99999
カンマ区切りの場合は、デリミタを指定する必要がある
cat amaimono.csv | column -t
商品,金額,在庫数
apple,10,100
banana,200,30
choco,300,99999
$ cat amaimono.csv | column -t -s,
商品 金額 在庫数
apple 10 100
banana 200 30
choco 300 99999
一発で置換する
awk置き換え
$ ls hoge.*
hoge.json hoge.swp hoge.txt
$ ls hoge.* | awk -F"." '{print "mv "$0" moge."$2}'
mv hoge.json moge.json
mv hoge.swp moge.swp
mv hoge.txt moge.txt
$ ls hoge.* | awk -F"." '{print "mv "$0" moge."$2}' | bash
$ ls moge.*
moge.json moge.swp moge.txt
grepでの複数行検索
grep -f ファイル名で、検索対象に対して、指定したファイルの中にある値でgrepしてくれる。
$ cat targetfile
hoge
moge
koge
tige
$ cat grepfile
hoge
moge
tige
aaa
bbb
$ cat targetfile| grep -f grepfile
hoge
moge
tige
# こちらと同等
$ cat targetfile| grep -e hoge -e moge -e tige
hoge
moge
tige
ヒアドキュメントでも可能
$ cat targetfile | grep -f <(cat << FIN
pipe cmdsubst heredoc> tige
pipe cmdsubst heredoc> hoge
pipe cmdsubst heredoc> moge
pipe cmdsubst heredoc> FIN
pipe cmdsubst> )
hoge
moge
tige
○行ごとにファイル分割する
split -l 3 -d ファイル名 [分割後のファイル名のprefix]
標準入力から受け取ったものをvimで開く
cat test.txt | vi -
pbpaseteと一緒に使ったり、取ってきたログをvimで見たりしつつ修正して保存するときに効果的
pbpaste | vi -