在互動式 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