Linuxで「動かない」ときの切り分けフロー|権限・PATH・ログ・ネットワークの順に見る【初心者向け】

前の記事では、サーバーを守る4つの柱を確かめました。今回は、ここまで学んだことを全部つなげて、「動かない」ときにどこから見ればいいかの切り分けフローを作ります。

自動化を始めたばかりのころ、スクリプトが動かないたびに、思いついたところを手当たりしだいに直していました。権限を変え、パスを書きかえ、再起動して、それでもだめで、最後は何を変えたのかもわからなくなる。そんなことのくり返しでした。あるとき「エラーの文を読む → 権限 → PATH → 場所 → ログ → ネットワーク」と順番を決めてからは、迷う時間がぐっと減りました。

この記事では、わざと4つの問題を仕込んだスクリプトを用意し、フローにそって1つずつ直していきます。全部、練習用のディレクトリの中だけで行います。

💡
この記事でわかること
  • 「動かない」ときに見る順番(エラーの文 → 権限 → PATH → 場所 → ログ → ネットワーク)
  • エラーの文と終了コード($?)から、原因の場所をしぼる方法
  • bash -x で、スクリプトのどこで止まったかを見る方法
  • 実行する場所が変わると動かなくなる理由と、その直し方
  • 1つずつ変えて、そのたびに確かめる、という進め方
🔰
この記事の前提
  • chmod・PATH・シェルスクリプトの基本がわかる
  • journalctl・curl -v・ss を使ったことがある
  • sed -i.bak でファイルを書きかえたことがある

まだの方は、前の記事「Linuxサーバーのセキュリティ基礎|最小権限・更新・fail2ban・ログ確認【初心者向け】」から読むのがおすすめです。PATH については「Linuxの環境変数とPATH|「command not found」の正体と.bashrc・alias」でくわしく説明しています。

💡
用語メモ
  • 変数:値を入れておく、名前つきの箱
  • 権限:誰が何をしてよいかの決まり。ファイルの場合は「持ち主・グループ・その他の人」ごとに、読む・書く・実行するを決め、これをパーミッションと言います
  • PATH:コマンドの本体を探しにいくディレクトリを :(コロン)で区切って並べた環境変数。左から順に探します
  • ログ:プログラムが残す「いつ、何が起きたか」の記録

用語で迷ったら、連載のまとめ記事の「よく出てくる用語ミニ辞典」でも確かめられます。


結論:エラーの文を読んでから、外側に向かって1つずつ

順番 よく出るエラーの文 疑う場所 確かめるコマンド
① 読む (すべて) エラーの文そのもの echo $?
② 権限 Permission denied 実行権限・持ち主 ls -l、id
③ PATH command not found コマンドの場所 type、echo $PATH
④ 場所 No such file or directory 今いる場所・ファイル名 pwd、ls
⑤ ログ (何も出ずに止まる) どこで止まったか bash -x、journalctl
⑥ ネットワーク Connection refused など 相手が待っているか curl -v、ss -tln
⑦ 最近の変更 (昨日まで動いていた) 最後に変えたこと history、ls -lt

「動かない」ときの切り分けフロー

準備:問題を仕込んだスクリプトを作る

練習用のディレクトリに、自作のコマンド kaito-hello と、それを使うスクリプト run.sh を作ります。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
mkdir -p ~/linux-practice/trouble-test/bin
cd ~/linux-practice/trouble-test
printf '%s\n' '#!/bin/bash' 'echo "kaito-hello が動きました"' > bin/kaito-hello
chmod u+x bin/kaito-hello
printf '%s\n' '#!/bin/bash' 'echo "開始"' 'kaito-hello' 'wc -l data.csv' 'curl -s --max-time 3 http://127.0.0.1:8000/ > /dev/null || echo "サーバーにつながりません" >&2' 'echo "終了"' > run.sh
cat run.sh
ls -l run.sh bin/kaito-hello

🖥️
この記事の出力例について

