Est-ce que> & - est plus efficace que> / dev / null?

58

Hier, j'ai lu ce commentaire SO qui dit que dans le shell (au moins bash) >&-"a le même résultat que" >/dev/null.

Ce commentaire fait en réalité référence au guide ABS comme source d'informations. Mais cette source dit que la >&-syntaxe "ferme les descripteurs de fichier".

Il n’est pas clair pour moi si les deux actions de fermeture d’un descripteur de fichier et de sa redirection vers le périphérique null sont totalement équivalentes. Donc ma question est: sont-ils?

En apparence, fermer un descripteur revient à fermer une porte, mais le rediriger vers un périphérique nul ouvre la porte aux limbes! Les deux ne me semblent pas exactement pareils parce que si je vois une porte fermée, je ne tenterai rien, mais si je vois une porte ouverte, je supposerai que je peux le faire.

En d’autres termes, je me suis toujours demandé si cela >/dev/nullsignifiait cat mybigfile >/dev/nulltraiter tous les octets du fichier et l’écrire dans /dev/nulllequel l’oublie. D'un autre côté, si le shell rencontre un descripteur de fichier fermé, j'ai tendance à penser (mais je ne suis pas sûr) qu'il n'écrira simplement rien, bien que la question de savoir s'il litcat toujours chaque octet reste posée .

Ce commentaire dit >&-et >/dev/null" devrait " être le même, mais ce n'est pas une réponse si retentissante pour moi. J'aimerais avoir une réponse plus autoritaire avec une référence au standard ou à la source ou pas ...

jamadagni
la source
Si je voulais être un organisme de bienfaisance, je dirais que le commentaire ne signifiait pas qu'ils avaient été mis en œuvre de la même manière, mais qu'ils avaient le même résultat final, à savoir vous empêcher de voir le résultat du programme.
Barmar

Réponses:

71

Non, vous ne voulez certainement pas fermer les descripteurs de fichier 0, 1 et 2.

Si vous le faites, la première fois que l'application ouvre un fichier, il deviendra stdin / stdout / stderr ...

Par exemple, si vous le faites:

echo text | tee file >&-

Lorsque tee(du moins certaines implémentations, comme busybox ') ouvrent le fichier en écriture, celui-ci sera ouvert dans le descripteur de fichier 1 (stdout). Alors teeva écrire textdeux fois dans file:

$ echo text | strace tee file >&-
[...]
open("file", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 1
read(0, "text\n", 8193)                 = 5
write(1, "text\n", 5)                   = 5
write(1, "text\n", 5)                   = 5
read(0, "", 8193)                       = 0
exit_group(0)                           = ?

Cela a été connu pour causer des failles de sécurité. Par exemple:

chsh 2>&-

Et chsh(une application setuid) peut finir par écrire des messages d'erreur dans /etc/passwd.

Certains outils et même certaines bibliothèques tentent de se protéger de cela. Par exemple, GNU teedéplacera le descripteur de fichier au-dessus de 2 si les fichiers ouverts en écriture se voient attribuer les valeurs 0, 1 et 2, alors que ce teen'est pas le cas pour busybox .

La plupart des outils, s'ils ne peuvent pas écrire sur stdout (parce que, par exemple, ce n'est pas ouvert), enverront un message d'erreur sur stderr (dans la langue de l'utilisateur, ce qui signifie un traitement supplémentaire pour ouvrir et analyser les fichiers de localisation ...), donc ce sera nettement moins efficace et peut entraîner l'échec du programme.

En tout cas, ça ne sera pas plus efficace. Le programme fera toujours un write()appel système. Cela ne peut être plus efficace que si le programme abandonne l'écriture sur stdout / stderr après le premier write()appel système ayant échoué , mais les programmes ne le font généralement pas. Ils finissent généralement avec une erreur ou continuent d'essayer.

Stéphane Chazelas
la source
4
Je pense que cette réponse serait encore meilleure si le dernier paragraphe était en haut (puisque c'est ce qui répond le plus directement à la question du PO), et il a ensuite expliqué pourquoi c'est une mauvaise idée même si cela fonctionnait généralement. Mais je vais le prendre, avoir un vote positif. ;)
un CVn
@ StéphaneChazelas: Comme Michael l'a dit, je m'attendais au dernier paraphe, mais merci de l'avoir clarifié ne crée probablement que des problèmes. Donc, je suppose qu’une question accessoire serait la suivante: quand la fermeture d’une FD sera-t-elle utile? Ou devrais-je poser cette question séparément?
Jamadagni
@jamadagni, voir unix.stackexchange.com/search?q=user%3A22565+%223%3E%26-%22 pour quelques exemples
Stéphane Chazelas
1
@jamadagni Si le lien fourni par Stéphane ne répond pas à la question, je dirais que cela ressemble au début d'une question distincte car il n'est pas directement lié à l'efficacité relative des deux méthodes.
un CVn
1
J'apprécie que Stéphane commence par cet important avertissement de sécurité, car il serait moins visible si le dernier paragraphe était en haut. +1 de moi.
Olivier Dulac
14

