Bash
- bash スクリプト
- 文法
- -
- コマンド
- 標準入力/標準出力
- ターミナルのショートカットキー
- Tips
- sudo をパスワードなしで実行できるようにする
- 別のユーザでコマンドを実行する
- 引数の必須チェック
- GNU parallel で並列処理
- コマンドの結果を 1 つずつ for でループ
- 各行ごとに指定した文字列の出現回数をカウント
- 引数の数チェック
- カレントディレクトリ配下のディスク容量を確認する
- ディレクトリを.tarファイルに圧縮する
- システムが起動してからの時間を分単位で取得する
- コマンドの処理結果が空の場合に任意のコマンドを実行
- tar.gz を解凍する
- 指定したディレクトリ配下のディレクトリ一覧を取得
- 複数行テキストのファイルを変数に代入、表示
- 変数の計算結果を使う
- 回数を変数にして指定回数ループ
- 2 つのファイルで重複行以外を取り出す
- tsv, csv の整形など
- コマンドのエラーを無視しつつ終了コードを取得する
- bash プロンプトに色をつける
- Ascii 文字をエスケープする
- 文字列が一致するか判定する
- Mac で色付き文字をエスケープする
- .gz 形式のログを見る
- .gz 形式のログを日付順にソートして 1 ファイルに結合する
- 半角スペースつきのパスへ cd する
- ディレクトリ内のファイルでループ
- フルパスからファイル名だけ取り出し
- ファイル名から拡張子を取り除く
- カレントディレクトリ配下の json ファイルを繰り返し処理で POST する
- /c/tools/配下のbinフォルダへ自動でパスを通す
- ポートをつかんでいる PID、サービス名を取得
- 指定した URL に接続できるまで待機
- サブシェル
- sudo でファイルにリダイレクトする
- CLI ツールなどでコマンド出力を変数に入れられないとき
- 標準エラーのみ捨てる
- シェル自身のパスを取得
- 指定したディレクトリのファイル名の一覧を取得
- ファイルの存在判定
- ディレクトリの存在判定
- 変数が定義されているか判定
- ssh, scp のパスワード入力プロンプトなしで実行
- 特定のポートを使っているプロセスを kill する
- バックグラウンド処理を stdout へ出力
- プロセス関連
- 無限ループ
- 引数の空判定
- シェルスクリプト内でエラーが起きたら即終了する
- ワンライナーで複数行ファイルを sudo で作成
- ワンライナーでユーザ作成
- ワンライナーで他ユーザの bash セッション開始
- コマンドが成功するか判定
- ユーザのホームディレクトリを変更
- ツール
- FAQ
bash スクリプト
set -euxo pipefail
について。
set -e
: エラーが起きたらすぐに終了set -o pipefail
: パイプの左側でエラーが起きても終了set -u
: 未定義の変数を参照したら終了set -x
: 実行したコマンドをコンソールに表示する
#!/usr/bin/env bash
set -euxo pipefail
SCRIPT_DIR=$(cd $(dirname $0); pwd)
Ref: Safer bash scripts with 'set -euxo pipefail'
文法
;
セミコロンで分の区切りになる。
echo "aaa"; echo "bbb"
\
改行があっても1行として実行。 長い文を見やすくするときに使う。
echo "aaa"; \
echo "bbb"
&&
直前のコマンドが成功(戻り値が 0)なら次のコマンドを実行する。
[ 1 -eq 1 ] && echo "hello"
# -> "hello"
[ 1 -eq 2 ] && echo "hello"
# -> ""
ブレース: {}
$(***)
コマンドの実行結果に置換される。
echo $(date '+%Y')
# -> 2019
-
ハイフン(-
)はファイルの代わりに標準出力を使うことを示している。
例えば、標準出力に文字列を出力するコマンドをパイプでつないで、後ろのコマンドでファイルの代わりにその文字列を使いたいときなどに利用できる。
# catはもともとファイルを引数にとるが、"-"にすることでパイプの標準出力を出力する
echo "hoge" | cat -
# -> hoge
:-
左辺が存在しない場合に右辺を返す(デフォルト値)。
echo ${str:-'default'}
# -> default
str=aaa
echo ${str:-'default'}
# -> aaa
利用例。関数の第一引数が指定されていたらそれを、指定されていなければ現在のディレクトリを dir 変数に代入する。
dir=${1:-`pwd`}
!!
直前のコマンドに置換される。
echo hoge
# -> hoge
!!
# -> hoge
!$
直前の引数に展開される。
touch script.sh
chmod +x !$
コマンド
scp: リモートーローカル間でファイルをコピー
# ローカルからリモートにコピー
scp hoge.txt <user>@<host>:<pass>
# ex: hoge.txtをリモートの /home/hako1912 ディレクトリにコピー
scp hoge.txt cloud-user@my-cloud-host:/home/hako1912
# ローカルファイルを踏み台サーバーを経由して直接コピー
scp -o "ProxyCommand ssh <踏み台user>@<踏み台host> -W %h:%p" hoge.txt <user>@<host>:<path>
wc: ファイル行数をカウント
wc -l hoge.txt
mkdir
# パスが存在しない場合は親ディレクトリ含め作成する。エラーも表示しない。
mkdir -p a/b/c
history
コマンド履歴を表示する。
history
# コマンド名でソート
history | sort -k2
# !コマンドの隣の数字でコマンドを実行できる
!10
nc (netcat)
https://qiita.com/yasuhiroki/items/d470829ab2e30ee6203f
# localhostの8080ポートが空いているか確認
nc -zv localhost 8080
dos2unix
インストール。
sudo apt install -y dos2unix
# ファイルは上書きされる
dos2unix <file>
# 再帰的に変換
find <dir name> -type f -print0 | xargs -0 dos2unix
# 拡張子で絞り込んで変換
find . -type f | egrep '\.(kt|yml|yaml|adoc|sh|json|sql)$' | xargs dos2unix
ps
プロセスの一覧を表示する。
ps
# 他のユーザのプロセスも含めすべて表示
ps a
# 詳細表示
ps u
# プロセスの親子関係も表示
ps u
# ユーザー指定して表示
ps -U <user name>
# 指定したユーザのPIDのみ表示
ps -U <user name> o pid --no-headers
date
date '+%Y/%m/%d'
# 2019/03/05
find
# 指定した拡張子のファイルを一覧表示
find . -type f | egrep '\.(kt|yml|yaml|adoc)$'
cut
区切り文字を指定してパースする。
# :で区切られた2番目の項目のみ出力する
cut -d: -f2 hoge.txt
# 例: ユーザ名の一覧を表示する
cut -d: -f1 /etc/passwd
# 例: 最後に登録したユーザ名を表示する
cut -d: -f1 /etc/passwd | tail -n1
tail
最終行から行を表示する。
tail <file name>
# 最後から数えて2行目までを表示
tail -n 2 hoge.txt
# 最初の1行をスキップ。n+1で指定することに注意
tail -n +2 hoge.csv
sed
ファイルの特定の文字列を置換する
# hoge.yamlの'origin'を'replaced'に置換したファイルをfuga.yamlとして出力する
sed 's/origin/replaced/' hoge.yaml > fuga.yaml
# hoge.yamlの'origin'を'replaced'に置換して上書きする
sed -i 's/origin/replaced/' hoge.yaml
chmod
権限を付与する。
# 実行権限を付与する
chmod +x my-script.sh
jq
json のパーサ。windows の場合は jq.exe をインストールする必要がある。 PowerShell を管理者で開いて次のコマンドを実行する。
chocolatey install jq
# jsonを整形してそのまま出力
echo '{ "foo":123 }' | jq
# キーを指定して値を取得する
echo '{ "foo": 123 }' | jq '.foo'
# 123
# ネストしているオブジェクトから取得する
echo '{"a": {"b":123 } }' | jq '.a.b'
# 123
# オブジェクトの配列から特定のキー値のみ取得する
echo '{ "items": [ { "val": 1 }, { "val": 2 }, { "val": 3 } ] }' | jq '.items[].val'
# 1
# 2
# 3
# キーをファイル名を指定して取得する
jq '.dependencies' package.json
# {
# "express": "^4.16.4",
# "lodash": "^4.17.11",
# }
# 特殊な値`keys`を使うと、キーの配列を取得できる
jq '.dependencies | keys' package.json
# [
# "express",
# "lodash"
# ]
# さらにパイプでつないでパースできる。
jq '.dependencies | keys | .[]' package.json
# "express"
# "lodash"
# さらにrオプションを使うとクォートを取り除ける
jq -r '.dependencies | keys | .[]' package.json
# express
# lodash
# REST APIから取得したjsonをパースして出力する例
curl -s https://api.github.com/repos/facebook/react | jq '.stargazers_count'
# jsonファイルを1行文字列にして出力
cat hoge.json | jq -c
# 出力結果をさらにパイプに渡す場合はフィルタが必要
# '.'はなにもしないフィルタ
cat hoge.json | jq -c . > aaa.txt
# 特殊文字は""で囲む
echo '{"$": 100}' | jq '."$"'
ルートが配列の JSON に対してフィルタリングする例:
JSON='[{"type":"A","value":1},{"type":"A","value":2},{"type":"B","value":3}]'
echo $JSON | jq 'map(select(.type == "A"))' | jq '.[].value'
ssh キー生成
# 鍵生成
ssh-keygen -t rsa -f ~/.ssh/[KEY_FILENAME] -C [USERNAME]
# 秘密鍵を古い形式で出力 `-m PEM`
ssh-keygen -t rsa -f ~/.ssh/[KEY_FILENAME] -C [USERNAME] -m PEM
git push
で以下のようなエラーがでることがある。
Permissions 0777 for '.ssh/id_rsa' are too open
そのときは権限を 600 に変更する。
chmod 600 <id_rsaのパス>
ref: ssh “permissions are too open” error
ln: シンボリックリンク作成
ln -s [実体] [リンク]
ln -s ~/dotfiles/.bashrc ~/.bashrc
# -f: 強制上書き
ln -sf a.txt b.txt
# -n: -fオプションを付けても対象がディレクトリであった場合は上書きされない。-nをつけると上書きしてくれる。
ln -sfn a.txt b.txt
# -v: 経過を表示する
ln -sfnv a.txt b.txt
cp: ファイルコピー
次のファイルツリーの場合。
src
- a1.txt
dist
# srcディレクトリごとdistディレクトリへコピー
cp src dist
ls dist
# ->
# src
# srcディレクトリの中身をdistディレクトリへコピー
cp src/* dist
ls dist
# ->
# a1.txt
xargs
前のコマンドの出力結果をコマンドライン引数に変換する。
echo "hello" | xargs echo
# -> hello hello
引数の位置を変えたい場合は-I
オプションを使う。
# {} が引数に置き換わる
echo "hello" | xargs -I {} echo "[" {} "]"
# -> [ hello ]
.tgz
を解凍する
tar: tar -xzf [ファイル名].tgz
標準入力/標準出力
標準出力の一覧を取得
ls -la /dev/ | grep 'std.*'
# lrwxrwxrwx 1 hatak 197609 15 9月 28 23:15 stderr -> /proc/self/fd/2
# lrwxrwxrwx 1 hatak 197609 15 9月 28 23:15 stdin -> /proc/self/fd/0
# lrwxrwxrwx 1 hatak 197609 15 9月 28 23:15 stdout -> /proc/self/fd/1
リダイレクト
コマンドの結果を出力
# 正常ログのみ出力
ls hoge 1> ls.txt
# エラーのみ出力
ls noexist 2> ls-errs.txt
# 正常もエラーも両方出力
ls noexist > ls.txt 2>&1
コマンドの引数へ入力
# ファイルの中身をコマンドへ渡す
cat < ls.txt
ターミナルのショートカットキー
ctrl + A
行頭へ移動
ctrl + E
行末へ移動
ctrl + K
カーソル位置より後ろを削除
ctrl + W
カーソル位置から直前の単語を削除
ctrl + L
コンソールをクリア
Tips
sudo をパスワードなしで実行できるようにする
USER='hoge'
echo "$USER ALL=NOPASSWD: ALL" | sudo EDITOR='tee -a' visudo
Ref:
linux - How do I edit /etc/sudoers from a script? - Stack Overflow
別のユーザでコマンドを実行する
複数行のスクリプトを実行する場合は以下のようにする。
ヒアドキュメント内で変数を参照する場合は"\$HOME"
のようにバックスラッシュを前に置く必要があることに注意。
sudo -i -u $OTHER_USER bash << EOF
#!/usr/bin/env bash
echo "\$USER"
echo "\$HOME"
echo "hoge"
EOF
Ref:
shell script - setting variables inside subshell when using << - Unix & Linux Stack Exchange
引数の必須チェック
if [ -z "$1" ] ; then
echo "hoge is required."
exit 1
fi
hoge=$1
GNU parallel で並列処理
インストール。
# mac
brew install parallel
# ubuntu
apt install parallel
parallel <command> ::: <スペース区切りの文字列>
で並列処理できる。
parallel echo ::: a b c
# 他のシェルを実行
parallel ./hoge.sh ::: a b c
コマンドの結果を 1 つずつ for でループ
items=$(command...)
for item in $items
do
echo $item
done
各行ごとに指定した文字列の出現回数をカウント
awk -F'|' 'BEGIN{print "count", "lineNum"}{print gsub(/,/,",") "\t" NR "\t" $0 }' <FILE>
引数の数チェック
$#
を使って受け取った引数の数をチェックできる。
# 受け取った引数が2つ未満なら使い方を表示して終了
if [ "$#" -ne 2 ]; then
echo "Usage: ./my-script.sh SOURCE_PATH DIST_PATH"
echo "Example: ./my-script.sh hoge.txt fuga.txt"
exit
fi
カレントディレクトリ配下のディスク容量を確認する
du -h --max-depth=1 .
.tar
ファイルに圧縮する
ディレクトリを- c: アーカイブ作成
- f: アーカイブファイル名指定
tar cf <圧縮後のファイル名> <圧縮するディレクトリ名>>
# ex: tar cf dir.tar dir
システムが起動してからの時間を分単位で取得する
Ubuntu の場合はapt install bc
しておく。
echo $(awk '{print $1}' /proc/uptime) / 60 | bc
コマンドの処理結果が空の場合に任意のコマンドを実行
<コマンド> | [ $(wc -c) -eq 0 ] && <コマンド結果が空だったときに実行するコマンド>
# /home 配下のファイルが120分以上編集されていなかったらシャットダウン
sudo find /home -type f -mmin -120 | [ $(wc -c) -eq 0 ] && shutdown -h now
Ref: https://stackoverflow.com/a/42884374
tar.gz を解凍する
tar -zxvf <file name>
指定したディレクトリ配下のディレクトリ一覧を取得
! -path <path>
で結果から自身のパスを除外できる。
DIR='.'
for FILE in $(find $DIR ! -path $DIR -maxdepth 1 -type d); do
echo "$FILE"
done
### 指定したポートを使用しているプロセスをkillする
```bash
lsof -i :<port> | tail -n +2 | tr -s ' ' | cut -d' ' -f 2 | xargs kill -9
複数行テキストのファイルを変数に代入、表示
複数行文字列を echo するときはダブルクォートで囲む必要がある。
# OK
echo "$(multiline.txt)" > hoge.txt
# NG
echo $(multiline.txt) > hoge.txt
Ref: https://orebibou.com/2014/11/シェルスクリプトで変数に改行コードを含める方/
変数の計算結果を使う
$[<計算処理>]
でできる。
COUNT=10
for i in $(seq $[$COUNT -1] 1)
do
echo "i = $i";
done
Ref: http://omoisan.hatenablog.com/entry/20120307/1331048559
回数を変数にして指定回数ループ
COUNT=10
for i in $(seq 0 $COUNT)
do
echo "i = $i";
done
2 つのファイルで重複行以外を取り出す
uniq -u
で重複した行を除外している。
awk '{ print $1 }' 1.txt 2.txt | sort | uniq -u | head -n 10
tsv, csv の整形など
# ヘッダ行を除いて表示
cat hoge.csv | tail -n +2
# ヘッダ行を除いた行数を表示
cat hoge.csv | tail -n +2 | wc -l
# 1列目でソート
cat hoge.csv | tail -n +2 | sort -k 1
Ref: https://stackoverflow.com/a/604871
コマンドのエラーを無視しつつ終了コードを取得する
#!/bin/bash -eu
<command> || echo $?; true
# エラーになっても即終了せずに終了コードを判定して独自の処理を入れる
<command> > /dev/null || true; result=$?
if [ $result -eq 1 ]; then
echo 'error!'
exit 1
fi
bash プロンプトに色をつける
Ref:
ANSI エスケープシーケンス チートシート - Qiita
Ascii 文字をエスケープする
プロンプトに色付き文字を表示するときに使える。
FOREGROUND_RED='\e[38;5;160m'
red_string() {
echo -e "${FOREGROUND_RED}赤い文字"
}
PS1="\$(red_string)"
Ref:
hex - How to type ASCII code "00" and "01" in linux bash? - Stack Overflow
文字列が一致するか判定する
hoge='test'
if [ "$hoge" = 'test' ]; then
echo "hoge == test"
else
echo "hoge != test"
fi
Mac で色付き文字をエスケープする
printf
を使う。
Ubuntu などではecho -e
でも動くが、Mac の場合はecho -e
だとうまく動かないのでとりあえずprintf
を使うと安全。
RED='\e[48;5;160m'
printf "${RED}赤い文字\n"
# MacでNG
echo -e "${RED}赤い文字"
Ref:
CentOS7 と OS X Mavericks の echo コマンドの違いについて - はらへり日記
.gz 形式のログを見る
zcat
で OK だけど MAC の場合はgzcat
にしないといけない。
gzcat hoge.gz
.gz 形式のログを日付順にソートして 1 ファイルに結合する
find . -name '*.gz' | sort | xargs gzcat >> hoge.log
半角スペースつきのパスへ cd する
# \でスペースをエスケープできる
cd ~/My\ Code
# 変数に入れた場合は"で囲めばOK
VSCODE_INSIDER_PATH="/c/Users/hoge-user/AppData/Roaming/Code - Insiders/User"
cd "${VSCODE_INSIDER_PATH}"
ディレクトリ内のファイルでループ
カレントディレクトリから深さ 2 までのテキストファイルに対して繰り返すときの例。
for FILE in $(find . -maxdepth 2 -type f -name "*.txt"); do
program -in $FILE -out $FILE.out
done
フルパスからファイル名だけ取り出し
file='aaa/bbb/ccc.txt'
echo `basename $file`
# => ccc.txt
sed
を使うパターン。
echo "/aaa/bbb/ccc/ddd.json" | sed -E 's/.*\///'
ファイル名から拡張子を取り除く
file='ccc.txt'
file_name=`basename $file | sed 's/\.[^\.]*$//'`
echo $file_name
# => ccc
カレントディレクトリ配下の json ファイルを繰り返し処理で POST する
# これでもいい
# for file in ./permissions/*.json; do
for file in `\find . -maxdepth 1 -type f -name "*.json"`; do
echo $file
curl -X POST -H 'Content-Type:application/json' -d @${file} localhost:8080
done
/c/tools/
配下のbin
フォルダへ自動でパスを通す
for FILE in $(find /c/tools -maxdepth 2 -type d -name "bin"); do
echo $FILE
done
ポートをつかんでいる PID、サービス名を取得
# 3306番ポートを使っているプロセスを表示してPIDを確認
netstat -ano | grep ":3306"
# PIDでフィルタしてサービス名を取得
# ※確認したPIDが`5992`だったときの例
tasklist -fi "PID eq 5992"
指定した URL に接続できるまで待機
Docker で他のコンテナサービスが立ち上がるまで待ちたいときとか。
until curl localhost:3000 ; do
sleep 3
done
echo "connected..."
自分のコンテナのサービス立ち上げを待機してから初期データを入れたいときは、待機してからデータ挿入する shell をバックグラウンドで実行するようにすればいい。
FROM couchbase
# `waitAndEcho.sh`で、localhost:8091の接続待機後にデータ挿入
RUN ./waitAndEcho.sh &
サブシェル
一時的にcd
して違うディレクトリでコマンドを打ちたいけど実際にディレクトリ移動はしたくないときなど。
# 一つ上の階層でls
(cd ..; ls)
sudo でファイルにリダイレクトする
sudo
でリダイレクトしたい場合は代わりにtee
を使う。
# OK
echo 'hoge' | sudo tee -a hoge.txt
# NG
sudo echo 'hoge' > hoge.txt
Ref: http://yut.hatenablog.com/entry/20111013/1318436872
CLI ツールなどでコマンド出力を変数に入れられないとき
標準エラーに出力しているので、標準エラーを標準入力に出力するようにしてから変数に代入する。
hoge=$([command] 2>&1)
標準エラーのみ捨てる
[command] 2>/dev/null
Ref: Linux / Unix | 標準エラー出力を捨てる
シェル自身のパスを取得
ref: Get the source directory of a Bash script from within the script itself
# シェルがあるディレクトリの絶対パスを取得(おすすめ)
SCRIPT_DIR=$(cd $(dirname $0); pwd)
# 実行する場所によっては動作しないけどシンプルver
dir=`dirname ${0}`
# こっちのほうが安全らしい
dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
.sh
ファイルでシェルのパスへ cd するとカレントをシェルのパスとして扱えるので、とりあえず先頭につけておくと便利。
#!/bin/bash
cd `dirname ${0}`
指定したディレクトリのファイル名の一覧を取得
find <directory> -type f -printf "%f\n"
Ref: For files in directory, only echo filename (no path)
ファイルの存在判定
FILE=/etc/resolv.conf
if [[ -f "$FILE" ]]; then
echo "$FILE exist"
fi
Ref:
How to Check if a File or Directory Exists in Bash | Linuxize
ディレクトリの存在判定
[ -d <path> ]
でディレクトリの存在判定ができる。
# VSCodeの設定ファイルのディレクトリがあるか判定する
VSCODE_PROFILE_DIR="/c/Users/${USER}/AppData/Roaming/Code/User"
if [ ! -d $VSCODE_PROFILE_DIR ]; then
echo 'dir not exists...'
fi
変数が定義されているか判定
[ -z <name> ]
でできる。
# HOGEが定義されていればtrue, それ以外でfalse
# 正確にはHOGEの文字列長が0かどうかで判定している
if [ -z $HOGE ]; then
echo 'HOGE is not defined'
exit 1
fi
ssh, scp のパスワード入力プロンプトなしで実行
sshpass
を入れる。
# mac
brew install https://raw.githubusercontent.com/kadwanev/bigboybrew/master/Library/Formula/sshpass.rb
# alpine
apk add sshpass
Ref: https://stackoverflow.com/a/32258393
次のようにしてログインする。
sshpass -p <password> <user>@<host>
scp
のファイルコピーもパスワードなしでできる。
sshpass -p <pass> scp <user>@<host>:/home/hoge/test.log .
ローカルマシンの関数を直接リモートで実行する例。
f() { ls -la; }
sshpass -p <pass> ssh <user>@<host> "`typeset -f f`; f"
Ref: https://codeday.me/jp/qa/20190211/238583.html
特定のポートを使っているプロセスを kill する
lsof -i :<ポート番号>
でプロセス ID を調べられる。
lsof -i :80
# 調べたPIDでkillする
kill -9 <PID>
バックグラウンド処理を stdout へ出力
command > /dev/null 2>&1 &
プロセス関連
# プロセスIDを確認する
ps
# 停止したいプロセスのWINPIDを指定して実行する
taskkill -pid [WINPID] -f
bash で ctrl+c が効かなくなったときは、その bash で実行中のプロセスを停止すると復活する。
無限ループ
while true
do
echo "hello"
done
1 行で書くとこうなる。do
とdone
の後ろだけセミコロンがいらないことに注意。
while true; do echo "hello"; done
# trueはコロンにできる
while :; do echo "hello"; done
# 1行関数の例
hoge() { while :; do echo "hello"; done }
引数の空判定
ref: Bash Shell Find Out If a Variable Is Empty Or Not
## syntax 1 ##
if [[ -z "$variable" ]]; then
echo "Empty $variable"
else
echo "Do whatever you want as \$variable is not empty"
fi
## Syntax 2 ##
[[ -z "$variable" ]] && echo "Empty" || echo "Not empty"
## Syntax 3 ##
[ -z "$var" ] && echo "Empty: Yes" || echo "Empty: No"
ref: How to check if a variable is set in Bash?
if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi
ワンライナーで引数の空判定をする例。
# 第一引数が空ならfoo、そうでなければbarを$aにいれて、$aを表示
hoge() { [ -z $1 ] && a='foo' || a='bar'; echo $a ; }
シェルスクリプト内でエラーが起きたら即終了する
# これ以降エラーや未定義変数を参照するとシェルが終了する
set -eu
# 解除
set +eu
シェルスクリプト内で使う場合はシェバンに-eu
を付けると、シェルスクリプト内でのみ有効(source
で他のシェルを呼び出した場合、そのシェルには-eu
は適用されない)となる。
#!/bin/bash -eu
解除もしなくていいのでこっちの方が便利。
ワンライナーで複数行ファイルを sudo で作成
echo '<文字列>' | sudo tee <file path>
# ファイル末尾に追記
echo '<文字列>' | sudo tee --append <file path>
# コンソールに結果を出さない場合
echo '<文字列>' | sudo tee --append <file path> > /dev/null
# 改行コードなどを文字列に使いたい場合
printf "aaa\nbbb\nccc" | sudo tee <file path>
ref: sudo echo “something” >> /etc/privilegedFile doesn't work
ワンライナーでユーザ作成
sudo useradd -p $(openssl passwd -1 <password>) <user name>
ref: How do I create a user with a password in one line, in Bash, on Redhat?
ワンライナーで他ユーザの bash セッション開始
sudo -u <user name> bash
コマンドが成功するか判定
yarn
コマンドがある場合のみパスを通す例
if yardn global bin ; then
export PATH="$PATH:`yarn global bin`"
else
echo "yarn not installed..."
fi
ユーザのホームディレクトリを変更
usermod -m -d <home directory> <user name>
ツール
ShellCheck
shell 用の Lint。 https://www.shellcheck.net/ https://github.com/koalaman/shellcheck
VSCode の拡張機能もある。 https://github.com/timonwong/vscode-shellcheck
インストール
brew install shellcheck
apt install shellcheck
FAQ
stdout is not a tty, stdin is not a tty
winpty bash
してから同じコマンドを実行すれば動く。