ここから先の出力例は、Debian 13での実行例です(ユーザー名 kaito・パソコン名 demo のダミー環境)。UbuntuやWSL(=Windows の中で Linux を動かせる、Microsoft のしくみ)では、表示が少し違うことがあります。

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
kaito@demo:~/linux-practice/trouble-test$ cat run.sh
#!/bin/bash
echo "開始"
kaito-hello
wc -l data.csv
curl -s --max-time 3 http://127.0.0.1:8000/ > /dev/null || echo "サーバーにつながりません" >&2
echo "終了"
kaito@demo:~/linux-practice/trouble-test$ ls -l run.sh bin/kaito-hello
-rwxrw-r-- 1 kaito kaito  50 Nov  1 04:18 bin/kaito-hello
-rw-rw-r-- 1 kaito kaito 174 Nov  1 04:18 run.sh

run.sh は -rw-rw-r-- で x がなく、bin/kaito-hello は -rwxrw-r-- で x があります。検証環境は新しいファイルに「グループも書きこめる」設定(umask 002)なので、まん中が rw- になっています。-rw-r--r--・-rwxr--r-- と表示される環境もあります。


STEP 1:エラーの文を読み、権限を見る

スクリプトを動かしてみます。動かなかったときは、まずエラーの文を最後まで読み、echo $? で終了コードを見ます。

  • 1
  • 2
./run.sh
echo $?

(例)

  • 1
  • 2
  • 3
  • 4
kaito@demo:~/linux-practice/trouble-test$ ./run.sh
-bash: ./run.sh: Permission denied
kaito@demo:~/linux-practice/trouble-test$ echo $?
126

エラーの文は -bash: ./run.sh: Permission denied で、終了コードは 126(実行する権限がない)です。先頭の -bash: は「bash(ログインしたときのシェル)が出したエラー」という意味です。環境によっては bash: と表示されます。日本語の環境では、Permission denied の部分が「許可がありません」と表示されます。

終了コードは、コマンドの結果を表す数字です。0 なら成功、それ以外は失敗です。よく出るものを覚えておくと、手がかりになります。

終了コード 意味
0 成功
1 一般的な失敗
126 実行する権限がない
127 コマンドが見つからない

「Permission denied」なので、権限を見ます。

  • 1
  • 2
  • 3
  • 4
  • 5
ls -l run.sh
chmod u+x run.sh
ls -l run.sh
./run.sh
echo $?

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
kaito@demo:~/linux-practice/trouble-test$ ls -l run.sh
-rw-rw-r-- 1 kaito kaito 174 Nov  1 04:18 run.sh
kaito@demo:~/linux-practice/trouble-test$ chmod u+x run.sh
kaito@demo:~/linux-practice/trouble-test$ ls -l run.sh
-rwxrw-r-- 1 kaito kaito 174 Nov  1 04:18 run.sh
kaito@demo:~/linux-practice/trouble-test$ ./run.sh
開始
./run.sh: line 3: kaito-hello: command not found
wc: data.csv: No such file or directory
サーバーにつながりません
終了
kaito@demo:~/linux-practice/trouble-test$ echo $?
0

-rw-rw-r-- が -rwxrw-r-- に変わり、自分(左の3文字)に x がつきました。日本語の環境では、command not found は「コマンドが見つかりません」、No such file or directory は「そのようなファイルやディレクトリはありません」と表示されます。

Debian 13での実行画面

権限の問題は直りましたが、今度はエラーが3つ出ました。しかも、終了コードは 0(成功)です。シェルスクリプトは、途中でエラーが出ても最後まで進み、最後のコマンドの結果を終了コードにするからです。「終了コードが 0 だから大丈夫」とは限らないことも覚えておきましょう。

エラーは、上から1つずつ直します。


STEP 2:PATH を見る(command not found)

  • 1
  • 2
  • 3
type kaito-hello
echo $PATH
ls -l bin/kaito-hello

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
kaito@demo:~/linux-practice/trouble-test$ type kaito-hello
-bash: type: kaito-hello: not found
kaito@demo:~/linux-practice/trouble-test$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
kaito@demo:~/linux-practice/trouble-test$ ls -l bin/kaito-hello
-rwxrw-r-- 1 kaito kaito 50 Nov  1 04:18 bin/kaito-hello

type のエラーも、先頭は -bash: です。PATH には /usr/local/bin・/usr/bin・/bin などが入っていますが、練習用の bin は入っていません。PATH の中身は、環境によってちがいます。

ファイルはあるのに、PATH(コマンドを探す場所のリスト)に bin が入っていないので見つかりません。ここでは、スクリプトの中でフルパス(絶対パス)で呼ぶように直します。sed・awk(=行を列に分けて、集計や取り出しをするコマンド)の記事で学んだ sed -i.bak を使います。

  • 1
  • 2
  • 3
