前の記事では、PATH と .bashrc で、ターミナルを自分好みに整える方法を見ました。今回は、いよいよ自動化の第一歩、シェルスクリプトを作ります。
毎日同じコマンドを、同じ順番で打っていませんか? よくあるのが、作業フォルダのバックアップを取るために、毎回 mkdir と cp を何行も手で打つことです。そのうち打ちまちがえて、別のフォルダにコピーしてしまう…。そんなときに「これ、ファイルに書いておけばいいのでは」と思えたら、シェルスクリプトの出番です。
この記事では、いつもの手順を1つのファイルにまとめて、1回の実行で動かせるようにするところまで、一緒に作っていきます。
- シェルスクリプトとは何か(コマンドを並べたファイル)
- 1行目の
#!/bin/bash(シバン)の意味 -
bash ファイル名と./ファイル名の2つの動かし方 - 日付つきのバックアップを取るスクリプトの作り方
-
bash -xで、どこで止まったかを確かめる方法 - Windows で作ったファイルで出る「required file not found」(古い bash では「bad interpreter」)の直し方
-
nanoでファイルを編集・保存できる -
chmod u+xで実行権限をつけられる - 環境変数(
$HOMEなど)と PATH のしくみがわかる
まだの方は、前の記事「Linuxの環境変数とPATH|「command not found」の正体と.bashrc・alias」から読むのがおすすめです。
- PATH:コマンドの本体を探しにいくディレクトリを
:(コロン)で区切って並べた環境変数。左から順に探します - .bashrc:bash をターミナルで開くたびに読みこまれる、自分用の設定ファイル(ホームディレクトリの隠しファイル)。cron から動くときなどは読まれません
- 実行権限:そのファイルをプログラムとして実行してよいかの許可(ls -l で見える x)
- フルパス(絶対パス):
/home/kaito/memo.txtのように、いちばん上の/から書いた場所
用語で迷ったら、連載のまとめ記事の「よく出てくる用語ミニ辞典」でも確かめられます。
結論:シェルスクリプトは「コマンドのレシピ」
シェルスクリプトは、ターミナルで打つコマンドを、上から順番にファイルに書いたものです。基本の形はこれだけです。
- 1
- 2
- 3
- 4
#!/bin/bash
# ここはコメント(説明のメモ)
echo "1つ目のコマンド"
echo "2つ目のコマンド"| やること | コマンド |
|---|---|
| 作る | nano ファイル名.sh |
| bash に読ませて動かす | bash ファイル名.sh |
| 実行権限をつける | chmod u+x ファイル名.sh |
| 直接動かす | ./ファイル名.sh |
| 1行ずつ表示しながら動かす | bash -x ファイル名.sh |
準備:練習用のディレクトリを作る
練習は ~/linux-practice/script-test の中で行います。
- 1
- 2
- 3
mkdir -p ~/linux-practice/script-test
cd ~/linux-practice/script-test
pwdここから先の出力例は、Debian 13での実行例です(ユーザー名 kaito・パソコン名 demo のダミー環境)。UbuntuやWSL(=Windows の中で Linux を動かせる、Microsoft のしくみ)では、表示が少し違うことがあります。
(例)
- 1
- 2
kaito@demo:~/linux-practice/script-test$ pwd
/home/kaito/linux-practice/script-testはじめてのシェルスクリプト
nano で hello.sh を作ります。
- 1
nano hello.sh開いた画面に、次の中身を貼り付けて保存し、閉じます(Ctrl + O → Enter → Ctrl + X)。
- 1
- 2
- 3
- 4
- 5
#!/bin/bash
# はじめてのシェルスクリプト
echo "こんにちは、kaito さん"
echo "今日の日付:$(date +%Y-%m-%d)"
echo "今いる場所:$(pwd)"書けたか確かめてから、まずは bash に読ませて動かしてみます。
- 1
- 2
cat hello.sh
bash hello.sh(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
kaito@demo:~/linux-practice/script-test$ cat hello.sh
#!/bin/bash
# はじめてのシェルスクリプト
echo "こんにちは、kaito さん"
echo "今日の日付:$(date +%Y-%m-%d)"
echo "今いる場所:$(pwd)"
kaito@demo:~/linux-practice/script-test$ bash hello.sh
こんにちは、kaito さん
今日の日付:2026-11-01
今いる場所:/home/kaito/linux-practice/script-test$(date +%Y-%m-%d) のように $( ) で囲むと、中のコマンドの結果をその場所に入れられます。日付をファイル名に使うときによく使う書き方です。
./hello.sh で直接動かす
今度は、bash をつけずに直接動かしてみます。
- 1
./hello.sh(例)
- 1
- 2
kaito@demo:~/linux-practice/script-test$ ./hello.sh
-bash: ./hello.sh: Permission denied日本語の環境では、最後が 許可がありません と表示されます。
Lv2 で見たとおり、実行権限(x)がないと動きません。chmod でつけてから、もう一度動かします。
- 1
- 2
- 3
chmod u+x hello.sh
ls -l hello.sh
./hello.sh(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
kaito@demo:~/linux-practice/script-test$ chmod u+x hello.sh
kaito@demo:~/linux-practice/script-test$ ls -l hello.sh
-rwxrw-r-- 1 kaito kaito 167 Nov 1 02:54 hello.sh
kaito@demo:~/linux-practice/script-test$ ./hello.sh
こんにちは、kaito さん
今日の日付:2026-11-01
今いる場所:/home/kaito/linux-practice/script-test持ち主のところに x がつきました。グループのところが rw- なのは、新しく作るファイルの権限の初期設定(umask)によるもので、環境によっては -rwxr--r-- と表示されます。
1行目の #!/bin/bash(シバン)の意味
./hello.sh のように直接動かすとき、Linux は1行目を見て「このファイルは /bin/bash に読ませればいいんだな」と判断します。この1行を シバン(shebang)と呼びます。#! のあとには、読ませるプログラムの場所を書きます。
- 1
- 2
head -1 hello.sh
which bash(例)
- 1
- 2
- 3
- 4
kaito@demo:~/linux-practice/script-test$ head -1 hello.sh
#!/bin/bash
kaito@demo:~/linux-practice/script-test$ which bash
/usr/bin/bashwhich bash が /usr/bin/bash と表示されても大丈夫です。Lv2 のフォルダ構成の記事で見たように、/bin は /usr/bin へのリンクになっているので、/bin/bash でも同じものが動きます。
実用編:日付つきのバックアップを取るスクリプト
次は、毎日の作業で使えるスクリプトです。練習用のメモフォルダを作り、それを日付と時刻つきのフォルダにコピーします。
- 1
- 2
- 3
- 4
- 5
cd ~/linux-practice/script-test
mkdir -p notes
echo "今日のやること:スクリプトを作る" > notes/todo.txt
echo "練習用のメモ" > notes/memo.txt
ls notes(例)
- 1
- 2
kaito@demo:~/linux-practice/script-test$ ls notes
memo.txt todo.txtnano backup-notes.sh で、次の中身を書いて保存します。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
#!/bin/bash
# notes フォルダを、日付と時刻つきのフォルダにコピーする
set -e
SRC="$HOME/linux-practice/script-test/notes"
DEST="$HOME/linux-practice/script-test/backup/$(date +%Y%m%d-%H%M%S)"
mkdir -p "$DEST"
cp -r "$SRC" "$DEST"
echo "バックアップしました:$DEST"それぞれの行の意味は、次のとおりです。
-
set -e:途中のコマンドが失敗したら、そこで止める(失敗したまま次に進まない) -
SRC=…・DEST=…:コピー元とコピー先の場所に名前をつけておく(変数。くわしくは次の記事で) -
"$DEST":変数を使うときは"で囲む(場所にスペースがあっても正しく動くように)
実行権限をつけて、動かしてみましょう。少し時間をあけて、2回動かします。
- 1
- 2
- 3
- 4
- 5
- 6
chmod u+x backup-notes.sh
./backup-notes.sh
sleep 2
./backup-notes.sh
ls backup
ls -R backup(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
kaito@demo:~/linux-practice/script-test$ chmod u+x backup-notes.sh
kaito@demo:~/linux-practice/script-test$ ./backup-notes.sh
バックアップしました:/home/kaito/linux-practice/script-test/backup/20261101-025506
kaito@demo:~/linux-practice/script-test$ sleep 2
kaito@demo:~/linux-practice/script-test$ ./backup-notes.sh
バックアップしました:/home/kaito/linux-practice/script-test/backup/20261101-025510
kaito@demo:~/linux-practice/script-test$ ls backup
20261101-025506 20261101-025510
kaito@demo:~/linux-practice/script-test$ ls -R backup
backup:
20261101-025506 20261101-025510
backup/20261101-025506:
notes
backup/20261101-025506/notes:
memo.txt todo.txt
backup/20261101-025510:
notes
backup/20261101-025510/notes:
memo.txt todo.txt1行のコマンドで、毎回ちがう名前のフォルダにバックアップが取れました。sleep 2 は「2秒待つ」コマンドで、フォルダ名の時刻をずらすために入れています。
SRC と DEST を、notes のような短い書き方ではなく、$HOME から始まるフルパスで書いているのもポイントです。短い書き方(相対パス)だと、「スクリプトを動かしたときに、どこにいたか」で探す場所が変わってしまいます。フルパスなら、どのフォルダから動かしても、同じ notes をコピーできます。この書き方は、あとの記事でスクリプトを毎朝自動で動かす(cron)ときに、とても大事になります。
作ったスクリプトを、名前だけで動かすには
前の記事で、自分のコマンドを置く場所として ~/linux-practice/bin を作り、PATH に足しました。そこにこのスクリプトをコピーすれば、./ をつけずに、どのフォルダからでも名前だけで動かせます。
実際に試してみます。前の記事の .bashrc の設定をまだしていない場合は、export PATH="$HOME/linux-practice/bin:$PATH" で、今のターミナルだけ PATH に足せます(ターミナルを閉じると元に戻ります)。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
mkdir -p ~/linux-practice/bin
export PATH="$HOME/linux-practice/bin:$PATH"
echo $PATH
cp backup-notes.sh ~/linux-practice/bin/
cd ~
backup-notes.sh
which backup-notes.sh(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
kaito@demo:~/linux-practice/script-test$ mkdir -p ~/linux-practice/bin
kaito@demo:~/linux-practice/script-test$ export PATH="$HOME/linux-practice/bin:$PATH"
kaito@demo:~/linux-practice/script-test$ echo $PATH
/home/kaito/linux-practice/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
kaito@demo:~/linux-practice/script-test$ cp backup-notes.sh ~/linux-practice/bin/
kaito@demo:~/linux-practice/script-test$ cd ~
kaito@demo:~$ backup-notes.sh
バックアップしました:/home/kaito/linux-practice/script-test/backup/20261101-054812
kaito@demo:~$ which backup-notes.sh
/home/kaito/linux-practice/bin/backup-notes.shecho $PATH の先頭に /home/kaito/linux-practice/bin が入ったので、ホームに戻ってから backup-notes.sh と名前だけで打っても動きました。which backup-notes.sh で、bin の中のファイルが見つかっていることも確かめられます。スクリプトの中がフルパスなので、ホームから動かしても、バックアップは script-test/backup の中にできています。
bash -x:どこで止まったかを確かめる
スクリプトがうまく動かないときは、bash -x で動かすと、実行した行を1行ずつ表示してくれます。
- 1
bash -x backup-notes.sh(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
kaito@demo:~/linux-practice/script-test$ bash -x backup-notes.sh
+ set -e
+ SRC=/home/kaito/linux-practice/script-test/notes
++ date +%Y%m%d-%H%M%S
+ DEST=/home/kaito/linux-practice/script-test/backup/20261101-025514
+ mkdir -p /home/kaito/linux-practice/script-test/backup/20261101-025514
+ cp -r /home/kaito/linux-practice/script-test/notes /home/kaito/linux-practice/script-test/backup/20261101-025514
+ echo バックアップしました:/home/kaito/linux-practice/script-test/backup/20261101-025514
バックアップしました:/home/kaito/linux-practice/script-test/backup/20261101-025514++ と2つついている行は、$( ) の中で動いたコマンド(ここでは date)です。
次は、わざと失敗させてみます。コピー元の notes の名前を変えてから動かします。
- 1
- 2
- 3
- 4
mv notes notes-old
bash -x backup-notes.sh
echo "終わったときの番号:$?"
mv notes-old notes(例)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
kaito@demo:~/linux-practice/script-test$ mv notes notes-old
kaito@demo:~/linux-practice/script-test$ bash -x backup-notes.sh
+ set -e
+ SRC=/home/kaito/linux-practice/script-test/notes
++ date +%Y%m%d-%H%M%S
+ DEST=/home/kaito/linux-practice/script-test/backup/20261101-025516
+ mkdir -p /home/kaito/linux-practice/script-test/backup/20261101-025516
+ cp -r /home/kaito/linux-practice/script-test/notes /home/kaito/linux-practice/script-test/backup/20261101-025516
cp: cannot stat '/home/kaito/linux-practice/script-test/notes': No such file or directory
kaito@demo:~/linux-practice/script-test$ echo "終わったときの番号:$?"
終わったときの番号:1
kaito@demo:~/linux-practice/script-test$ mv notes-old notes日本語の環境では、cp: '…/notes' を stat できません: そのようなファイルやディレクトリはありません のように表示されます。
set -e のおかげで、cp が失敗したところで止まり、「バックアップしました」とうそのメッセージが出ずにすみました。$? は「直前のコマンドが、うまくいったか」の番号で、0 なら成功、それ以外は失敗です。
よくあるつまずき:「required file not found」(bad interpreter)と出る
Windows のメモ帳などで書いたスクリプトを Linux で動かすと、次のようなエラーが出ることがあります。練習用のファイルでわざと起こしてみましょう。
- 1
- 2
- 3
printf '#!/bin/bash\r\necho "Windowsで書いたファイル"\r\n' > crlf.sh
chmod u+x crlf.sh
./crlf.sh(例)
- 1
- 2
- 3
- 4
kaito@demo:~/linux-practice/script-test$ printf '#!/bin/bash\r\necho "Windowsで書いたファイル"\r\n' > crlf.sh
kaito@demo:~/linux-practice/script-test$ chmod u+x crlf.sh
kaito@demo:~/linux-practice/script-test$ ./crlf.sh
-bash: ./crlf.sh: cannot execute: required file not found「実行に必要なファイルが見つからない」というエラーです。日本語の環境では -bash: ./crlf.sh: 実行できません: 必要なファイルがありません と表示されます。古い bash では、/bin/bash^M: bad interpreter: No such file or directory という表示になります。
原因は、改行の書き方のちがいです。Windows は行の終わりに \r\n、Linux は \n を使います。よけいな \r(画面では ^M)がついていると、Linux は「/bin/bash\r という名前のプログラム」を探してしまいます。sed(=文字の置きかえや行の削除をするコマンド)で \r を消せば直ります。
- 1
- 2
sed -i 's/\r$//' crlf.sh
./crlf.sh(例)
- 1
- 2
- 3
kaito@demo:~/linux-practice/script-test$ sed -i 's/\r$//' crlf.sh
kaito@demo:~/linux-practice/script-test$ ./crlf.sh
Windowsで書いたファイルVS Code や Cursor でスクリプトを書くときは、画面の右下に「CRLF」「LF」と表示されています。シェルスクリプトは「LF」にしておくと、このエラーを防げます。
ネットの手順で、curl … | bash のように、ダウンロードしたスクリプトをそのまま実行する書き方を見かけることがあります。また、人からもらったスクリプトに sudo(=許可されたユーザーが、コマンドの前につけて、そのコマンドだけを管理者の権限で実行するしくみ)や rm -rf が入っていることもあります。中身を読まずに動かすと、ファイルが消えたり、知らない設定が入ったりするおそれがあります。この記事では試しません。動かす前に cat や less で中身を読み、わからない行は下のプロンプトで AI に聞いてから動かしましょう。
🤖 詰まったらAIに聞こう:スクリプトの中身を読み解きたい・エラーを直したい
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
Linuxの初心者です。シェルスクリプトについて教えてください。
次のスクリプトが何をしているのかを、1行ずつやさしく説明してください。
エラーが出ている場合は、原因と直し方もお願いします。
ファイルを消したり、システムの設定を変えたりする行があれば、教えてください。
【使っている環境】WSLのUbuntu など
【スクリプトの中身(秘密情報は***に置き換え済み)】ここに貼る
【動かし方】例:./backup-notes.sh
【表示されたエラー、または bash -x の結果】ここに貼る自動化のスクリプトには、APIキー、トークン、パスワード、サーバーのアドレスを直接書いてしまいがちです。APIキーやトークンは、Web のサービスを使うときに本人の代わりになる、合言葉のような文字列です。AI に貼る前に、それらを必ず「***」に置き換えてください。bash -x の結果にも、変数の中身がそのまま表示されるので同じように注意しましょう。
よくある質問
Q. ファイル名の最後は、必ず .sh にしないといけませんか?
いいえ。Linux は、ファイル名の最後ではなく、1行目のシバンと実行権限を見て動かします。.sh は「シェルスクリプトです」と人にわかりやすくするための目印です。前の記事の hello-kaito のように、自分のコマンドとして使うものは、.sh をつけないこともよくあります。
Q. sh hello.sh で動かしてもいいですか?
Debian や Ubuntu の sh は、bash ではなく、もっと小さなシェル(dash)です。sh ファイル名 で動かすと、1行目の #!/bin/bash は使われず、dash が読みます。この記事のくらいの中身なら動きますが、bash だけの書き方が入ると、エラーになることがあります。この連載では、bash ファイル名 か ./ファイル名 で動かしましょう。
Q. set -e を書いておけば、失敗はすべて止められますか?
すべてではありません。たとえば if の条件の中で失敗したコマンドや、パイプ(|)の途中で失敗したコマンドでは、止まらないことがあります。set -e は「ふつうに並べたコマンドが失敗したら止まる」安全のための1行、と考えておき、大事なところは bash -x やメッセージで確かめるのが安心です。
まとめ
- シェルスクリプトは、コマンドを上から順番に書いたファイル
- 1行目の
#!/bin/bashは「bash に読ませてね」という印(シバン) -
bash ファイル名で動かすか、chmod u+xして./ファイル名で動かす -
$(date +%Y%m%d)で、日付をファイル名に入れられる -
set -eで、失敗したところで止められる。$?が 0 なら成功 - うまくいかないときは
bash -xで1行ずつ確かめる - 「required file not found」(古い bash では「bad interpreter」)は改行のちがい。
sed -i 's/\r$//'で直す
次の記事では、今回少しだけ使った変数と、条件で動きを変える if、くり返す for を使って、スクリプトをもっと便利にしていきます。
次に読む記事
関連する記事
- Pythonで毎日の作業を自動化する入門|ダウンロードフォルダを自動で整理しよう:ファイルの整理を Python で自動化してみたい方に
- 自動化スタック総覧|GAS・n8n・Python・Notionの使い分け:シェルスクリプトと、ほかの自動化ツールの使い分けを知りたい方に
- Cursorにエラーを直してもらう5ステップ|エラーの渡し方・頼み方・直った後の確認まで:スクリプトのエラーを AI に上手に渡したい方に