Você poderia fazer:
(set -C && cat < /path/to/src > /path/to/dest)
Não copiará nada além do conteúdo do arquivo (não as permissões, propriedade ou dispersão como fazem algumas implementações de cp
).
Existe uma maneira de fazer com que cp
(do GNU coreutils no Linux) retorne um valor diferente de zero caso o arquivo de destino já exista?
Ou existe algum outro pequeno utilitário comumente disponível e que fornece essa funcionalidade?
Isso seria útil em scripts de shell, para copiar atomicamente um arquivo sem perder acidentalmente nada e sem interação do usuário. Um resultado semelhante pode ser obtido comparando a cópia com o original, mas eu esperava uma solução mais simples.
Algumas implementações de cp
, incluindo o GNU cp
, possuem uma opção% padrão -n
para não estrobiar um arquivo se ele existir. mas o% GNUcp
retorna 0
de qualquer forma.
você poderia usar uma instrução if em seu script de shell para testar a existência do arquivo antes de copiar algo para ele:
if [ -e /path/to/file ]
then
exit 1
else
cp file /path/to/file
fi
ou se você quiser uma função, pode usar algo assim:
function cpa(){
if [ -e "$2" ]
then
exit 1
else
/bin/cp "$1" "$2"
fi
}
O fragmento de shell a seguir copia um arquivo apenas se o destino não existir e retorna um status diferente de zero se o destino existir.
(set -C; : >"$target" && cp -- "$source" "$target")
Isso não é atômico: se um arquivo for criado após a tentativa de anular $target
e antes do início da cópia, ele será sobrescrito. Além disso, cp
não é atômico: outro processo pode observar o arquivo sendo gravado. Isso, no entanto, funciona corretamente se todos os escritores concorrentes usarem a mesma estratégia.
O snippet mais complexo a seguir cria uma cópia temporária e a move para o lugar. Ainda existe uma condição de corrida: há uma pequena janela de tempo durante a qual um programa não relacionado que não joga bola pode criar o arquivo de destino.
( set -C
tmp=$(mktemp -p "$(dirname -- "$target")")
cp -- '$source" "$tmp"
if : >"$target"; then
mv -i -- "$tmp" "$target"
else
rm -- "$tmp"
false
fi
)
Expandindo o meu comentário para a resposta do h3rrmiller, e seguindo o padrão do trecho de Gilles, aqui está outro método de fazer uma cópia temporária e, em seguida, movê-la para o lugar. Eu acredito que este método não é vulnerável a nenhuma condição de corrida. Ele exige o sinalizador -n
em mv
, que não faz parte da especificação POSIX. No entanto, parece estar amplamente disponível. (Verificado na implementação do GNU, também BusyBox, FreeBSD, OS X.)
(
tmp=$(mktemp "${target}.XXXXXX") && # "mktemp -p" doesn't work on BSD
cp -- "$source" "$tmp" && # you may want to add -p flag
inode=$(ls -di "$tmp" | cut -d' ' -f1) && # see comment below
mv -n -- "$tmp" "$target" && # won't overwrite, but doesn't then exit non-0
if [ "$(ls -di "$target" | cut -d' ' -f1)" != "$inode" ]; then
rm -- "$tmp"; false
fi
)
No Linux, você pode obter o inode usando stat -c'%i' "$tmp"
, mas que não é portátil .
Em vez de verificar o inode, outra ideia seria verificar se um arquivo ainda existe no caminho $tmp
. Se o mv -n ...
for bem sucedido, então não deveria haver. (A menos que algum outro processo tenha criado recentemente $tmp
antes de você verificar se estava faltando.)
Para eficiência, faria sentido adicionar uma verificação se $target
existir no início da rotina. Se percebermos que existe, podemos falhar imediatamente, em vez de fazer a cópia, tentando movê-la, e ver que isso falhou, removendo assim a cópia. Isso é deixado como um exercício (fácil).
Tags cp coreutils shell-script