Por que você `cat / dev / null> / var / log / messages`?

77

Nesta página de exemplo de script do bash, o autor apresenta este script:

# Cleanup
# Run as root, of course.

cd /var/log
cat /dev/null > messages
cat /dev/null > wtmp
echo "Log files cleaned up."

Por que você colocaria cat /dev/nullalguma coisa? Não consigo entender o que se pretende aqui (é como usar while TRUE; sleep 1; elihwpara {some busy program}?). No entanto, o autor chama isso de "Nada incomum".

isomorfismos
fonte

Respostas:

40

Normalmente, cat /dev/null > [something]quando você deseja limpar o conteúdo do arquivo, assegura um risco zero de interrupção no estado real do arquivo. O conteúdo do arquivo será limpo com clareza, cat /dev/nullmas o arquivo em si - como existe e é conhecido pelo sistema de arquivos em que reside - ainda estará lá com o mesmo número de inode, propriedade e permissões.

No caso de um arquivo de log, pode ser que o próprio arquivo de log esteja marcado como "em uso" por outro processo. Fazer isso, por exemplo, rm /var/log/messages && touch /var/log/messagesseria prejudicial a outros processos e poderia causar o sufocamento dos processos em execução. Significando que um processo que de alguma forma está bloqueado para um número de inode específico conectado ao arquivo /var/log/messagespode entrar em pânico repentinamente e dizer: “Ei! O que aconteceu com /var/log/messages! ”Mesmo se o arquivo ainda estiver lá. Sem mencionar possíveis problemas com a propriedade e as permissões sendo recriadas incorretamente.

Devido a esta incerteza no uso / estado de um arquivo o uso de cat /dev/null > [something]é o preferido pelos administradores de sistema que querem limpar um log, mas não querem potencialmente interferir com o funcionamento dos processos já existentes.

Além disso, no contexto da página que você vincula ao autor, declara o seguinte:

Não há nada incomum aqui, apenas um conjunto de comandos que poderiam ser facilmente invocados um a um a partir da linha de comando no console ou em uma janela do terminal. As vantagens de colocar os comandos em um script vão muito além de não ter que digitá-los novamente.

Portanto, o “nada de incomum” que o autor menciona diz respeito a todo o conceito do que é esse script bash específico: é apenas um conjunto de comandos simples que podem ser executados com facilidade na linha de comando, mas são colocados em um arquivo de texto para evite ter que digitá-los repetidamente.

JakeGould
fonte
10
@jlliagre A única coisa que é "mantida viva" é o contexto da pergunta, focada em por que o autor original faria isso. Se você tem paixão por dissipar o "mito", poste uma resposta que forneça contexto para o método de codificação do autor original, bem como uma perspectiva de por que você acredita que ele pode ser substituído por outros métodos.
perfil completo de JakeGould
7
@jlliagre A resposta de Cyrus não aborda a questão central de por que o autor original teria usado esse método nem o raciocínio por trás dele. O tutorial mencionado é bastante antigo e razoavelmente aceito. Não é incorreto nem perpetua uma suposta "lenda urbana". Pelo contrário, é a maneira como uma pessoa codifica versus a maneira como outra pessoa codifica. Tão simples como isso. É uma questão de estilo que não tem impacto negativo na confiabilidade ou no desempenho.
JakeGould
3
truncate -s 0faria a mesma coisa e seria menos idiomático. No entanto, os programadores de shell são um grupo conservador e pode-se encontrar sistemas com idade suficiente ou maluco o suficiente para não ter esse comando.
Schwern
5
@skift cat /dev/null > /foo/bartrunca o arquivo; echo "" > /foo/bartrunca e, em seguida, grava um único caractere de nova linha.
David
2
Outra vantagem desse método sobre o rm & touch é que a propriedade e as permissões são mantidas.
Jjmontes
121

Por que você colocaria / dev / null em algo?