sed -i.bak 's|^kaito-hello$|"$HOME/linux-practice/trouble-test/bin/kaito-hello"|' run.sh
diff run.sh.bak run.sh
./run.sh

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
kaito@demo:~/linux-practice/trouble-test$ sed -i.bak 's|^kaito-hello$|"$HOME/linux-practice/trouble-test/bin/kaito-hello"|' run.sh
kaito@demo:~/linux-practice/trouble-test$ diff run.sh.bak run.sh
3c3
< kaito-hello
---
> "$HOME/linux-practice/trouble-test/bin/kaito-hello"
kaito@demo:~/linux-practice/trouble-test$ ./run.sh
開始
kaito-hello が動きました
wc: data.csv: No such file or directory
サーバーにつながりません
終了

3c3 は「3行目が変わった」という意味です。

s|…|…| は、区切りに / のかわりに | を使った置き換えです。置き換える文字に / が入っているときに便利です。

cron(=決まった日時に、コマンドをくり返し自動で実行してくれるしくみ)や systemd から動かすスクリプトは、ターミナルとは PATH がちがうことがよくあります。「ターミナルでは動くのに、自動で動かすと command not found」というときは、まずフルパス(=いちばん上の / から書いた場所)で書くのが確実です。

sudo をつけたときも、PATH が変わります。セキュリティ基礎の記事の sudo -l の結果にあった secure_path=… が、sudo のときに使われる PATH です。「sudo をつけたら command not found」というときも、フルパスで書くか、type コマンド名 で場所を確かめてから、その場所を書きましょう。


STEP 3:場所を見る(No such file or directory)

  • 1
  • 2
  • 3
  • 4
pwd
ls
echo "name,price" > data.csv
./run.sh

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
kaito@demo:~/linux-practice/trouble-test$ pwd
/home/kaito/linux-practice/trouble-test
kaito@demo:~/linux-practice/trouble-test$ ls
bin  run.sh  run.sh.bak
kaito@demo:~/linux-practice/trouble-test$ echo "name,price" > data.csv
kaito@demo:~/linux-practice/trouble-test$ ./run.sh
開始
kaito-hello が動きました
1 data.csv
サーバーにつながりません
終了

直ったように見えますが、もう1つ落とし穴があります。別の場所から動かしてみましょう。

  • 1
  • 2
  • 3
cd ~
~/linux-practice/trouble-test/run.sh
cd ~/linux-practice/trouble-test

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
kaito@demo:~/linux-practice/trouble-test$ cd ~
kaito@demo:~$ ~/linux-practice/trouble-test/run.sh
開始
kaito-hello が動きました
wc: data.csv: No such file or directory
サーバーにつながりません
終了
kaito@demo:~$ cd ~/linux-practice/trouble-test

data.csv は「今いる場所の data.csv」という意味なので、ホームから動かすとホームの中を探してしまいます。スクリプトの最初で「スクリプトがある場所に移動する」1行を足して直します。

  • 1
  • 2
  • 3
  • 4
  • 5
sed -i.bak2 '2i cd "$(dirname "$0")"' run.sh
head -n 3 run.sh
cd ~
~/linux-practice/trouble-test/run.sh
cd ~/linux-practice/trouble-test

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
kaito@demo:~/linux-practice/trouble-test$ sed -i.bak2 '2i cd "$(dirname "$0")"' run.sh
kaito@demo:~/linux-practice/trouble-test$ head -n 3 run.sh
#!/bin/bash
cd "$(dirname "$0")"
echo "開始"
kaito@demo:~/linux-practice/trouble-test$ cd ~
kaito@demo:~$ ~/linux-practice/trouble-test/run.sh
開始
kaito-hello が動きました
1 data.csv
サーバーにつながりません
終了
kaito@demo:~$ cd ~/linux-practice/trouble-test

2i は「2行目の前に挿入する」、$(dirname "$0") は「このスクリプトが置いてあるフォルダ」という意味です。


STEP 4:bash -x とログで「どこで止まったか」を見る

エラーの文が出ない、または流れて見えないときは、bash -x で1行ずつの動きを見ます。

  • 1