IOW, je me suis toujours demandé si cela >/dev/nullsignifiait que l' cat mybigfile >/dev/nullon traiterait chaque octet du fichier et l'écrirait dans /dev/nulllequel on l'oublie.

Ce n'est pas une réponse complète à votre question, mais oui, voici comment cela fonctionne.

catlit le (s) fichier (s) nommé (s) ou l'entrée standard si aucun fichier n'est nommé, et envoie à sa sortie standard le contenu de ceux-ci jusqu'à ce qu'il rencontre un EOF (incluant l'entrée standard) avec le dernier fichier nommé. C'est son travail.

En ajoutant, >/dev/nullvous redirigez la sortie standard vers / dev / null. C'est un fichier spécial (un nœud de périphérique) qui jette tout ce qui y est écrit (et renvoie immédiatement EOF en lecture). Notez que la redirection d'E / S est une fonctionnalité fournie par le shell, et non par chaque application, et que le nom / dev / null n'a rien de magique , mais seulement ce qui existe dans la plupart des systèmes de type Unix .

Il est également important de noter que les mécanismes spécifiques des nœuds de périphériques varient d’un système d’exploitation à l’autre, mais cat (qui, dans un système GNU, signifie coreutils) est multiplate-forme (le même code source doit être exécuté au moins sous Linux et Hurd) et ne peuvent donc pas dépendre de dépendances de noyaux de système d’exploitation spécifiques. En outre, cela fonctionne toujours si vous créez un alias / dev / null (sous Linux, cela signifie un nœud de périphérique avec le même numéro de périphérique majeur / mineur) avec un autre nom. Et il y a toujours le cas d'écrire ailleurs qui se comporte effectivement de la même manière (par exemple, / dev / zero).

Il s’ensuit qu’il catn’est pas au courant des propriétés spéciales de / dev / null et qu’il n’est probablement pas au courant de la redirection au départ, mais il doit toujours effectuer exactement le même travail: il lit les fichiers nommés et affiche le contenu du fichier. le / ces fichier (s) à sa sortie standard. Le fait que la sortie standard de catva dans un vide ne concerne pas en catsoi.

un CVn
la source
2
Pour étendre votre réponse: Oui, cat mybigfile > /dev/nullfera catlire chaque octet de la bigfilemémoire. Et, pour chaque noctet, il appellera write(1, buffer, n). À l'insu du catprogramme, writeils ne feront absolument rien (à l'exception peut-être d'une comptabilité triviale). Ecrire à /dev/nullne nécessite pas de traiter chaque octet.
G-Man dit 'Réintégrez Monica'
2
Je me souviens que j'ai été époustouflé lorsque j'ai lu la source du noyau Linux du périphérique / dev / null. Je m'attendais à ce qu'il y ait un système élaboré de tampons de libération (), etc., mais non, c'est simplement un retour ().
Brian Minton
4
@ G-Man: Je ne sais pas si vous pouvez garantir que cela sera vrai dans tous les cas. Je ne trouve pas de preuves pour le moment, mais je me souviens de certaines implémentations de l'une catou de l'autre cpqui fonctionneraient en mmapmettant en mémoire de gros morceaux du fichier source, puis en appelant write()la région mappée. Si vous écrivez sur /dev/null, l' write()appel sera immédiatement renvoyé sans erreur dans les pages du fichier source, de sorte qu'il ne sera jamais réellement lu à partir du disque.
Nate Eldredge
2
En outre, quelque chose comme GNU catfonctionne sur de nombreuses plates-formes, mais un coup d'œil sur le code source montrera de nombreux #ifdefs: il ne s'agit pas littéralement du même code que celui qui s'exécute sur toutes les plates-formes et de nombreuses sections dépendant du système.
Nate Eldredge
@NateEldredge: C'est un point intéressant, mais je ne faisais que développer la réponse de Michael. Vous ne me contredisez donc pas autant que vous contredisiez Michael.
G-Man dit 'Réintégrez Monica'