Você fará isso para truncar o conteúdo de um arquivo enquanto mantém o inode intacto. Todos os programas que possuem esse arquivo aberto para leitura ou gravação não seriam afetados fora do fato de que o tamanho do arquivo seria redefinido para zero.

Uma alternativa falsa frequentemente encontrada é remover o arquivo e criá-lo novamente:

rm file
touch file

ou similar:

mv file file.old
gzip file.old
touch file

O problema é que esses métodos não impedem que o arquivo antigo seja mantido gravado por quaisquer processos que tenham o arquivo excluído aberto no momento da exclusão. A razão pela qual está em sistemas de arquivos Unix, quando você exclui um arquivo, você desvincula apenas o nome (caminho) do conteúdo (inode). O inode é mantido ativo enquanto houver processos abertos para leitura ou gravação.

Isso leva a vários efeitos negativos: os logs gravados após a exclusão do arquivo são perdidos, pois não há uma maneira simples / portátil de abrir um arquivo excluído. Enquanto um processo está gravando no arquivo excluído, seu conteúdo ainda está usando espaço no sistema de arquivos. Isso significa que, se você remover / criar o arquivo porque estava preenchendo seu disco, ele permanecerá preenchido. Uma maneira de corrigir esse último problema é reiniciar os processos do criador de logs, mas talvez você não queira fazer isso para serviços críticos e os logs intermediários seriam definitivamente perdidos. Também existem efeitos colaterais, porque o arquivo que você cria pode não ter as mesmas permissões, proprietário e grupo que o original. Isso, por exemplo, pode impedir que um analisador de logs leia o arquivo recém-criado ou, pior ainda, evite que o processo de log grave seus próprios logs.

O primeiro método, cat /dev/null > fileatingir o objetivo corretamente , no entanto, apesar de uma lenda urbana tenaz, sua cat /dev/nullparte não faz absolutamente nada útil. Ele abre um pseudo arquivo vazio por design, não consegue ler nada dele e, finalmente, sai. O uso desse comando é um desperdício de pressionamentos de tecla, bytes, chamadas de sistema e ciclos de CPU e pode ser substituído sem nenhuma alteração funcional pelo comando no-op, sem dúvida, mais rápido :ou mesmo, com a maioria dos shells, por nenhum comando.

Deixe-me tentar uma metáfora para explicar como cat /dev/nullé inútil . Digamos que seu objetivo é esvaziar um copo.

  • Você primeiro remove qualquer líquido dele. Isso é suficiente e é precisamente o que ( > file) faz, pois os redirecionamentos são sempre processados ​​primeiro.

  • Depois, você escolhe uma garrafa vazia ( /dev/null) e despeja no copo vazio ( cat). Este é o passo inútil ...

Se você ler o documento vinculado até o final, poderá observar os comentários nesta linha na versão aprimorada do script:

    cat / dev / null> wtmp #   ':> wtmp' e '> wtmp' têm o mesmo efeito.

Eles realmente têm; muito ruim cat /dev/nullfoi mantido no código.

Isso significa que o código a seguir funcionará com todos os shells comuns (ambos cshe shfamílias):

cd /var/log
: > messages
: > wtmp
echo "Log files cleaned up."

e isso vai funcionar com todas as conchas usando a sintaxe Bourne, como ash, bash, ksh, zshe os gostos:

cd /var/log
> messages
> wtmp
echo "Log files cleaned up."

Observe, no entanto, que com shells antigos Bourne pré-POSIX, qualquer um desses comandos, inclusive cat /dev/null, não truncará um arquivo se ele for gravado posteriormente por um script de shell ainda em execução anexado a ele. Em vez de um arquivo de zero byte, seria um arquivo esparso com seu tamanho inalterado. O mesmo aconteceria se o arquivo fosse gravado por um processo buscando a posição que ele pensa ser a atual antes de ser gravada.

