Diferença entre gato e '>' para zerar um arquivo

23

Esses dois comandos são diferentes na maneira como eles zeram os arquivos? O último é uma maneira mais curta de fazer o primeiro? O que está acontecendo nos bastidores?

Ambos

$ cat /dev/null > file.txt

$ > file.txt 

produção

-rw-r--r--  1 user  wheel  0 May 18 10:33 file.txt
KM.
fonte

Respostas:

28

cat /dev/null > file.txté um uso inútil de gato .

Basicamente, cat /dev/nullsimplesmente resulta em catnada de saída. Sim, funciona, mas é desaprovado por muitos porque resulta na invocação de um processo externo que não é necessário.
É uma daquelas coisas comuns simplesmente porque é comum.

Usar apenas > file.txtfuncionará na maioria dos shells, mas não é totalmente portátil. Se você quiser totalmente portátil, a seguir estão boas alternativas:

true > file.txt
: > file.txt

Ambos :e truenão produzem dados, e são componentes internos do shell (embora catseja um utilitário externo), portanto, são mais leves e mais 'adequados'.

 

Atualizar:

Como Tylerl mencionou em seu comentário, também há a >| file.txtsintaxe.

A maioria dos shells possui uma configuração que os impede de truncar um arquivo existente via >. Você deve usar em seu >|lugar. Isso é para evitar erros humanos quando você realmente deseja anexar >>. Você pode ativar o comportamento com set -C.

Portanto, com isso, acho que o método mais simples, adequado e portátil de truncar um arquivo seria:

:>| file.txt
Patrick
fonte
2
O comando dois pontos é definido no POSIX . É uma operação nula que existe para expandir argumentos da linha de comandos.
Kojiro #
3
LOL, "abuso de gato"
KM.
2
O @kojiro :também é mandatado pelo POSIX para ser embutido e, na verdade, é diferente do truefato de ser considerado um embutido "especial" .
Jw013
2
não se esqueça de noclobber . >| fileé um truncado mais explícito.
tylerl
1
Não trueé necessário que ele seja construído e tradicionalmente não era. :é construído em todas as conchas da família Bourne. :é um built-in especial por POSIX (portanto : > file, sairá do shell, por exemplo, se filenão puder ser aberto para escrever em shells POSIX) e truenão será. O POSIX até menciona que :pode ser mais eficiente do que trueem alguns sistemas.
Stéphane Chazelas
23

Em termos de portabilidade:

                      Bourne POSIX  zsh    csh/tcsh  rc/es  fish
> file                Y      Y      N(1)   N(1)      N      N
: > file              N/Y(2) Y(3)   Y      Y(4)      N(5)   N(5)
true > file           Y(5)   Y      Y      Y(5)      Y(5)   Y(5)
cat /dev/null > file  Y(5)   Y      Y(5)   Y(5)      Y(5)   Y(5)
eval > file           Y(3,8) Y(3)   Y      Y(6)      Y      Y
cp /dev/null file (7) Y(5)   Y      Y(5)   Y(5)      Y(5)   Y(5)
printf '' > file      Y(5)   Y      Y      Y(5)      Y(5)   Y

Notas:

  1. exceto in shou kshemulation, para redirecionamentos sem um comando, no zsh, um comando padrão é assumido (um pager apenas para o redirecionamento stdin, catcaso contrário), que pode ser ajustado com as variáveis ​​NULLCMD e READNULLCMD. Isso é inspirado no recurso semelhante em(t)csh
  2. Os redirecionamentos não foram inicialmente executados :no UnixV7, pois :foram interpretados a meio caminho entre um líder de comentário e um comando nulo. Mais tarde, eles foram e foram para todos os componentes internos, se o redirecionamento falhar, que sai do shell.
  3. :e evalsendo built-ins especiais, se o redirecionamento falhar, que sai do shell ( bashsomente isso no modo POSIX).
  4. Curiosamente, em (t)csh, isso está definindo um rótulo nulo (para goto), para que goto ''haja ramificação lá. Se o redirecionamento falhar, isso sai do shell.
  5. A menos que / se o comando correspondente está disponível em $PATH( :geralmente não é; true, cat, cpe printfgeralmente são (POSIX exige que eles)).
  6. Se o redirecionamento falhar, isso sai do shell.
  7. fileNo entanto, se houver um link simbólico para um arquivo inexistente, algumas cpimplementações como o GNU se recusarão a criá-lo.
  8. As versões iniciais do shell Bourne não suportavam o redirecionamento de builtins

