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/N
arquivos, 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/stdin
funcionarão).
Você echo -n "Hello" | cp -i /dev/stdin test.txt
realmente 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 bash
já 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 noclobber
configuraçã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