Observe também que algumas soluções alternativas frequentemente sugeridas para truncar um arquivo têm falhas.

  • Ambos os seguintes simplesmente não fazem o trabalho. O arquivo resultante não está vazio, mas contém uma linha vazia. Isso quebraria arquivos de log como wtmpesse que armazenam registros de largura fixa.

    echo > file
    echo "" > file
  • A próxima baseada em uma shopção BSD não é portátil, o POSIX não especifica nenhuma opção permitida para eco, portanto, você pode acabar com um arquivo contendo uma linha com " -n":

    echo -n > file
  • Esse também não é portátil, usando uma shsequência de escape do System V. Alguns shells criarão um arquivo contendo uma linha com " \c":

    echo "\c" > file
  • Esse usa um comando projetado para fazer o trabalho. O problema usado truncatenão é portátil, pois esse comando, não sendo especificado pelo POSIX, pode estar ausente em um sistema Unix / Linux.

    truncate -s 0

Finalmente, aqui estão algumas alternativas que são portáteis e farão o trabalho corretamente:

  • Imprimindo explicitamente uma sequência vazia no arquivo:

    printf "" > file
  • Usando o truecomando que é estritamente equivalente ao no-op, :embora mais legível:

    true > file
jlliagre
fonte
1
@ JonathanLeffler Eu acredito que faz parte de todas as cshimplementações atuais, embora não esteja necessariamente documentado. Na csh página de manual do Solaris : Null command. This command is interpreted, but performs no action.. Não encontrei menção do mesmo na tcshpágina do manual ou no original do BSD csh, mas essa sintaxe sempre funcionou.
Jlliagre
6
Um dos motivos para escrever cat /dev/nullé explicitar suas intenções.
Davidmh
1
@ Davididmh Isso seria sensato, mas sua declaração não resiste a verificações de fatos. Eu observei esse idioma de shell por provavelmente algumas décadas. Quando tive a oportunidade de perguntar ao autor o raciocínio por trás disso, sempre tive teorias infundadas sobre o uso de /dev/nulluma maneira eficiente de injetar zero bytes, de qualquer maneira mais confiável do que usar :ou nada. Nesta mesma página, a resposta de Cyrus, que era concisa, mas direta ao ponto, tinha zero voto, enquanto a de JakeGould, que estava contestando o fato, cat /dev/nullera um no-op (veja nossos comentários) já tinha pelo menos quatro votos.
Jlliagre
2
@ jlliagre meu conhecimento do shell é bastante básico, então talvez eu não seja o melhor alvo da população; mas uma nua >ou :não é clara. Concordo que é uma pena que os mitos tenham sido propagados devido ao seu uso.
Davidmh
4
@ Davididmh Se você realmente quer algo explícito antes do redirecionamento, eu recomendaria o printf "" > fileque é portátil (POSIX) e leve, tão frequentemente implementado quanto um shell embutido.
Jlliagre
6

É uma maneira complicada de colocar o arquivo em tamanho zero.

cd /var/log
> messages
> wtmp
echo "Log files cleaned up."
Cyrus
fonte
Essa sintaxe não funcionará em todos os shell.
Reinierpost
2
Corrigir. A pergunta está marcada apenas com "bash".
Cyrus
@reinierpost Ele funcionará em todas as conchas no estilo Bourne, não é? Não vamos nos preocupar csh.
Barmar 12/12
: > messagestambém funciona. :ou truesão as opções mais óbvias para comandos que não imprimem nada e retornam verdadeiros.
Peter Cordes
-3

Para truncar um arquivo aberto. Isso é equivalente e mais compreensível:

echo -n > /var/log/messages

(Adicionado -n para evitar a nova linha)

bbaassssiiee
fonte
3
Isso é realmente mais compreensível, mas infelizmente não é equivalente. Ele pode até quebrar funcionalidades como no wtmpcaso. Veja minha resposta atualizada.
Jlliagre
eco -n evita nova linha.
Jun
6
De fato, se você tiver certeza de usar bash, -nnão é garantido que funcione em todos os reservatórios Bourne.
Jlliagre 07/07