1. 概要

このチュートリアルでは、開いているファイルハンドルを持つファイルを削除、移動、または置換したときのシステムの動作を確認します。

まず、ファイルとiノードについて簡単に説明します。 次に、さまざまなシナリオを実行し、それぞれで何が起こるかを確認します。

2. ファイルとiノードを理解する

Linuxファイルシステムで作業する場合、iノードを使用してファイルに関する情報を格納します。

フォルダ内のファイルを一覧表示すると、iノードへのリンクが表示されます。 iノードには複数のリンクを含めることができ、それらはシンボリックリンクまたはハードリンクにすることができます

ファイルでstatを使用して、それがどのiノードを参照しているかを確認できます。

$ touch inode_example
$ stat inode_example 
  File: inode_example
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: fd03h/64771d    Inode: 20448632    Links: 1

このファイルはiノード20448632を参照しており、statにもリンクが1つあると記載されています。 新しいハードリンクを追加して、inode_examplestatを再実行してみましょう。

$ ln inode_example hardlink_inode_example
$ stat inode_example 
  File: inode_example
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: fd03h/64771d    Inode: 20448632    Links: 2

linksの値が1つ増えていることがわかります。 これで、hardlink_inode_exampleでもstatを使用できるようになりました。

$ stat hardlink_inode_example 
  File: hardlink_inode_example
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: fd03h/64771d    Inode: 20448632    Links: 2

ハードリンクも同じiノード20448632を指していることに注意してください。 hardlink_inode_exampleinode_exampleの両方のファイルは、同じiノードへのハードリンクです。

ハードリンクとは異なり、シンボリックリンクには独自のiノード番号があります。

このすべてを念頭に置いて、ファイル名をiノードのエイリアスと考えることができ、同じiノードに複数のファイルをリンクさせることができます。

3. ファイルの削除

ファイルを開くと、そのファイルを指すファイル記述子が得られます。これはファイルハンドルとも呼ばれます。 削除されたファイルの開いたファイルハンドルを使用できます。 ファイルが存在するかのように、書き込みと読み取りを行うことができます。 ファイル名はファイルシステムに表示されませんが、ファイルハンドルはまだ存在しているiノードを指します

このアイデアをテストするために、remove_opened_file.shというスクリプトを作成してみましょう。

#!/bin/bash

FILE="/tmp/remove_example"

(   sleep 1
    echo "Before rm." >&4
    rm "$FILE"
    if [ ! -e "$FILE" ]; then
        echo "The file $FILE was removed."
    fi
    echo "After rm." >&4
) 4>"$FILE" &

(   sleep 2
    echo "This is the content in file handle 4:"
    cat <&4
) 4<"$FILE"

このスクリプトでは、2つのサブシェルを起動し、リダイレクトを使用して / tmp /remove_exampleをファイルハンドル4として作成して開きます。 ]両方のサブシェルで。 サブシェルが終了すると、システムはファイルハンドルを閉じます。

最初のサブシェル内では、1秒待つことから始めます。 これは、2番目のサブシェルが開始されてファイルが開かれたことを確認するために行います。 次に、ファイル記述子 4 に2行を書き込みます。1つはファイルを削除する前で、もう1つはファイルを削除した後です。 また、最後の行を書き込む前に、ファイルが存在しないことをテストします。 わかりやすくするためにこれを行います。

2番目のサブシェルは2秒間待機することで開始するため、最初のサブシェルがファイルに書き込み、ファイルを削除する間、ファイルは開いたままになります。 その後、ファイルハンドル4の内容を出力します。

それを実行して、何が起こるか見てみましょう:

$ ./remove_opened_file.sh 
The file /tmp/remove_example was removed.
This is the content in file handle 4:
Before rm.
After rm.

ご覧のとおり、 2番目のサブシェルは、ファイルが存在しない場合でも両方の行を出力します この動作は、ファイルを削除すると、実際にはiノードからファイル名のリンクが解除されるためです。 iノードはまだ存在しているので、ファイルを開いたままにしておくと、iノードに書き込んだりiノードから読み取ったりできます。

次の場合、システムはiノードを削除します。

  • それを指すハードリンクはもうありません
  • 開いているファイルハンドルはありません

これには別の効果があります。ファイルを削除しても、それを指すファイルハンドルが開いている場合、空きディスク領域の量は変わりません。

4. ファイルの移動

システムがファイルを移動する方法は2つあります。

  • ファイルの名前を変更する
  • コンテンツを宛先にコピーしてから、ソースを削除します

状況に応じて、システムはいずれかの方法を使用します。 たとえば、ファイルを別のファイルシステムに移動する場合、システムはファイルを宛先にコピーする必要があります。 一方、同じファイルシステムで作業している場合、システムはファイルの名前を変更するだけで済みます。

