Pourquoi ls ls.out fait-il figurer "ls.out" dans la liste des noms?

26

Pourquoi $ ls > ls.out provoque-t-il l'inclusion de "ls.out" dans la liste des noms de fichiers du répertoire en cours? Pourquoi cela a-t-il été choisi? Pourquoi pas autrement?

    
posée edward torvalds 01.02.2016 - 18:10
la source

4 réponses

36

Lors de l’évaluation de la commande, la redirection > est résolue en premier: ainsi, au moment où ls exécute, le fichier de sortie a déjà été créé.

C'est aussi la raison pour laquelle la lecture et l'écriture sur le même fichier à l'aide de la redirection > dans la même commande tronque le fichier; au moment où la commande s'exécute, le fichier a déjà été tronqué:

$ echo foo >bar
$ cat bar
foo
$ <bar cat >bar
$ cat bar
$ 

Astuces pour éviter cela:

  • <<<"$(ls)" > ls.out (fonctionne pour toute commande devant s'exécuter avant la résolution de la redirection)

    La substitution de commande est exécutée avant l’évaluation de la commande externe, donc ls est exécuté avant la création de ls.out :

    $ ls
    bar  foo
    $ <<<"$(ls)" > ls.out
    $ cat ls.out 
    bar
    foo
    
  • ls | sponge ls.out (fonctionne pour toute commande devant s'exécuter avant la résolution de la redirection)

    sponge écrit dans le fichier uniquement lorsque l'exécution du reste du canal est terminée. Par conséquent, ls est exécuté avant la création de ls.out ( sponge est fourni avec le package moreutils ):

    $ ls
    bar  foo
    $ ls | sponge ls.out
    $ cat ls.out 
    bar
    foo
    
  • ls * > ls.out (fonctionne pour le cas spécifique de ls > ls.out )

    L’extension du nom de fichier est effectuée avant la résolution de la redirection, donc ls s'exécutera sur ses arguments, qui ne contiendront pas ls.out :

    $ ls
    bar  foo
    $ ls * > ls.out
    $ cat ls.out 
    bar
    foo
    $
    

Pourquoi les redirections sont-elles résolues avant que le programme / script / soit exécuté, je ne vois pas de raison spécifique pour que cela soit obligatoire à le faire, mais je vois deux raisons pour lesquelles mieux de le faire:

  • ne pas rediriger STDIN au préalable rendrait le programme / script / quoi que ce soit jusqu'à ce que STDIN soit redirigé;

  • ne pas rediriger STDOUT au préalable devrait obligatoirement faire en sorte que le shell mette en mémoire tampon la sortie du programme / script / peu importe jusqu'à ce que STDOUT soit redirigé;

Une perte de temps dans le premier cas et une perte de temps et de mémoire dans le second cas.

C'est exactement ce qui m'arrive, je ne prétends pas que ce sont les raisons réelles; mais je suppose que dans l’ensemble, si l’on avait le choix, ils iraient de toute façon avec la redirection pour les raisons susmentionnées.

    
réponse donnée kos 01.02.2016 - 18:18
la source
11

De man bash :

  

REDIRECTION

     

Avant d’exécuter une commande, ses entrées et sorties peuvent être redirigées     en utilisant une notation spéciale interprétée par le shell. La redirection permet     les descripteurs de fichier des commandes à dupliquer, à ouvrir, à fermer, à faire référence     vers différents fichiers, et peut changer les fichiers lus par la commande et     écrit à.

La première phrase suggère que la sortie est faite pour aller ailleurs que stdin avec une redirection juste avant l'exécution de la commande. Ainsi, pour être redirigé vers un fichier, le fichier doit d'abord être créé par le shell lui-même.

Pour éviter d’avoir un fichier, je vous suggère de rediriger d’abord la sortie vers le canal nommé, puis de la classer. Notez l'utilisation de & pour renvoyer le contrôle sur le terminal à l'utilisateur

DIR:/xieerqi
[email protected]:$ mkfifo /tmp/namedPipe.fifo                                                                         

DIR:/xieerqi
[email protected]:$ ls > /tmp/namedPipe.fifo &
[1] 14167

DIR:/xieerqi
[email protected]:$ cat /tmp/namedPipe.fifo > ls.out

Mais pourquoi?

Pensez à cela - où sera le résultat? Un programme a des fonctions comme printf , sprintf , puts , qui par défaut vont à stdout , mais leur sortie peut-elle être transférée au fichier si le fichier n'existe pas en premier lieu? C'est comme de l'eau. Pouvez-vous obtenir un verre d'eau sans mettre de verre sous le robinet en premier?

    
réponse donnée Sergiy Kolodyazhnyy 01.02.2016 - 18:17
la source
10

Je ne suis pas en désaccord avec les réponses actuelles. Le fichier de sortie doit être ouvert avant que la commande ne s'exécute ou que la commande n'ait nulle part où écrire sa sortie.

C'est parce que "tout est un fichier" dans notre monde. La sortie à l'écran est SDOUT (également appelé descripteur de fichier 1). Pour qu'une application écrive sur le terminal, ouvre fd1 et y écrit comme un fichier.

Lorsque vous redirigez une sortie d'une application dans un shell, vous modifiez fd1 pour qu'il pointe effectivement vers le fichier. Lorsque vous conduisez, vous modifiez le STDOUT d'une application pour en faire un autre (fd0).

Mais tout va bien, mais vous pouvez très facilement voir comment cela fonctionne avec strace . C'est assez lourd mais cet exemple est assez court.

strace sh -c "ls > ls.out" 2> strace.out

Dans strace.out , nous pouvons voir les faits saillants suivants:

open("ls.out", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3

Cela ouvre ls.out comme fd3 . Ecrire uniquement. Tronque (écrase) s'il existe, sinon crée.

fcntl(1, F_DUPFD, 10)                   = 10
close(1)                                = 0
fcntl(10, F_SETFD, FD_CLOEXEC)          = 0
dup2(3, 1)                              = 1
close(3)                                = 0

C'est un peu de jonglage. Nous shuntons STDOUT (fd1) pour fd10 et le fermons. C'est parce que nous n'émettons rien dans le STDOUT réel avec cette commande. Il se termine en dupliquant le descripteur d’écriture dans ls.out et en fermant l’original.

stat("/opt/wine-staging/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/home/oli/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/local/sbin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/local/bin/ls", 0x7ffc6bf028c0) = -1 ENOENT (No such file or directory)
stat("/usr/sbin/ls", 0x7ffc6bf028c0)    = -1 ENOENT (No such file or directory)
stat("/usr/bin/ls", 0x7ffc6bf028c0)     = -1 ENOENT (No such file or directory)
stat("/sbin/ls", 0x7ffc6bf028c0)        = -1 ENOENT (No such file or directory)
stat("/bin/ls", {st_mode=S_IFREG|0755, st_size=110080, ...}) = 0

Ceci est la recherche de l'exécutable. Une leçon peut-être pour ne pas avoir un long chemin;)

clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f0961324a10) = 31933
wait4(-1, [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 31933
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=31933, si_status=0, si_utime=0, si_stime=0} ---
rt_sigreturn()                          = 31933
dup2(10, 1)                             = 1
close(10)                               = 0

Ensuite, la commande s'exécute et le parent attend. Au cours de cette opération, tout STDOUT sera effectivement associé au descripteur de fichier ouvert sur ls.out . Lorsque l'enfant émet SIGCHLD , cela indique que le processus parent est terminé et qu'il peut reprendre. Il se termine avec un peu plus de jonglage et une clôture de ls.out .

Pourquoi y a-t-il beaucoup de jonglerie? Non, je ne suis pas tout à fait sûr non plus.

Bien sûr, vous pouvez modifier ce comportement. Vous pouvez mettre en mémoire tampon quelque chose comme sponge et qui sera invisible à partir de la commande précédente. Nous affectons toujours les descripteurs de fichiers, mais pas de manière visible pour les systèmes de fichiers.

ls | sponge ls.out
    
réponse donnée Oli 02.02.2016 - 14:25
la source
6

Il y a aussi un bel article sur Mise en place de redirection et d'opérateurs de pipeline en shell . Ce qui montre comment la redirection pourrait être implémentée pour que $ ls > ls.out puisse ressembler à:

main(){
    close(1); // Release fd no - 1
    open("ls.out", "w"); // Open a file with fd no = 1
    // Child process
    if (fork() == 0) {
        exec("ls"); 
    }
}
    
réponse donnée incBrain 01.02.2016 - 18:40
la source

Lire d'autres questions sur les étiquettes