使用 cp 的互動選項儲存來自 stdin 的文本

使用 cp 的互動選項儲存來自 stdin 的文本

在互動式 shell 腳本中,我想將命令的輸出儲存到檔案中,例如

$ echo "Hello, World" > test.txt

但如果 test.txt 已經存在,則防止其被覆蓋。所以我認為使用它是一個好主意CP與它的互動選項(CP-i) 檢查目標檔案是否存在。我試過這個:

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

如果 test.txt 尚不存在,則將「Hello」寫入 test.txt,但如果 test.txt 存在,則中止複製,因為 cp 僅從該管道讀取是否覆蓋的答案。

然而,這

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

按預期工作, cp 似乎將子進程的 filedesriptor 作為來源,並將其 cp'ied 到 test.txt,我根本不知道為什麼。

有什麼想法或解釋或更好的方法嗎?

答案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

在這兩種變體中,僅在 zsh 中需要額外的{ ... }周圍cp -i ...,以便解決其非標準問題多作業系統特徵。

這應該可以在任何標準 shell 和任何支援/dev/fd/N檔案的系統中工作,但 Linux + ksh93 組合除外,該組合不起作用,因為 ksh93 正在使用 Unix 套接字實現管道,而在 Linux 上無法透過該管道開啟/dev/fd/N(甚至無法echo | cat /dev/stdin工作)。

如果你不把文件放在那裡,你echo -n "Hello" | cp -i /dev/stdin test.txt實際上會破壞文件。這就是您不應該依賴的原因之一。"Hello""You suck!"cp -i

答案2

諸如此類的 shellbash已經可以防止透過重定向覆蓋文件

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

相關內容