bash -x ./run.sh

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
kaito@demo:~/linux-practice/trouble-test$ bash -x ./run.sh
++ dirname ./run.sh
+ cd .
+ echo 開始
開始
+ /home/kaito/linux-practice/trouble-test/bin/kaito-hello
kaito-hello が動きました
+ wc -l data.csv
1 data.csv
+ curl -s --max-time 3 http://127.0.0.1:8000/
+ echo サーバーにつながりません
サーバーにつながりません
+ echo 終了
終了

最初の ++ dirname ./run.sh は、$(dirname "$0") の中で動いたコマンドです(+ が2つなのは、$( ) の中だからです)。その結果が .(今いる場所)だったので、次の行は + cd . になっています。+ のない行は、コマンドが画面に出した結果です。

Debian 13での実行画面

+ のついた行が、実際に実行されたコマンドです。どこまで進んで、どこで失敗したかがひと目でわかります。

自動で動かすスクリプトは、画面がないので、結果をログに残すことが大切です。ここでは journal に書きこんでみます。

  • 1
  • 2
./run.sh 2>&1 | logger -t kaito-run
journalctl -t kaito-run -n 10 --no-pager

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
kaito@demo:~/linux-practice/trouble-test$ ./run.sh 2>&1 | logger -t kaito-run
kaito@demo:~/linux-practice/trouble-test$ journalctl -t kaito-run -n 10 --no-pager
Hint: You are currently not seeing messages from other users and the system.
      Users in groups 'adm', 'systemd-journal' can see all messages.
      Pass -q to turn off this notice.
Nov 01 04:19:13 demo kaito-run[1533]: 開始
Nov 01 04:19:13 demo kaito-run[1533]: kaito-hello が動きました
Nov 01 04:19:13 demo kaito-run[1533]: 1 data.csv
Nov 01 04:19:13 demo kaito-run[1533]: サーバーにつながりません
Nov 01 04:19:13 demo kaito-run[1533]: 終了

最初の3行の Hint: は、「一般ユーザーなので、ほかのユーザーやシステムのメッセージは見えていません」という案内です(エラーではありません)。自分で書いたログは、そのまま読めます。

2>&1 で、エラーの出力もいっしょにログへ送っています。


STEP 5:ネットワークを見る(サーバーにつながりません)

最後のエラーは、127.0.0.1:8000 につながらないことです。「つながらない」ときの記事で学んだ順番で確かめます。

  • 1
  • 2
curl -v --max-time 3 http://127.0.0.1:8000/
ss -tln | grep 8000

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
kaito@demo:~/linux-practice/trouble-test$ curl -v --max-time 3 http://127.0.0.1:8000/
*   Trying 127.0.0.1:8000...
* connect to 127.0.0.1 port 8000 from 127.0.0.1 port 41698 failed: Connection refused
* Failed to connect to 127.0.0.1 port 8000 after 0 ms: Could not connect to server
* closing connection #0
curl: (7) Failed to connect to 127.0.0.1 port 8000 after 0 ms: Could not connect to server
kaito@demo:~/linux-practice/trouble-test$ ss -tln | grep 8000

最後の行は curl: (7) … Could not connect to server です。ss -tln | grep 8000 は何も表示されませんでした。

「Connection refused」で、8000番で誰も待っていないので、相手のサーバーが止まっているとわかります。もう1つターミナルを開いて、練習用の Web サーバーを動かします。

  • 1
  • 2
cd ~/linux-practice/trouble-test
python3 -m http.server 8000 --bind 127.0.0.1

(例)

  • 1
  • 2
  • 3
