Usando a opção interativa do cp para salvar texto do stdin

Usando a opção interativa do cp para salvar texto do stdin

Em um script de shell interativo, gostaria de salvar a saída de um comando em um arquivo como em

$ echo "Hello, World" > test.txt

mas evite que test.txt seja substituído se já existir. Então pensei que seria uma boa ideia usarCPcom sua opção interativa (cp-eu) para verificar se o arquivo de destino existe. Eu tentei isso:

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

Isso grava "Hello" em test.txt se test.txt ainda não existir, mas aborta a cópia se test.txt existir, pois cp lê a resposta para substituir ou não apenas desse canal.

No entanto, isso

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

funciona como pretendido, cp parece usar o filedesriptor do subprocesso como fonte e isso é cp'ied para test.txt e eu simplesmente não sei por quê.

Alguma idéia ou explicação ou melhor maneira de fazer isso?

Responder1

Use outro descritor de arquivo em vez de/dev/stdin

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

Ou ainda programável via yes(1)

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

Em ambas as variantes, os { ... }recursos extras cp -i ...são necessários apenas no zsh, para contornar seu problema não padrãomultiosrecurso.

Isso deve funcionar em qualquer shell padrão e em qualquer sistema que suporte /dev/fd/Narquivos, exceto para a combinação Linux + ksh93, que não funcionará porque ksh93 está implementando pipes com soquetes Unix, que no Linux não podem ser abertos /dev/fd/N(nem mesmo echo | cat /dev/stdinfuncionarão).

Você echo -n "Hello" | cp -i /dev/stdin test.txtrealmente destruirá o arquivo se, em vez de "Hello"colocá-lo "You suck!"lá. Essa é uma das razões pelas quais você não deve confiar no cp -i.

Responder2

Shells como bashjá podem proteger contra a substituição de arquivos com um redirecionamento

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

Se você quiser perguntar ao usuário antes de substituir, eu usaria algo assim em vez da noclobberconfiguração

#!/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

informação relacionada