Em termos de legibilidade:

(esta seção é altamente subjetiva)

  • > file. Isso >parece muito com um prompt ou um comentário. Além disso, a pergunta que vou fazer ao ler isso (e a maioria das conchas se queixam da mesma) é qual saída exatamente você está redirecionando? .
  • : > file. :é conhecido como o comando no-op. Então, isso é lido imediatamente como gerando um arquivo vazio. No entanto, aqui novamente, isso :pode ser facilmente esquecido e / ou visto como um prompt.
  • true > file: o que booleano tem a ver com o redirecionamento ou o conteúdo do arquivo? O que se entende aqui? é a primeira coisa que me vem à cabeça quando leio isso.
  • cat /dev/null > file. Concatenar /dev/nullem file? catsendo muitas vezes visto como o comando para despejar o conteúdo do arquivo, que ainda pode fazer sentido: despejar o conteúdo do arquivo vazio emfile , um pouco como um modo complicado de dizer cp /dev/null file, mas ainda compreensível.
  • cp /dev/null file. Copia o conteúdo do arquivo vazio para file. Faz sentido, embora alguém que não saiba como cpdeve ser feito por padrão possa pensar que você está tentando criar fileum nulldispositivo também.
  • eval > fileou eval '' > file. Não executa nada e redireciona sua saída para a file. Faz sentido para mim. Estranho que não seja um idioma comum.
  • printf '' > file: explicitamente imprime nada em um arquivo. Aquele que faz mais sentido para mim.

Em termos de desempenho

A diferença será se estamos usando um shell embutido ou não. Caso contrário, um processo deve ser bifurcado, o comando carregado e executado.

evalé garantido para ser construído em todas as conchas. :está embutido onde quer que esteja disponível (como Bourne / csh). trueestá embutido apenas em conchas semelhantes a Bourne.

printfé embutido na maioria dos modernos cascos Bourne-like e fish.

cpe catgeralmente não são integrados.

Agora cp /dev/null filenão invoca redirecionamentos de shell, portanto, coisas como:

find . -exec cp /dev/null {} \;

serão mais eficientes do que:

find . -exec sh -c '> "$1"' sh {} \;

(embora não necessariamente que:

find . -exec sh -c 'for f do : > "$f"; done' sh {} +

)

Pessoalmente

Pessoalmente, eu uso : > fileem conchas do tipo Bourne e não uso nada além de conchas do tipo Bourne atualmente.

Stéphane Chazelas
fonte
Que tal dd of=file count=0?
Kojiro 19/05
2
@kojiro, Com algumas implementações dd(como pelo menos Solaris 10), count=0é ignorado. dd if=/dev/null of=fileseria mais portátil. De qualquer forma, isso é independente do shell.
Stéphane Chazelas
OK, mas não é menos merecedor de inclusão do que cp /dev/null file, certo?
Kojiro 19/05
2
@kojiro, cp /dev/null fileé um idioma comum. Estou limitando a eles, o ponto não é listar todas as maneiras possíveis.
Stéphane Chazelas
5

Você pode querer olhar truncate, o que faz exatamente isso: truncar um arquivo.

Por exemplo:

truncate --size 0 file.txt

Provavelmente é mais lento do que usar true > file.txt.

Meu ponto principal, no entanto, é: truncatedestina-se a truncar arquivos, enquanto o uso de> tem o efeito colateral de truncar um arquivo.

