web-dev-qa-db-ja.com

読み取り専用ディレクトリ内のファイルを変更することは可能ですか?

私はすでに読んだ: ファイルをインプレースで変更する方法はありますか? 一時ファイルを作成しないコマンドを使用してファイルをインプレースで変更する方法があるかどうか知りたいです。ディレクトリmkdir read_onlyを作成してから、read_only内にいくつかのファイルを作成するとします。ファイルの作成が完了したら、chmod 555 read_onlyを実行します。一時ファイルを使用せずにファイルを変更することは可能ですか?具体的には、bashスクリプトから実行できるソリューションが必要です。可能かどうかだけは知りたくありません。私も解決策を探しています。これまでbash/unixコマンドで管理してきたアイデアはすべて、一時ファイルを作成します。

編集:私の質問は重複していないと思います ファイルを「書き込み」権限で編集できますが、親ディレクトリでは編集できませんか? 次の理由によります。

  • 私は「缶」を求めることに加えて解決策を探しています。一方、彼らは「できる」とだけ尋ねました。
  • 重複の可能性についての回答は、私の質問に完全には答えていません。 bashスクリプトで実行できるコマンドを要求します。
4
Kevin Tindall

ディレクトリ内のファイルを作成または削除するには、ディレクトリに対する書き込み権限が必要ですが、ディレクトリ内のファイルに書き込むことはできません。ほとんどのシェルコマンドは、出力ファイルが与えられると、書き込みのためにそのファイルを開き、以前にファイルにあったデータを置き換えるだけです。 >リダイレクト演算子は、既存のファイルを切り捨て(つまり、既存のファイルコンテンツを削除して、長さがゼロのファイルになります)、そのファイルへの書き込みを開始します。 >>リダイレクト演算子を使用すると、データがファイルの最後に追加されます。

ファイルへの書き込みは、低レベルのインターフェイスによって提供される可能性によって制限されます。ファイル内のバイトをその場で上書きし、ファイルの末尾に追加し、選択した長さにファイルを切り捨てることができます。後続のバイトを前方にシフトしている間(foobarfooNEWSTUFFbarのように)バイトを挿入したり、後続のバイトを後方にシフトしている間(foobarforのように)バイトを削除したりすることはできません。 )、ただし、データを読み取って移動し、新しい場所に書き込むことでこれらの操作をシミュレートする場合を除きます。

ファイルを適切に編集する際の問題は、何か問題が発生した場合(プログラムのバグ、ディスクがいっぱい、電力損失など)にファイルの内容の一貫性を維持することが難しいことです。これが、堅牢なファイル処理では通常、新しいデータを使用して一時ファイルを書き込み、その一時ファイルを所定の場所に移動する必要がある理由です。

ファイルの編集に関するもう1つの制限は、読み取りと書き込みを含む複雑な操作がアトミックではないことです。これは、他のタスクが変更と同時にファイルを読み取りたい場合にのみ問題になります。たとえば、最後の3バイト(foobar)を読み取ってからfooNEWSTUFFbarbarに変更し、新しいデータを書き込む場合(foofooNEWSTUFF)そして最後に古いテール(fooNEWSTUFFfooNEWSTUFFbar)を追加すると、同時リーダーはfooNEWSTUFF、またはファイルの一部のみを含むファイルの他の部分的な状態を見る可能性があります。書き込まれたデータ。繰り返しますが、一時ファイルを所定の位置に移動することはアトミック操作であるため、一時ファイルへの書き込みは最初にこの問題を解決します。

これらの制限を気にしない場合は、その場でファイルを変更できます。データの追加は簡単で(>>)、他のほとんどの変換はより複雑です。よくある落とし穴は

somefilter <somefile >somefile

ファイルの内容にsomefilterを適用しません。>演算子は、somefilterが出力ファイルからの読み取りを開始する前に出力ファイルを切り捨てます。

