シェルでこれどうやるんだっけ?ってなる自分のための備忘録
はじめに
忘れっぽいので、シェルでこうしたい。っていうのをまとめていく。
何かあったら適宜追加するかも。
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'
awkで出力する区切り文字をカンマ区切りにしたい
OFSを指定する。
$ echo "a b c" | awk 'BEGIN{OFS=","} {print $1, $2, $3}'
a,b,c
{print}
だと {print $0}
と解釈されるので、OFSが利かない。
参考:
awkの入力の区切り文字をスペース以外にしたい
FSを指定、もしくは-Fオプションを使う
cat test2
ばなな,100
りんご,120
ぶどう,200
$ cat test2 | awk -F, '{print $1}'
ばなな
りんご
ぶどう
$ cat test2 | awk 'BEGIN{FS=","} {print $1}'
ばなな
りんご
ぶどう
cutコマンドを使うのもあり
$ cat test2 | cut -d, -f1
ばなな
りんご
ぶどう
リダイレクトとパイプ
-
>
:標準出力を指定ファイルに書き出す(ファイルが無かったら新規作成。ファイルがあったら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
:標準エラー出力を標準出力と同じ出力先に変更する
参考
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 <文字列> で実行する。
xargsの後にコマンドを指定するが、何も指定しないとechoコマンドが使われる。
※-pを指定すると、xargsが実行しようとするコマンドを確認することができる。
参考:
$ cat << FIN | xargs -I{} -p sh -c 'echo hello {}'
pipe heredoc> Alice
pipe heredoc> Bob
pipe heredoc> Cathy
pipe heredoc> FIN
sh -c 'echo hello Alice' ?...
sh -c 'echo hello Bob' ?...
sh -c 'echo hello Cathy' ?...
特定の文字列に一致するものを取得
grep -rho 'hoge' ./* --exclude=fuga
- r:再帰
- h:検索結果からファイル名を除外する
- o:--only-matching。マッチした文字列のみ抽出
- l:ファイル名のみ列挙する
- --exclude:指定ファイルを除外する
複数の文字列を検索したい
and条件で検索するなら、パイプで繋ぐのが一番はやい。
grep hoge in.txt | grep moga
or条件なら、-e で条件を繋げる
grep -e hoge -e fuga in.txt
大量の文字列のいずれかに一致するか検索したい
量が多い場合(従業員リストからIDで検索したいなど)、パイプで繋げなくても、検索したい文字列をファイルに列挙しておき、-f
オプションで指定することが可能。
$ cat test
ばなな 100
りんご 120
ぶどう 200
$ cat pattern.txt
100
ばなな
200
# 1行目は、ばななと100に一致している
$ grep -f pattern.txt test
ばなな 100
ぶどう 200
参考:
大量にあるファイルを一発で置換する
自分で特定のファイル名に変更するシェルコマンドをawkで組み立て、shに標準入力で渡す。
参考:
$ 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
uuid生成する
$ uuidgen
e283483f-7638-4af4-92f3-7a97be2e6081
適当なUUIDをテストコードなどで使いたい時、改行が邪魔なので
uuidgen | tr -d '\n' | pbcopy
標準エラー出力をログに書き込む
exec コマンドを使うことで以降、そのプロセスでの標準エラー出力は指定したファイルへ書き出される。(スクリプト書くときの一番最初に書いたりする)
exec 2> /var/tmp/logfile
バッチで途中処理ファイルをtmpデータに吐き出しておき、その中身をログで確認したい場合には、
上記のexecと組み合わせつつ、cat で標準エラー出力すればログに書き出せる。
cat ${tmp}-data_log 1>&2
引数の何個目以降を使用する
functionやスクリプトで渡された引数は、$1, $2,...
と取得できるが、N番目以降と取りたい場合は、${@:N}と書く
$ function args_test() {
function> echo "引数すべて: $@"
function> echo "引数1個目: $1"
function> echo "引数3個目以降:${@:3}"
function> }
$ args_test a b c d e
引数すべて: a b c d e
引数1個目: a
引数3個目以降:c d e
行の操作
xargsを使う。
複数行を一行にまとめる
% cat test
ばなな 100
りんご 120
ぶどう 200
% cat test| xargs
ばなな 100 りんご 120 ぶどう 200
○行ごとに一行にまとめる
xargs -L数字
で、指定した行ごとに1行にまとめる
% cat test2
Aさん
1/1うまれ
男
Bさん
2/1生まれ
女
Cさん
3/1生まれ
男
# 3行ごとに1行にまとめる(1行は3列になる)
% cat test2 | xargs -L3
Aさん 1/1うまれ 男
Bさん 2/1生まれ 女
Cさん 3/1生まれ 男
複数列を一列にする
xargs -n数字
で指定
-n, --max-args
は、xargsに指定されたコマンドを1回実行する時の引数上限。
参考:
% cat test
ばなな 100
りんご 120
ぶどう 200
# 1
% cat test | xargs -n1
ばなな
100
りんご
120
ぶどう
200
もしくは、trコマンドを使って、改行を削除する(最後の改行も無くなるので注意)
$ cat test | tr -d '\n'
ばなな 100りんご 120ぶどう 200
1つ前の引数を扱う
$_
が直前のコマンドの最後の引数を格納している特殊変数
lsしたものをcatとかviしたい時や、mkdirしたものにcdしたい時などで使える
$ ls tmp-file
tmp-file
$ echo $_
tmp-file
$ mkdir hoge ; cd $_
ファイルの一部を修正して上書き
$ 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
○行ごとにファイル分割する
splitコマンドを使用。
-l で分割する行数を指定する可能。
split -l 3 -d ファイル名 [分割後のファイル名のprefix]
参考:
空行削除
sed '/^$/d'
seqコマンド
seq 1 10 だと、1から10まで順番になる。
2つ刻みで行きたい場合には、開始、インクリメントする値、終了の3つを指定する
2刻み
$ 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
フォーマットで%02g
などで0埋めできる
$ seq -f 'user_%02g' 1 5
user_01
user_02
user_03
user_04
user_05
文字列数を確認する
hoge="hoge"
# -n で改行コードを無くさないと駄目
echo -n ${hoge} | wc -m
# もしくは
echo ${#hoge}
参考:
表示するテキストの幅合わせをする
うまくインデントを合わせたい
$ 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
文字をめっちゃ生成する
同じ文字を生成する
ブレース展開で同じ文字生成
hogeの後に空白を付けている。
10個生成するのに、,
が9個必要なのは注意
$ echo hoge{,,,,,,,,,}
hoge hoge hoge hoge hoge hoge hoge hoge hoge hoge
yesコマンドで同じ文字生成
たぶん一番手っ取り早い
$ yes hoge | head -n 10
hoge
hoge
hoge
hoge
hoge
hoge
hoge
hoge
hoge
hoge
yes [文字列]
でyesの代わりに、指定した文字列を繰り返し列挙する。
そのままだとCtrl+Cで強制終了しないと止まらないので、headで欲しい分取るようにする。
参考:
treeコマンドで特定のファイルを除外する
-I オプション
tree -L 1 -I "target"
参考:こちらから為になったもの
ターミナル操作
履歴展開
直前のコマンドを呼び出す
!!
を使う
$ apt update
パッケージリストを読み込んでいます... 完了
E: ロックファイル /var/lib/apt/lists/lock をオープンできません - open (13: 許可がありません)
E: ディレクトリ /var/lib/apt/lists/ をロックできません
$ sudo !!
sudo apt update # ← シェルが履歴展開した結果が表示されて、実際にコマンドが実行される
コマンド展開としても評価できる
# おもむろに並列で投げまくりたいときとか
$ yes localhost/ping | head | tr '\n' ' '
localhost/ping localhost/ping localhost/ping localhost/ping localhost/ping localhost/ping localhost/ping localhost/ping localhost/ping localhost/ping
$ curl --parallel --parallel-immediate --parallel-max 300 $(!!)
curl --parallel --parallel-immediate --parallel-max 300 $(yes localhost/ping | head | tr '\n' ' ')
直前のコマンドに渡した最後の引数を展開する
!$
を使う
# 編集したあと
$ vi ~/.zshrc
# 読み込む
$ source !$
source ~/.zshrc
Discussion