Faça o cp retornar um valor de erro se o destino existir

6

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.

    
por MvG 14.11.2012 / 02:29

4 respostas

1

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 ).

    
por 14.11.2012 / 10:54
3

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 
}
    
por 14.11.2012 / 03:20
3

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
)
    
por 15.11.2012 / 01:14
1

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).

    
por 15.11.2012 / 10:38