Linux Bash スクリプトでファイルが存在するかどうかを確認する方法

Linux Bash スクリプトが特定のファイルやディレクトリが存在していることを前提としている場合、それらが存在していると想定することはできません。それらが確実に存在することを確認する必要があります。その方法を説明します。

何も想定しない

スクリプトを記述する場合、コンピュータに存在するものと存在しないものについて仮定することはできません。そのスクリプトが配布されてさまざまなコンピュータで実行される場合は、なおさらです。遅かれ早かれ、スクリプトは想定に合わないコンピュータで実行され、スクリプトは失敗するか予測不能な動作をします。

コンピュータで価値があるものや作成したものはすべて何らかの形式のファイルに保存され、それらのファイルはすべてディレクトリにあります。スクリプトは、コマンドラインで行えるすべての操作(ファイルやディレクトリの読み取り、書き込み、名前の変更、削除、移動)を実行できます。

人間としての利点は、ディレクトリの内容を確認でき、ファイルが存在するかどうか、または想定されたディレクトリが存在するかどうかがわかることです。ファイル操作中にスクリプトが失敗すると、深刻で有害な結果を招く可能性があります。

Bash は、ファイルとディレクトリを検出し、多くの属性をテストするために使用できる包括的なテストを提供しています。これらをスクリプトに組み込むのは簡単ですが、堅牢性と詳細な制御の点で得られる利点は大きくなります。

テストの範囲

if 文と、多数のファイルとディレクトリのテストから適切なテストを組み合わせることで、ファイルが存在するかどうか、実行可能かどうか、書き込み可能かどうかなどを簡単に判断できます。

  • -b: ファイルがブロック特殊ファイルの場合に true を返します。
  • -c: ファイルが文字特殊ファイルの場合に true を返します。
  • -d: 「ファイル」がディレクトリの場合に true を返します。
  • -e: ファイルが存在する場合に true を返します。
  • -f: ファイルが存在し、通常のファイルである場合に true を返します。
  • -g: ファイルに
    <code>setgid</code>
    パーミッション セットがある場合に true を返します(
    <code>chmod g+</code>
    )。
  • -h: ファイルがシンボリックリンクの場合に true を返します。
  • -L: ファイルがシンボリックリンクの場合に true を返します。
  • -k: スティッキービットが設定されている場合に true を返します(
    <code>chmod +t</code>
    )。
  • -p: ファイルが名前付きパイプの場合に true を返します。
  • -r: ファイルが読み取り可能の場合に true を返します。
  • -s: ファイルが存在し、空でない場合に true を返します。
  • -S: ファイルがソケットの場合に true を返します。
  • -t: ファイル記述子がターミナルで開かれている場合に true を返します。
  • -u: ファイルに
    <code>setuid</code>
    パーミッション セットがある場合に true を返します(
    <code>chmod u+</code>
    )。
  • -w: ファイルが書き込み可能の場合に true を返します。
  • -x: ファイルが実行可能の場合に true を返します。
  • -O: 自分自身が所有している場合に true を返します。
  • -G: 自分のグループが所有している場合に true を返します。
  • -N: ファイルが最後に読み取られてから変更されている場合に true を返します。
  • !: 論理 NOT 演算子。
  • &&: 論理 AND 演算子。
  • ||: 論理 OR 演算子。

リストは

<code>-b</code>
から始まります。これは
<code>-a</code>
テストが非推奨となり、
<code>-e</code>
テストに置き換えられたためです。

スクリプトでテストを使用する

一般的なファイル テストif文は、単純なスクリプティング構造です。二重角かっこ「

<code>[[ ]]</code>
」内での比較では-fテストを使用して、その名前を持つ通常のファイルが存在するかどうかを判断します。

このスクリプトのテキストをエディタにコピーして「script1.sh」というファイルに保存し、

<code>chmod</code>
を使用して実行可能にします。

#!/bin/bash
if [[ -f $1 ]]
then
  echo "The file $1 exists."
else
  echo "The file $1 cannot be found."
fi

コマンドラインでスクリプトにファイルの名前を渡す必要があります。

chmod +x script1.sh

この記事の他の例を試す場合は、各スクリプトでこれを行う必要があります。

単純なテキスト ファイルでスクリプトを試してみましょう。

./script1.sh test-file.txt

ファイルが存在し、スクリプトはそれを正しく報告します。ファイルを削除してもう一度試すと、テストは失敗し、スクリプトはそれを報告します。

./script1.sh test-file.txt

実際の状況では、スクリプトは適切なアクションを実行する必要があります。エラーにフラグを立てて停止するかもしれません。ファイルを新規作成して続行するかもしれません。不足しているファイルを置き換えるためにバックアップ ディレクトリから何かをコピーするかもしれません。すべてはスクリプトの目的によります。しかし、少なくともスクリプトはファイルが存在するかどうかを認識した上で、決定を下すことができるようになりました。