Joey Hessのmoreutils には、この問題を修正する sponge というユーティリティが含まれています。出力をsomefileにリダイレクトする代わりに、それをspongeにパイプします。これにより、すべての入力が読み取られ、then既存のファイルを、読み取った入力で上書きします。フィルタが失敗した場合でも、部分的なデータで終わる可能性があることに注意してください。

somefilter <somefile | sponge somefile

spongeがない場合、これを修正するためのポータブルで簡単な方法は、最初にデータをメモリに読み込むことです。

content=$(cat somefile; echo a)
content=${content%a}

echo aビットは、ファイルの最後に改行を保持するためのものです—コマンド置換は、常に末尾の改行を取り除きます。次に、コンテンツをコマンドに渡すことができます。

printf %s "$content" | somefilter >somefile

これにより、ファイルの内容がフィルターの出力に置き換えられます。コマンドが何らかの理由で失敗した場合、ファイルの元の内容は失われ、ファイルには、失敗する前にコマンドが書き込んだデータがすべて含まれます。

ほとんどのシェルはnullバイトをサポートしていないため、この方法はバイナリファイルでは機能しないことに注意してください。

その場でファイルを変更する別の方法は、 ed エディターを使用することです。これはsedに非常に似ていますが、ファイルをメモリにロードして保存します。 sedの行ごとの操作とは対照的に、場所。

ファイルをメモリにロードせず、一時ファイルを作成せずにファイルを操作することは、標準のシェルツールだけでは扱いにくいですが、実行できます。シェルリダイレクトとほとんどのテキスト処理ユーティリティでは、ファイルに追加するか、最初から上書きすることしかできません。 dd conv=notrunc seek=…を使用すると、上書きされていない部分に影響を与えることなく、ファイル内のあるオフセットでデータを上書きできます。例については、 ファイルをインプレースで変更する方法はありますか? を参照してください。

ディレクトリが読み取り専用であるが、そのディレクトリ内のファイルが読み取り/書き込みである場合、それらのファイルの上書きを妨げるものは何もありません。

スクリプトから、通常のリダイレクトを使用してファイルに書き込むことができます>および>>およびcpを使用してそれらを上書きします。

ディレクトリに新しいファイルを作成し、既存のファイルの上に名前を変更することはできません。名前の変更はアトミックに行われるため、新しいファイルを作成して名前を変更することが非常に望ましい場合がよくあります。これにより、データ損失に対するある程度の保護が提供され、通常の名前でファイルを読み取る他のプロセスが更新中にファイルの一部しか表示されないようになります。

新しいファイルを作成して上部の名前を変更する機能がないということは、ファイルを更新するときに必要な保証について非常に慎重に考える必要があることを意味します。

3
kasperd

Perlの Tie::File module は、真のインプレース編集機能を提供します。

Perl -MTie::File -e '
    tie @a,"Tie::File","your_file_here";
    # Do something...
'

これにより、@aの要素がファイルの行に追加され、ファイルが読み取り専用ディレクトリにある場合でも、@aに加えられた変更がファイルに反映されます。

3
Joseph R.

いいえ。sedやPerlは使用できません-i読み取り専用ディレクトリにあるファイルを編集するように切り替えます。正しく想定しているように、必要な一時ファイルを作成することはできません。

$ ls -ld read_only/
dr-xr-xr-x 2 terdon terdon 4096 Apr 13 02:16 read_only/
$ ls -l read_only/file 
-rw-r--r-- 1 terdon terdon 3 Apr 13 02:16 read_only/file
$ sed -i 's/a/A/' read_only/file
sed: couldn't open temporary file read_only/sedOEdv8L: Permission denied
$ Perl -i -pe 's/a/A/' read_only/file
Can't remove read_only/file: Permission denied, skipping file.

ただし、Joseph.Rの気の利いたPerlトリックを使用することはできます。

$ Perl -MTie::File -e 'tie @a,"Tie::File","$ARGV[0]"; $a[0]=~s/a/A/' read_only/file  
1
terdon