使用 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

相关内容