Fabian
fonte
2
Truncar é bom quando você deseja truncar um arquivo para algo diferente de 0. Dito isso, mesmo sem um shell é uma afirmação estranha: você pode descrever um contexto onde truncateestaria disponível, mas >nem as unistdbibliotecas C estariam disponíveis?
Kojiro
Na verdade não. Provavelmente existe uma solução mais elegante para todos os scripts ou linguagens de programação disponíveis.
Fabian
3
truncateé um utilitário FreeBSD, adicionado relativamente recentemente (2008) aos coreutils do GNU (embora o --sizeestilo de opção longa do GNU seja específico do GNU), portanto não está disponível em sistemas que não são do GNU ou FreeBSD e não está disponível nos sistemas GNU mais antigos, Eu não diria que é portátil. cp /dev/null filefuncionaria sem um redirecionamento de shell e seria mais portátil.
Stéphane Chazelas
Ok, vou remover esse comentário de portabilidade. Embora sua definição de recente pareça diferente.
Fabian
2

A resposta depende um pouco do que file.txté e como o processo é gravado nela!

Vou citar um caso de uso comum: você tem um arquivo de log crescente chamado file.txte deseja alterná-lo.

Portanto, você copia, por exemplo, file.txtpara e file.txt.save, em seguida, trunca file.txt.

Nesse cenário, se o arquivo não for aberto por another_process(ex: another_processpoderia ser um programa que gera esse arquivo, por exemplo, um programa que registra algo), suas duas propostas são equivalentes e ambas funcionam bem (mas a segunda é preferida como a O primeiro "cat / dev / null> file.txt" é um uso inútil do gato e também abre e lê / dev / null).

Mas o verdadeiro problema seria se o other_processainda estiver ativo e ainda tiver um identificador aberto indo para o arquivo.txt.

Em seguida, surgem 2 casos principais, dependendo de como other processo arquivo foi aberto:

  • Se o other_processabrir da maneira normal, o identificador ainda estará apontando para o local anterior no arquivo, por exemplo, com deslocamento de 1200 bytes. A próxima gravação começará, portanto, no deslocamento 1200 e, portanto, você terá novamente um arquivo de 1200 bytes (+ o que outro processo escreveu), com 1200 caracteres nulos iniciais! Não é o que você quer , eu presumo.

  • Se other_processaberto file.txtno "modo de acréscimo", toda vez que ele gravar, o ponteiro procurará ativamente o final do arquivo. Portanto, quando você o truncar, ele "procurará" até o byte 0 e você não terá o efeito colateral ruim! É isso que você quer (... geralmente!)

Observe que isso significa que, ao truncar um arquivo, é necessário garantir que todos os que other_processainda estão gravando no local o tenham aberto no modo "anexar". Caso contrário, será necessário pará-los other_processe iniciá-los novamente, para que eles comecem a apontar para o início do arquivo e não para o local anterior.

Referências: /programming//a/16720582/1841533 para obter uma explicação mais limpa e um bom exemplo curto de diferença entre os logs de modo normal e de acréscimo em /programming//a/984761/1841533

Olivier Dulac
fonte
2
Muito pouco dessa resposta é realmente relevante ou responde à pergunta. A diferença entre a cat /dev/null > filee a > fileé a cat /dev/nulle isso não faz diferença para o arquivo.
Jw013 19/05
@ jw013: Verdade! Mas eu só queria aproveitar a oportunidade da pergunta para reexpor as informações "o que você quer / não o que você quer", como não é muito conhecido, e poderia bater com força em alguém que tentasse girar logs (um caso comum em que você deseja truncar um arquivo).
Olivier Dulac
1
Tem uma hora e um lugar pra tudo. Suas informações podem ser úteis em algum outro contexto, mas não pertencem aqui - você deve encontrar um local mais apropriado para elas, porque ninguém que tenta girar logs vai procurar nesta questão de redirecionamento completamente não relacionada. Aqui, sua resposta é equivalente a uma erva daninha digital, assim como uma planta de abóbora útil no meio de um milharal seria considerada uma erva daninha.
Jw013
1

Gosto disso e uso-o com frequência porque parece mais limpo e não como se alguém tivesse pressionado a tecla Return por acidente:

echo -n "" > file.txt

Também deve ser um built-in?

awsm
fonte
3
Existem várias maneiras de zerar um arquivo. Eu acho que KM. estava interessado apenas em entender a diferença entre os dois métodos mostrados na pergunta.
drs
6
Muitas echoimplementações não suportam -n(e geraria -n<SPC><NL>aqui. printf '' > file.txtSeria mais portátil (pelo menos entre os sistemas modernos / POSIX).
Stéphane Chazelas