cp の対話型オプションを使用して標準入力からテキストを保存する

cp の対話型オプションを使用して標準入力からテキストを保存する

対話型シェルスクリプトで、コマンドの出力を次のようにファイルに保存したい。

$ echo "Hello, World" > test.txt

しかし、test.txtがすでに存在する場合は上書きされないようにしたい。そこで、cpインタラクティブオプション(cp -i) を使用して、対象ファイルが存在するかどうかを確認します。私はこれを試しました:

$ echo -n "Hello" | cp -i /dev/stdin test.txt 

これは、test.txt がまだ存在しない場合は test.txt に「Hello」を書き込みますが、test.txt が存在する場合はコピーを中止します。これは、cp が上書きするかどうかの答えをそのパイプからのみ読み取るためです。

しかし、この

$ cp -i <(echo "World") test.txt
cp: overwrite 'test.txt'? y
'/proc/self/fd/11' -> 'test.txt'

意図したとおりに動作します。cp はサブプロセスのファイル記述子をソースとして取得し、これが test.txt に cp されるようですが、その理由はまったくわかりません。

何かアイデアや説明、あるいはより良い方法はありますか?

答え1

代わりに別のファイル記述子を使用する/dev/stdin

echo Hello | { cp -i /dev/fd/5 test.txt 5<&0 </dev/tty; }

または、yes(1) 経由でスクリプト化可能

{ echo Hello | { cp -i /dev/fd/5 test.txt 5<&0 <&4; }; } 4<&0

どちらのバージョンでも、追加の{ ... }aroundはcp -i ...zshでのみ必要であり、非標準のマルチOS特徴。

これは、Linux + ksh93 の組み合わせを除く、標準シェルとファイルをサポートするシステムであればどれでも動作するはずです。Linux /dev/fd/N+ ksh93 の組み合わせは、ksh93 が Unix ソケットでパイプを実装しており、Linux ではこれを介して開くことができない/dev/fd/N(echo | cat /dev/stdin動作すらしない) ため動作しません。

の代わりに をそこに置いた場合、実際echo -n "Hello" | cp -i /dev/stdin test.txtにファイルが上書きされます。これが に頼るべきではない理由の 1 つです。"Hello""You suck!"cp -i

答え2

などのシェルは、bashリダイレクトによるファイルの上書きをすでに防ぐことができます。

set -o noclobber
echo hello >world
echo hello >world
-bash: world: cannot overwrite existing file

上書きする前にユーザーに確認したい場合は、設定の代わりに次のようなものを使用しますnoclobber

#!/bin/bash

# Define function sureYN  [<prompt>]  <target>
sureYN() {
    local prompt="$1" target="$2" yn='y'
    [[ -z "$target" ]] && { target="$prompt"; prompt="Overwrite $target"; }

    [[ -f "$target" && -t 0 ]] && read -p "$prompt (y/n)? " yn >&2
    [[ "$yn" =~ ^[Yy] ]]
}

sureYN /tmp/first && cp -fp /etc/hosts /tmp/first
sureYN "Can I replace /tmp/second" /tmp/second && echo this is the second >/tmp/second

関連情報