Différence entre chat et '>' pour mettre à zéro un fichier

23

Ces deux commandes sont-elles différentes sur la façon dont elles mettent à zéro les fichiers? La seconde est-elle une façon plus courte de faire la première? Que se passe-t-il dans les coulisses?

Tous les deux

$ cat /dev/null > file.txt

$ > file.txt 

rendement

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

Réponses:

28

cat /dev/null > file.txtest une utilisation inutile du chat .

Fondamentalement, cela ne cat /dev/nullgénère catrien. Oui, cela fonctionne, mais il est mal vu par beaucoup car il entraîne l'invocation d'un processus externe qui n'est pas nécessaire.
C'est une de ces choses qui est courante simplement parce qu'elle est commune.

Utiliser juste > file.txtfonctionnera sur la plupart des shells, mais ce n'est pas complètement portable. Si vous voulez être complètement portable, voici de bonnes alternatives:

true > file.txt
: > file.txt

Les deux :et truene produisent aucune donnée, et sont des commandes intégrées au shell (alors qu'il cats'agit d'un utilitaire externe), ils sont donc plus légers et plus «appropriés».

 

Mise à jour:

Comme tylerl l'a mentionné dans son commentaire, il y a aussi la >| file.txtsyntaxe.

La plupart des shells ont un paramètre qui les empêchera de tronquer un fichier existant via >. Vous devez utiliser à la >|place. C'est pour éviter les erreurs humaines lorsque vous vouliez vraiment ajouter >>. Vous pouvez activer le comportement avec set -C.

Donc, avec cela, je pense que la méthode la plus simple, la plus appropriée et portable de tronquer un fichier serait:

:>| file.txt
Patrick
la source
2
La commande deux-points est définie dans POSIX . Il s'agit d'une opération nulle qui existe pour développer les arguments de ligne de commande.
kojiro
3
LOL, "abus de chat"
KM.
2
@kojiro :est également mandaté par POSIX pour être intégré, et en fait est différent du truefait qu'il est considéré comme un intégré "spécial" .
jw013
2
n'oubliez pas le noclobber . >| fileest un tronçon plus explicite.
tylerl
1
Aucun truen'est requis pour être intégré et ce n'était pas le cas traditionnellement. :est construit dans toutes les coquilles de la famille Bourne. :est un prédéfini spécial par POSIX (ainsi : > filequittera le shell par exemple s'il filene peut pas être ouvert pour écrire dans des shells POSIX) et truene l'est pas. POSIX mentionne même que cela :peut être plus efficace que truesur certains systèmes.
Stéphane Chazelas
23

En termes de portabilité:

                      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

Remarques:

  1. sauf dans shou kshémulation, pour les redirections sans commande, dans zsh, une commande par défaut est supposée (un pager pour la redirection stdin uniquement, catsinon), qui peut être réglée avec les variables NULLCMD et READNULLCMD. C'est inspiré de la fonctionnalité similaire de(t)csh
  2. Les redirections n'étaient initialement pas effectuées :dans UnixV7 comme cela a :été interprété à mi-chemin entre un leader de commentaire et une commande nulle. Plus tard, ils l'ont été et comme pour tous les buildins, si la redirection échoue, cela quitte le shell.
  3. :et evalétant des fonctions intégrées spéciales, si la redirection échoue, cela quitte le shell ( bashne le fait qu'en mode POSIX).
  4. Fait intéressant, dans (t)csh, cela définit une étiquette nulle (pour goto), donc goto ''il y aurait une branche là-bas. Si la redirection échoue, cela quitte le shell.
  5. À moins / si la commande correspondante est disponible en $PATH( :est généralement pas, true, cat, cpet printfsont en général (les oblige Posix)).
  6. Si la redirection échoue, cela quitte le shell.
  7. fileCependant, s'il s'agit d'un lien symbolique vers un fichier inexistant, certaines cpimplémentations comme GNU refuseront de le créer.
  8. Les versions initiales du shell Bourne ne prenaient cependant pas en charge la redirection des buildins

En termes de lisibilité:

(cette section est très subjective)

  • > file. Cela >ressemble trop à une invite ou à un commentaire. De plus, la question que je poserai en lisant cela (et la plupart des shells se plaindront de la même chose) est quelle sortie exactement redirigez-vous? .
  • : > file. :est connu comme la commande no-op. Donc, cela se lit immédiatement comme générant un fichier vide. Cependant, là encore, cela :peut facilement être manqué et / ou vu comme une invite.
  • true > file: qu'est - ce que le booléen a à voir avec la redirection ou le contenu des fichiers? Que veut-on dire ici? est la première chose qui me vient à l'esprit lorsque je lis cela.
  • cat /dev/null > file. Concaténer /dev/nullen file? catétant souvent considérée comme la commande pour vider le contenu du fichier, qui peut encore faire sens: vider le contenu du du fichier vide dansfile , un peu comme une façon alambiquée de dire cp /dev/null filemais toujours compréhensible.
  • cp /dev/null file. Copie le contenu du fichier vide dans file. Cela a du sens, bien que quelqu'un qui ne sait pas comment cpfaire par défaut puisse penser que vous essayez également de fabriquer fileun nullappareil.
  • eval > fileou eval '' > file. N'exécute rien et redirige sa sortie vers a file. Ça a du sens pour moi. Étrange que ce ne soit pas un idiome commun.
  • printf '' > file: n'imprime explicitement rien dans un fichier. Celui qui a le plus de sens pour moi.

En termes de performances

La différence va être de savoir si nous utilisons un shell intégré ou non. Sinon, un processus doit être bifurqué, la commande chargée et exécutée.

evalest garanti pour être construit dans toutes les coquilles. :est intégré partout où il est disponible (Bourne / csh aime). trueest intégré dans des coquilles de type Bourne uniquement.

printfest intégré dans la plupart des coquilles de type Bourne et fish.

cpet catne sont généralement pas intégrés.

N'invoque cp /dev/null filepas maintenant les redirections shell, donc des choses comme:

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

vont être plus efficaces que:

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

(mais pas nécessairement:

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

).

Personnellement

Personnellement, j'utilise : > filedans des coquilles de type Bourne, et je n'utilise rien d'autre que des coquilles de type Bourne de nos jours.

Stéphane Chazelas
la source
Et alors dd of=file count=0?
kojiro
2
@kojiro, avec certaines implémentations de dd(comme Solaris 10 au moins), count=0est ignoré. dd if=/dev/null of=fileserait plus portable. Dans tous les cas, c'est indépendant du shell.
Stéphane Chazelas
D'accord, mais cela ne mérite pas moins d'être inclus que cp /dev/null file, non?
kojiro
2
@kojiro, cp /dev/null fileest un idiome commun. Je me limite à ceux-ci, il ne s'agit pas d'énumérer toutes les façons possibles.
Stéphane Chazelas
5

Vous voudrez peut-être regarder truncate, ce qui fait exactement cela: tronquer un fichier.

Par exemple:

truncate --size 0 file.txt

C'est probablement plus lent que l'utilisation true > file.txt.

Cependant, mon point principal est: truncateest destiné à tronquer des fichiers, alors que l'utilisation de> a pour effet secondaire de tronquer un fichier.

Fabien
la source
2
Troncature est agréable quand vous voulez tronquer un fichier à autre chose que 0. Cela dit, même sans coquille est une déclaration étrange: pouvez - vous décrire un contexte où truncateserait disponible, mais ni >ni les unistdbibliothèques C serait disponible?
kojiro
Pas vraiment. Il existe probablement une solution plus élégante pour chaque script ou langage de programmation disponible.
Fabian
3
truncateest un utilitaire FreeBSD, relativement récemment (2008) ajouté aux coreutils GNU (bien que le --sizestyle d'option long GNU soit spécifique à GNU), il n'est donc pas disponible dans les systèmes non GNU ou FreeBSD, et il n'est pas disponible dans les anciens systèmes GNU, Je ne dirais pas que c'est portable. cp /dev/null filefonctionnerait sans redirection de shell et serait plus portable.
Stéphane Chazelas
D'accord, je supprimerai ce commentaire sur la portabilité. Bien que votre définition de récent semble différer.
Fabian
2

La réponse dépend un peu de ce qui file.txtest et de la façon dont le processus y écrit!

Je citerai un cas d'utilisation courant: vous avez un fichier journal de plus en plus appelé file.txtet vous souhaitez le faire pivoter.

Par conséquent, vous copiez, par exemple, file.txtdans file.txt.save, puis tronquez file.txt.

Dans ce scénario, SI le fichier n'est pas ouvert par another_process(ex: another_processpourrait être un programme sortant vers ce fichier, par exemple un programme enregistrant quelque chose), alors vos 2 propositions sont équivalentes, et les deux fonctionnent bien (mais la 2e est préférable comme premier "cat / dev / null> file.txt" est une utilisation inutile de Cat et ouvre et lit également / dev / null).

Mais le vrai problème serait si le other_processest toujours actif et a toujours une poignée ouverte pour le fichier.txt.

Ensuite, 2 cas principaux se présentent, selon l' other processouverture du fichier:

  • Si l' other_processouvre normalement, la poignée pointera toujours vers l'ancien emplacement dans le fichier, par exemple à un décalage de 1200 octets. La prochaine écriture commencera donc à l'offset 1200, et vous aurez donc à nouveau un fichier de 1200 octets (+ tout ce que other_process a écrit), avec 1200 premiers caractères nuls! Pas ce que tu veux , je présume.

  • S'il est other_processouvert file.txten "mode d'ajout", chaque fois qu'il écrit, le pointeur cherchera activement à la fin du fichier. Par conséquent, lorsque vous le tronquerez, il "cherchera" jusqu'à l'octet 0, et vous n'aurez pas le mauvais effet secondaire! C'est ce que vous voulez (... généralement!)

Notez que cela signifie que vous devez, lorsque vous tronquez un fichier, vous assurer que tous ceux other_processqui écrivent toujours à cet emplacement l'ont ouvert en mode "ajout". Sinon, vous devrez les arrêter other_processet les redémarrer, afin qu'ils commencent à pointer vers le début du fichier au lieu de l'ancien emplacement.

Références: /programming//a/16720582/1841533 pour une explication plus claire et un bel exemple court de différence entre la journalisation en mode normal et en mode ajout sur /programming//a/984761/1841533

Olivier Dulac
la source
2
Très peu de cette réponse est pertinente ou répond à la question. La différence entre a cat /dev/null > fileet a > fileest a cat /dev/nullet cela ne fait aucune différence pour le fichier.
jw013
@ jw013: Vrai! Mais je voulais juste saisir l'opportunité de la question pour reformuler les informations "ce que vous voulez / pas ce que vous voulez", car elles ne sont pas très connues et pourraient frapper fort quelqu'un essayant de faire tourner les journaux (un cas courant où vous voulez tronquer un fichier).
Olivier Dulac
1
Il y a un temps et une place pour tout. Vos informations peuvent être utiles dans un autre contexte, mais elles n'appartiennent pas ici - vous devriez trouver un endroit plus approprié car personne ne tentant de faire pivoter les journaux ne cherchera dans cette question de redirection complètement indépendante. Ici, votre réponse est l'équivalent d'une mauvaise herbe numérique, tout comme une usine de citrouille autrement utile au milieu d'un champ de maïs serait considérée comme une mauvaise herbe.
jw013
1

J'aime cela et je l'utilise souvent car il a l'air plus propre et pas comme si quelqu'un avait accidentellement appuyé sur la touche de retour:

echo -n "" > file.txt

Devrait être intégré aussi?

awsm
la source
3
Il existe plusieurs façons de mettre à zéro un fichier. Je pense que KM. était uniquement intéressé à comprendre la différence entre les deux méthodes présentées dans la question.
drs
6
De nombreuses echoimplémentations ne prennent pas en charge -n(et sortiraient -n<SPC><NL>ici. printf '' > file.txtSeraient plus portables (au moins sur les systèmes modernes / POSIX).
Stéphane Chazelas