4.1. ファイルの名前が変更されました

ファイルの名前が変更された場合、このは開いているファイルハンドルには影響しません。 書き込みと読み取りは引き続き可能です。 iノードは同じであるため、ファイルの移動後に書き込むコンテンツは、宛先ファイルに含まれます。

move_opened_file.sh と記述して、開いているファイルを移動し、何が起こるかを見てみましょう。

#!/bin/bash

FILE=/tmp/move_example
FILE_NEW=/tmp/move_example.new

(   echo "Before mv." >&4
    mv "$FILE" "$FILE_NEW";
    if [ ! -e "$FILE" -a -e "$FILE_NEW" ]; then
        echo "The file $FILE was moved to $FILE_NEW."
    fi
    echo "After mv." >&4
    echo "This is the content in $FILE_NEW:"
    cat "$FILE_NEW"
) 4>"$FILE"

このスクリプトでは、前のセクションで行ったように、サブシェルとリダイレクトを使用します。 mv コマンドを実行する前後に、開いているファイルハンドルに書き込みます。 最後に、catを使用して新しいファイルのコンテンツを印刷します。

ソースとオリジンは同じフォルダー/tmpにあるため、システムはファイルの名前を変更するだけで済みます。 結果を見てみましょう:

The file /tmp/move_example was moved to /tmp/move_example.new.
This is the content in /tmp/move_example.new:
Before mv.
After mv.

出力からわかるように、古いファイルに両方を書き込んだ場合でも、新しいファイルには両方の行があります。

4.2. ファイルがコピーされます

システムがファイルを宛先にコピーするときの動作は異なります。 この場合、コピー後にソースファイルに書き込まれたコンテンツは、宛先ファイルには含まれません。 また、ファイルハンドルを閉じると、この新しいコンテンツは失われます。 これは、宛先ファイルが新しいiノードになるためです。 開いているファイルハンドルは古いiノードを指し、ファイルがコピーされると削除されます。

次に、move_opened_file.shスクリプトの$ FILE_NEWを変更して、宛先が別のファイルシステムになるようにします。次のように $ FILE_NEW =〜/move_by_copy_example.newに変更できます。通常、 /tmp/homeは異なるファイルシステムにあります。 結果を見てみましょう:

The file /tmp/move_example was moved to /home/baeldung/move_by_copy_example.new.
This is the content in /home/baeldung/move_by_copy_example.new:
Before mv.

予想どおり、新しいファイルにはmvの前の行しかありません。 ファイルハンドルは引き続き使用でき、削除されたファイルとして動作します。

5. ファイルの置き換え

ファイルが置き換えられると、状況はファイルの削除と同様になります。 開いているファイルハンドルがあり、それを別のファイルに置き換えると、システムは元のファイルを削除します。 これは、ファイルハンドルを開いたままにしておけば、ファイルを使用できることを意味します。

この動作をテストするスクリプトを作成できます。 それをreplace_opened_file.shと呼びましょう:

#!/bin/bash

FILE=/tmp/replace_example
FILE_OLD=/tmp/replace_example.old

echo "This is the old file." > $FILE_OLD

(   sleep 1
    echo "Before mv." >&4
    mv "$FILE_OLD" "$FILE"
    echo "After mv." >&4
) 4>"$FILE" &

(   sleep 2
    echo "This is the content in file handle 4:"
    cat <&4
    echo "This is the content in $FILE:"
    cat $FILE
) 4<"$FILE"

このスクリプトでは、最初に古いファイルを作成します。 次に、ファイルの削除をテストしたときと同じように、新しいファイルを開く2つのサブシェルを実行します。 最初のサブシェルは、新しいファイルを古いファイルに置き換えます。 そして最後に、新しいファイルの内容を印刷します。

replace_opened_file.shを実行してみましょう。

$ ./replace_opened_file.sh 
This is the content in file handle 4:
Before mv.
After mv.
This is the content in /tmp/replace_example:
This is the old file.

ご覧のとおり、ファイルを開いている限り、に書き込みおよび読み取りを行うことができます。 ただし、最後の行 cat“ $ FILE” を見ると、ファイルを再度開いたときに、古いファイルの内容が含まれていることがわかります。

6. 結論

この記事では、まだ開いているファイルを削除、移動、または置換するとどうなるかについて説明しました。

ファイルを削除または置換できることを確認しました。ファイルを開いたままにしておく限り、開いているファイルハンドルをまだ存在しているかのように使用できます。

一方、同じファイルシステム内でファイルを移動でき、開いているファイルハンドルが新しいファイルを指すことがわかりました。 ただし、ファイルを別のファイルシステムに移動すると、ファイルハンドルは古いファイルを指します。