Manter (ou restaurar) permissões de arquivo ao substituir o arquivo

9

Eu tenho um comando que aceita um arquivo como um argumento, modifica o arquivo e grava-o no nome do arquivo especificado no segundo argumento. Vou chamar esse programa modifyfile .

Eu queria que funcionasse "no lugar", então escrevi um script de shell (bash) que o modifica para um arquivo temporário e o move de volta:

TMP='mktemp'
modifyfile "$original" "$TMP"
mv -v "$TMP" "$original"

Isso tem o efeito colateral infeliz de destruir as permissões nesse arquivo. O arquivo é recriado com permissões padrão.

Existe uma maneira de dizer ao comando mv para sobrescrever o destino sem alterar suas permissões? Ou, alternativamente, existe uma maneira de salvar o usuário, o grupo e as permissões do original e restaurá-los?

    
por Stephen Ostermiller 17.09.2013 / 18:44

3 respostas

10

Em vez de usar mv , apenas redirecione cat . Por exemplo:

TMP=$(mktemp)
modifyfile "$original" "$TMP"
cat "$TMP" > "$original"

Isso sobrescreve $original com o conteúdo de $TMP , sem tocar em nada no nível do arquivo.

    
por 17.09.2013 / 18:48
8

Existem duas estratégias para substituir um arquivo por uma nova versão:

  1. Crie um arquivo temporário com a nova versão e mova-o para o lugar.

    • Vantagem: se um programa abrir esse arquivo, ele lerá o conteúdo antigo ou o novo conteúdo, dependendo de ter aberto o arquivo antes ou depois da movimentação. Não há confusão.
    • Vantagem: em caso de falha, o conteúdo antigo é preservado.
    • Desvantagem: como um novo arquivo é criado, os atributos do arquivo (propriedade, permissão, etc.) não são preservados.
  2. Substituir o arquivo antigo no lugar.

    • Vantagem: os atributos do arquivo são preservados.
    • Desvantagem: em caso de falha, o arquivo pode ser deixado pela metade.
    • Desvantagem: se um programa tiver o arquivo aberto quando estiver sendo atualizado, esse programa poderá ler dados inconsistentes.

Se puder, use o método 1, mas primeiro replique os atributos do arquivo original com cp -p . Isso replica os dados também, você não pode escapar disso com ferramentas básicas.

tmp=$(mktemp)
cp -p "$original" "$tmp"
modifyfile "$original" "$tmp"
mv -f "$tmp" "$original"

Se não for possível replicar os atributos do arquivo existente, por exemplo, porque você tem permissões de gravação, mas não o possui e deseja preservar o proprietário, somente o método 2 é possível. Para minimizar o risco de perda de dados:

  • Faça a janela durante a qual o arquivo ficará incompleto o menor possível. Prepare os dados primeiro em um arquivo temporário e copie-o no lugar.
  • Faça um backup do arquivo antigo primeiro.

tmp=$(mktemp)
backup="${original}~"
modifyfile "$original" "$tmp"
cp -p "$original" "$backup"
cp -f "$tmp" "$original"
    
por 18.09.2013 / 03:56
5

Após nossa discussão sobre a primeira resposta, proponho uma resposta diferente:

TMP="$(mktemp "$original".XXXXXXXXXX)"
modifyfile "$original" "$TMP"
chmod --reference="$original" "$TMP"
chown --reference="$original" "$TMP"
mv -f "$TMP" "$original"

Observações:

  • Eu uso $original no modelo mktemp para garantir que o arquivo temporário não seja colocado em /tmp , mas na mesma pasta que $original . Acredito que, se /tmp estiver montado em um sistema de arquivos diferente, a operação não será mais atômica.
  • O resultado de mktemp agora é citado no caso de conter espaço em branco.
  • Eu uso $() em vez de '' porque eu considero isso mais limpo.
  • ch{mod,own} --reference são usados para transferir as permissões de $original para $TMP . Se alguém tiver ideias adicionais sobre quais metadados podem e devem ser transferidos, edite a minha publicação e adicione-a.
  • Bem, isso requer permissões de root, como Gilles apontou. Bem, eu não vou descartar isso agora que eu escrevi: P
por 18.09.2013 / 01:09