-fフラグはファイルが存在するかどうかをテストし、「通常の」ファイルです。言い換えると、ファイルのように見えるがファイルではない、デバイス ファイルなどのファイルではありません。

ls を使用して「/dev/random」ファイルが存在することを確認し、スクリプトがそれをどのように認識するかを確認します。

ls -lh /dev/random

./script /dev/random

スクリプトは通常のファイルのテストを行っており、「/dev/random」はデバイス ファイルであるため、テストは失敗します。ほとんどの場合、ファイルが存在するかどうかを突き止めるには、使用するテストを慎重に選択するか、複数のテストを使用する必要があります。

これは通常のファイルと文字デバイス ファイルをテストする「script2.sh」です。

#!/bin/bash
if [[ -f $1 ]]
then
  echo "The file $1 exists."
else
  echo "The file $1 is missing or not a regular file."
fi
if [[ -c $1 ]]
then
  echo "The file $1 is a character device file."
else
  echo "The file $1 is missing or not a special file."
fi

このスクリプトを「/dev/random」デバイス ファイルで実行すると、予想通り最初のテストは失敗し、2 番目のテストは成功します。ファイルをデバイス ファイルとして認識します。

./script2.sh /dev/random

実際には、文字デバイス ファイルとして認識されます。デバイス ファイルにはブロック デバイス ファイルがあります。今のところ、スクリプトはそれらに対応できません。

./script2.sh /dev/sda

論理OR演算子を利用して、2 番目の if 文に別のテストを含めることができます。今回は、ファイルが文字デバイス ファイルかブロック デバイス ファイルかに関係なく、テストは true を返します。これが「script3.sh」です。

#!/bin/bash
if [[ -f $1 ]]
then
  echo "The file $1 exists."
else
  echo "The file $1 is missing or not a regular file."
fi
if [[ -c $1 || -b $1 ]]
then
  echo "The file $1 is a character or block device file."
else
  echo "The file $1 is missing or not a special file."
fi

このスクリプトは、文字デバイス ファイルとブロック デバイス ファイルの両方を認識します。

./script3.sh /dev/random

./script3.sh /dev/sda

さまざまなタイプのデバイス ファイルを区別することが重要な場合は、入れ子になったif文を使用できます。これが「script4.sh」です。

#!/bin/bash
if [[ -f $1 ]]
then
  echo "The file $1 exists."
else
  echo "The file $1 is missing or not a regular file."
fi
if [[ -c $1 ]]
then
  echo "The file $1 is a character device file."
else
  if [[ -b $1 ]]
  then
    echo "The file $1 is a block device file."
  else
    echo "The file $1 is missing or not a device file."
  fi
fi

このスクリプトは、文字デバイス ファイルとブロック デバイス ファイルの両方を認識して分類します。

./script4.sh /dev/random

./script4.sh /dev/sda

論理 AND 演算子を使用すると、一度に複数の特性をテストできます。これが「script5.sh」です。ファイルが存在し、スクリプトがそのファイルに対する読み取り権限と書き込み権限を持っていることを確認します。

#!/bin/bash
if [[ -f $1 && -r $1 && -w $1 ]]
then
  echo "The file $1 exists and we have read/write permissions."
else
  echo "The file $1 is missing, not a regular file, or we can't read/write to it."
fi

スクリプトを自分のものであるファイルとrootのものであるファイルで実行します。

./script5.sh .bashrc

./script5.sh /etc/fstab

ディレクトリが存在するかどうかをテストするには、-dテストを使用します。これが「script6.sh」です。これはバックアップ スクリプトの一部です。最初に、コマンドラインで渡されたディレクトリが存在するかどうかを確認します。if文のテストで論理NOT演算子!を使用します。

#!/bin/bash
if [[ ! -d $1 ]]
then
  echo "Creating backup directory:" $1
  mkdir $1
  if [[ ! $? -eq 0 ]]
  then
    echo "Couldn't create backup directory:" $1
    exit
  fi
else
  echo "Backup directory exists."
fi
# continue with file backup
echo "Backing up to: "$1

ディレクトリが存在しない場合は作成します。ディレクトリの作成に失敗した場合は、スクリプトは終了します。ディレクトリの作成に成功した場合、またはディレクトリがすでに存在する場合は、スクリプトはバックアップ アクションを続行します。

スクリプトを実行し、ls-d(ディレクトリ)オプションを使用してバックアップ ディレクトリが存在するかどうかを確認します。

./script6.sh Documents/project-backup

ls -d Documents/project-backup

バックアップ ディレクトリが作成されました。スクリプトをもう一度実行すると、ディレクトリがすでに存在することが報告されます。

./script6.sh

スクリプトはディレクトリを検出し、バックアップを実行します。

テストする、推測しない

遅かれ早かれ、推測は悪い結果につながります。最初にテストし、それに応じて反応します。

知識は力です。テストを使用して、スクリプトに必要な知識を提供します。