kaito@demo:~$ cd ~/linux-practice/trouble-test
kaito@demo:~/linux-practice/trouble-test$ python3 -m http.server 8000 --bind 127.0.0.1
Serving HTTP on 127.0.0.1 port 8000 (http://127.0.0.1:8000/) ...

このあと元のターミナルで ./run.sh を動かすと、こちらに "GET / HTTP/1.1" 200 のようなアクセスの記録が1行出ます。

元のターミナルに戻って、もう一度動かします。

  • 1
  • 2
./run.sh
echo $?

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
kaito@demo:~/linux-practice/trouble-test$ ./run.sh
開始
kaito-hello が動きました
1 data.csv
終了
kaito@demo:~/linux-practice/trouble-test$ echo $?
0

4つの問題が、すべて直りました。

💡
直したら、次は「失敗したら、失敗と返す」スクリプトにする

STEP 1 で見たとおり、この run.sh は、エラーが3つ出ても終了コードが 0 でした。これでは、cron や systemd から動かしたときに「成功」とみなされ、次の記事の死活監視(=サーバーが動いているかを、外から定期的に確かめること)でも気づけません。直し方の考え方は、次の2つです。

  • 失敗したら止める:スクリプトの2行目あたりに set -e と書くと、失敗したコマンドのところで止まり、そのコマンドの終了コードを返します。ただし、grep で何も見つからないときなど、「失敗あつかいだけど止まってほしくない」場面でも止まるので、入れたら一度 bash -x で動きを確かめましょう。反対に、| でつないだ前のほうのコマンドや、if の条件の中で失敗したときは止まりません(下の検証)。
  • 知らせたうえで、失敗を返す:curl … || echo "サーバーにつながりません" >&2 は、メッセージを出したあと成功あつかいで先に進みます。|| { echo "サーバーにつながりません" >&2; exit 1; } のように exit 1 を足すと、終了コードで失敗を伝えられます(ここでは一例です)。

もう1つ、STEP 4 の ./run.sh 2>&1 | logger -t kaito-run のように | でつないだときの echo $? は、最後の logger の結果になります。run.sh が失敗しても 0 になるので、スクリプトの結果を見たいときは、| をつけずに動かしてから echo $? を見ます。

set -e と exit 1 の動きを、実際に試す(検証)

検証では、3行ほどの小さなスクリプトを printf で作って、set -e の止まり方を比べました。printf '%s\n' … は、'…' の1つずつを1行にしてファイルに書く、という意味です。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
printf '%s\n' '#!/bin/bash' 'set -e' 'echo start' 'ls /nosuch-dir' 'echo end' > e1.sh
bash e1.sh; echo $?
printf '%s\n' '#!/bin/bash' 'set -e' 'echo start' 'grep kaito /etc/hostname' 'echo end' > e2.sh
bash e2.sh; echo $?
bash -x e2.sh; echo $?
printf '%s\n' '#!/bin/bash' 'set -e' 'false | true' 'if ls /nosuch-dir; then echo found; fi' 'echo end' > e3.sh
bash e3.sh; echo $?

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
kaito@demo:~/linux-practice/trouble-test$ printf '%s\n' '#!/bin/bash' 'set -e' 'echo start' 'ls /nosuch-dir' 'echo end' > e1.sh
kaito@demo:~/linux-practice/trouble-test$ bash e1.sh; echo $?
start
ls: cannot access '/nosuch-dir': No such file or directory
2
kaito@demo:~/linux-practice/trouble-test$ printf '%s\n' '#!/bin/bash' 'set -e' 'echo start' 'grep kaito /etc/hostname' 'echo end' > e2.sh
kaito@demo:~/linux-practice/trouble-test$ bash e2.sh; echo $?
start
1
kaito@demo:~/linux-practice/trouble-test$ bash -x e2.sh; echo $?
+ set -e
+ echo start
start
+ grep kaito /etc/hostname
1
kaito@demo:~/linux-practice/trouble-test$ printf '%s\n' '#!/bin/bash' 'set -e' 'false | true' 'if ls /nosuch-dir; then echo found; fi' 'echo end' > e3.sh
kaito@demo:~/linux-practice/trouble-test$ bash e3.sh; echo $?
ls: cannot access '/nosuch-dir': No such file or directory
end
0

e1.sh は、ls が失敗したところで止まり、end は出ないまま、ls の終了コード 2 を返しました。e2.sh は、grep が何も見つけられなかった(終了コード 1)だけで止まっています。エラーの文が1つも出ないので、bash -x で見ると、+ grep kaito /etc/hostname のところで止まったことがわかります。e3.sh は、false | true(前のほうが失敗)と if ls …(条件の中で失敗)では止まらず、end まで進んで 0 を返しました。

Debian 13での実行画面

次は、|| echo と || { …; exit 1; } のちがいと、| でつないだときの echo $? です。ポート(=1台のコンピューターの中で、どのサービスにつなぐかを分ける番号) 8000 では何も動いていない状態で試しました。

  • 1
  • 2
  • 3
  • 4
  • 5
printf '%s\n' '#!/bin/bash' 'curl -s --max-time 3 http://127.0.0.1:8000/ > /dev/null || echo "サーバーにつながりません" >&2' 'echo "続き"' > f1.sh
bash f1.sh; echo $?
printf '%s\n' '#!/bin/bash' 'curl -s --max-time 3 http://127.0.0.1:8000/ > /dev/null || { echo "サーバーにつながりません" >&2; exit 1; }' 'echo "続き"' > f2.sh
bash f2.sh; echo $?
false | logger -t t; echo $?

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
kaito@demo:~/linux-practice/trouble-test$ printf '%s\n' '#!/bin/bash' 'curl -s --max-time 3 http://127.0.0.1:8000/ > /dev/null || echo "サーバーにつながりません" >&2' 'echo "続き"' > f1.sh
kaito@demo:~/linux-practice/trouble-test$ bash f1.sh; echo $?
サーバーにつながりません
続き
0
kaito@demo:~/linux-practice/trouble-test$ printf '%s\n' '#!/bin/bash' 'curl -s --max-time 3 http://127.0.0.1:8000/ > /dev/null || { echo "サーバーにつながりません" >&2; exit 1; }' 'echo "続き"' > f2.sh
kaito@demo:~/linux-practice/trouble-test$ bash f2.sh; echo $?
サーバーにつながりません
1
kaito@demo:~/linux-practice/trouble-test$ false | logger -t t; echo $?
0

f1.sh はメッセージを出したあと 続き まで進み、終了コードは 0(成功)でした。exit 1 を足した f2.sh は、そこで止まって 1 を返しました。最後の false | logger -t t は、前の false が失敗しているのに 0 になっています。これが、上の箱で説明した「最後の logger の結果になる」ということです。

Debian 13での実行画面
🚫
切り分けのときに、やってはいけないこと(文章だけで説明します)

動かないからといって、sudo をつけて押し切ったり、chmod 777 にしたりするのはやめましょう。動いたように見えても、原因は残ったままで、別の問題(自分で消せないファイルや、誰でも書きかえられるファイル)が増えます。サーバーを何度も再起動するのも、ログが流れて原因の手がかりが消えることがあります。そして、一度に2つ以上を変えないことが大切です。何が効いたのかわからなくなり、別のところを壊していても気づけません。

最後の手段が「⑦ 最近変えたこと」です。昨日まで動いていたなら、その間に何かが変わっています。history | tail -n 30 で自分の操作を、ls -lt で最近更新したファイルを、/var/log/apt/history.log で入れたソフトを確かめましょう。

🤖 詰まったらAIに聞こう:どこから調べればいいかわからない

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
Linuxの初心者です。スクリプト(またはサービス)が動かなくて困っています。
「エラーの文 → 権限 → PATH → 場所 → ログ → ネットワーク → 最近の変更」の順で、
どこが原因になっていそうかと、次に打つコマンドを1つずつ教えてください。
一度に複数の設定を変える方法ではなく、1つずつ確かめる方法でお願いします。

【動かないもの】例:毎朝cronで動かすバックアップのスクリプト
【実行したコマンド】ここに貼る
【エラーの文と echo $? の結果】ここに貼る
【bash -x の結果(長い場合は止まった前後だけ)】ここに貼る
【最近変えたこと】例:スクリプトを別のフォルダに移した

🔒
AIに貼る前に:秘密情報やパスワードは貼らない

bash -x の結果には、変数の中身がそのまま表示されます。スクリプトの中で API キーやパスワード、Webhook(=何かが起きたときに、決めた URL へ自動で知らせを送るしくみ)の URL を使っていると、それも全部出てしまいます。貼る前に必ず見直して、「***」に置き換えてください。echo $PATH やファイルのパスに入っている、自分の名前や会社名も伏せておきましょう。


後片づけ

もう1つのターミナルで Ctrl + C を押して、Web サーバーを止めます。そのあと、元のターミナルで次を打ちます。

  • 1
  • 2
  • 3
cd ~/linux-practice
rm -r trouble-test
ls

もう1つのターミナルで Ctrl + C を押すと、次のように表示されてサーバーが止まります。

(例)

  • 1
  • 2
^C
Keyboard interrupt received, exiting.

(例)

  • 1
  • 2
  • 3
  • 4
kaito@demo:~/linux-practice/trouble-test$ cd ~/linux-practice
kaito@demo:~/linux-practice$ rm -r trouble-test
kaito@demo:~/linux-practice$ ls
archive-test  chmod-test  edit-test  find-test  grep-test  link-test  perm-test  redirect-test

trouble-test がなくなりました。ほかのフォルダは、これまでの記事の練習で作ったものです。

logger で書いたログは journal に残りますが、時間がたつと古いものから自動で整理されるので、そのままで大丈夫です。


よくある質問

Q. Windows で作ったスクリプトを動かすと、bad interpreter や required file not found と出ます。

Windows のメモ帳などで保存すると、行の終わりに見えない文字(\r、CRLF)がつくことがあります。すると、1行目の #!/bin/bash が「/bin/bash のうしろに見えない文字がついた名前」になり、見つかりません。検証環境(Debian 13、bash 5.2.37)では、-bash: ./run.sh: cannot execute: required file not found と出て、終了コードは 127 でした。bad interpreter という文で出る環境もあります(検証では確かめていません)。sed・awk の記事の sed -i.bak を使い、sed -i.bak 's/\r$//' run.sh で見えない文字を消せます。WSL で Windows 側のフォルダのファイルを使うときにも起きやすいので、気をつけましょう。

検証では、printf の \r\n で、行の終わりに見えない文字がついた run.sh をわざと作って試しました(記事の練習が終わったあとに行ったので、練習で使った run.sh は上書きしています。試すなら、練習が終わってからにしましょう)。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
printf '#!/bin/bash\r\necho "hello"\r\n' > run.sh
chmod u+x run.sh
./run.sh; echo $?
cat -A run.sh
sed -i.bak 's/\r$//' run.sh
cat -A run.sh
./run.sh; echo $?
ls

(例)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
kaito@demo:~/linux-practice/trouble-test$ printf '#!/bin/bash\r\necho "hello"\r\n' > run.sh
kaito@demo:~/linux-practice/trouble-test$ chmod u+x run.sh
kaito@demo:~/linux-practice/trouble-test$ ./run.sh; echo $?
-bash: ./run.sh: cannot execute: required file not found
127
kaito@demo:~/linux-practice/trouble-test$ cat -A run.sh
#!/bin/bash^M$
echo "hello"^M$
kaito@demo:~/linux-practice/trouble-test$ sed -i.bak 's/\r$//' run.sh
kaito@demo:~/linux-practice/trouble-test$ cat -A run.sh
#!/bin/bash$
echo "hello"$
kaito@demo:~/linux-practice/trouble-test$ ./run.sh; echo $?
hello
0
kaito@demo:~/linux-practice/trouble-test$ ls
e1.sh  e2.sh  e3.sh  f1.sh  f2.sh  run.sh  run.sh.bak

cat -A は、見えない文字も記号で表示するコマンドです。行の終わりの ^M が見えない文字(\r)で、$ が行の終わりです。sed -i.bak のあとは ^M が消え、hello と表示されて終了コードは 0 になりました。元のファイルは run.sh.bak として残っています。

Debian 13での実行画面

Q. エラーの文が長すぎて、どこを読めばいいかわかりません。

まずいちばん最後の行と、いちばん最初のエラーの行を見ます。多くのエラーは、最初の1つが原因で、あとのエラーはその結果です。この記事の STEP 1 で、3つのエラーを上から1つずつ直したのと同じ考え方です。それでもわからないときは、この記事の「詰まったらAIに聞こう」の形で、伏せ字にしてから聞きましょう。

Q. フローの順番どおりに見ないといけませんか?

いいえ。エラーの文がはっきりしていれば(たとえば Permission denied)、その段から見てかまいません。順番は「何から見ればいいかわからないとき」の道しるべです。ただし、どの段から始めても「1つ変えたら、1回確かめる」は守りましょう。


まとめ

  • まずエラーの文を最後まで読む。echo $? で終了コードも見る(126 は権限、127 はコマンドなし)
  • Permission denied は権限、command not found は PATH、No such file は場所を疑う
  • 自動で動かすスクリプトは、フルパスと cd "$(dirname "$0")" で、場所や PATH に左右されないようにする
  • 止まった場所は bash -x とログで見る
  • refused や timed out は、「つながらない」ときの記事の順番でネットワークを調べる
  • 1つ変えたら、1回確かめる。sudo や chmod 777 で押し切らない

次の記事は、Lv5 の最後です。サーバーが止まったら知らせるしくみ(死活監視)を作り、n8n で通知する方法を学びます。


次に読む記事


関連する記事