Linuxでのテキストファイルへの出力コマンドまとめ
DockerやTerraformなどでLinux環境を立ち上げる際に設定ファイルを自動で作成する必要がある。
以下でよく使うファイル出力コマンドについてまとめる。
1行だけ記述したテキストファイルを作成・追記
1行だけの場合は以下のようにechoコマンドで出力するのが手っ取り早い。
echo 'export PATH=$PATH:~/bin' >> ~/.bashrc
なお、>>は既存ファイルに追記する形式で出力する。
新規ファイルとして作成したい場合は>を使う。
echo "gnome-session --session=ubuntu" > ~/.xsession
なお、ダブルクォーテーションで括った際に変数が含まれている場合、自動的に変数展開されてしまうので、変数を含めてファイルに出力したい場合はシングルクォーテーションで囲むこと。
シングルクォーテーションを文字列として出力
シングルクォーテーションを含めてファイルに出力したい場合は以下のように記述する。
echo 'abc'\''de' > ~/temp.txt
abc'de
'\'で\をエスケープ文字として認識させ、次の'を文字として出力する。
2行以上のテキストファイルを作成
複数行をまとめて出力したい場合はcatコマンドとヒアドキュメントを用いる。
cat > ~/bin/sample.sh << EOF
#!/bin/bash
echo "Hello World."
EOF
echoを複数記述してファイル作成するより可読性が高くなるので2行以上の場合は使うことを推奨。
また、開始を示すキーワードは開始と終了で同じキーワードを使えば何でもよい。
ヒアドキュメントを使う際に変数展開をしない
bashスクリプトなどを自動作成するようなヒアドキュメントを記述しようとすると、
変数を使いたい場合がある。
前述のような記述をしてしまうと変数が自動的に展開されてしまう。
その場合は、開始のキーワードをシングルクォーテーションかダブルクォーテーションで囲む。
cat > ~/bin/sample.sh << "EOF"
#!/bin/bash
OUTPUT_STRING="Hello World."
echo "${OUTPUT_STRING}"
EOF
ヒアドキュメントを使う際に一部変数だけ変数展開しない
一部変数だけを変数展開したくない場合は$記号を\でエスケープすればよい
cat > ~/bin/sample.sh << EOF
#!/bin/bash
OUTPUT_STRING="${USER}: Hello World."
echo "\${OUTPUT_STRING}"
EOF
上記の場合、作成されるスクリプトでは${USER}はログインユーザーの名前が出力されるが、
${OUTPUT_STRING}はそのまま出力される。
関数内でヒアドキュメントをきれいに記述したい
ヒアドキュメントは原則として行頭に書く必要があるので関数内で記述しようとすると、
インデントを一つ下げて書くことになってしまうため、可読性が悪い。
ヒアドキュメントの機能で<<を<<-で記述することで行頭のハードタブを無視することができる。
#!/bin/bash
make_script() {
cat > ~/bin/sample.sh <<- "EOF"
#!/bin/bash
OUTPUT_STRING="Hello World."
echo "${OUTPUT_STRING}"
EOF
}
make_script
※上記のコードブロックだが、ハードタブで入力するとZenn側でなぜか2回分インデントされるので半角スペースでインデントしている、コードコピーする際は注意。
#!/bin/bash行から最後のEOF行までのインデントはハードタブでインデントされているものとする。
あくまで無視されるのはハードタブだけなので注意すること。
ハードタブは複数記述可能。ハードタブ以外の文字列が記述されない限りは全てのハードタブが無視される。
使用しているエディタによってはハードタブが自動的に半角スペースに置換されて入力されることもある。
行頭でカーソルを移動させることによってハードタブが入力されているのか、半角スペースが入力されているのか確認すること。
vs codeでは設定でタブと半角スペースを可視化できるので、機能を有効化しておくことを推奨。
sudoとヒアドキュメントでファイルを作成
sudoコマンドを組み合わせて使う場合は、catとヒアドキュメントではファイルの作成ができない。その場合はteeコマンドを組み合わせることでroot権限でファイルを作成することができる。
sudo tee ./sample.sh << EOF > /dev/null
#!/bin/bash
echo "Hello World."
EOF
> /dev/nullを記述することによってteeコマンドによる標準出力は捨てられるのでターミナル上には出力されない。
また、既存ファイルに追記したい場合は-aオプションを用いる。
sudo tee -a ./sample.sh << EOF > /dev/null
echo "Hello World2."
EOF
なお、上記の方法で実行した場合は所有者がrootユーザーになってしまうので
ユーザー権限で実行したい場合は別途以下のコマンドを実行して実行権限の付与と所有者の変更をしておく。
sudo chmod +x home/${USER}/bin/sample.sh
sudo chown -R ${USER}:${USER} home/${USER}/bin/sample.sh
ヒアドキュメント以外での出力方法
{}で囲むと複数コマンドをまとめて実行できる。
{
echo '#!/bin/bash'
echo 'echo Hello World.'
} > ~/bin/sample.sh
echoを大量に記述する必要が合ったり、シングルクォーテーションやダブルクォーテーションで囲むかで出力結果が変わったりと面倒な点が多いのでヒアドキュメントを使うのを推奨。
本来はテキストファイルへの出力(echoコマンドの実行)を目的としたような使い方ではないので当然と言えば当然。
テキスト出力と標準出力を両方実行
前述のteeコマンドで実現することができる。
bash ./sample.sh 2>&1 | tee $(date +"%m%d%H%M%S")_[filename].log
※2>&1は標準エラー出力を標準出力をまとめて出力できる
標準出力と標準エラー出力を別々のファイルに出力
bash ./sample.sh 1> >(tee $(date +"%m%d%H%M%S")_stdout_[filename].log >&1 ) 2> >($(date +"%m%d%H%M%S")_stderr_[filename].log >&2)
reference
- sudoとヒアドキュメントでファイルを作成する小技 #shell - Qiita
- シェルの入出力制御あれこれ #Bash - Qiita
- 逆引きシェルスクリプト/ヒアドキュメント内の変数を展開しない方法 - Linuxと過ごす (just4fun.biz)
- 【<,>,&】Linuxコマンド_コマンドの入出力結果をファイルへ保存する
- ヒアドキュメントのパラメータ展開を個別に制御する(及びそれに関してハマったところ) #Bash - Qiita
- bashのヒアドキュメントを活用する #Bash - Qiita
- 標準出力、標準エラー出力のリダイレクションとteeコマンドのまとめ #Linux - Qiita
Discussion