Pourquoi «sort <(ls -l)» `fonctionne mais` sort <(ls -l) `échoue?

32

Aujourd'hui, j'apprends quelque chose sur fifo avec cet article: Introduction aux pipes nommées , qui est mentionné cat <(ls -l).

J'ai fait quelques expériences en utilisant sort < (ls -l), ce qui fait apparaître une erreur:

-bash: syntax error near unexpected token `('`

Ensuite, j'ai découvert que j'avais mal ajouté un espace supplémentaire dans la commande.

Mais, pourquoi cette commande supplémentaire conduira à cet échec? Pourquoi le symbole de redirection doit-il être proche de (?

Zen
la source
Il convient de noter que * nix shells divise les éléments en fonction des espaces, ce qui crée les jetons mentionnés par Alec.
poussins

Réponses:

45

Parce que ce n'est pas un <, c'est un <()qui est complètement différent. C'est ce qu'on appelle la substitution de processus . Il s'agit d'une fonctionnalité de certains shells qui vous permet d'utiliser la sortie d'un processus en tant qu'entrée pour un autre.

Les opérateurs >et <redirigent la sortie vers et l’entrée depuis les fichiers . L' <()opérateur traite des commandes (processus), pas des fichiers. Quand tu cours

sort < (ls)

Vous essayez d'exécuter la commande lsdans un sous-shell (c'est ce que signifient les parenthèses), puis de transmettre ce sous-shell en tant que fichier d'entrée sort. Ceci, cependant, n'est pas une syntaxe acceptée et vous obtenez l'erreur que vous avez vue.

terdon
la source
3
Votre réponse est bonne, mais then sort is attempting to read the subshell as its input file→ c'est évidemment faux, car Bash n'analysera même pas la syntaxe. Ni est lsni sortest réellement exécuté.
sleblanc
1
@sebleblanc fair point, a reformulé la réponse, merci.
terdon
1
Il n'y a pas de sous-shell dans ce cas. < (ls)n'est pas un jeton valide ici.
jeudi
@cuonglm no, car bash le traite comme une erreur de syntaxe. Mon point est que (ls)cela fonctionnerait lsdans un sous-shell.
terdon
22

Parce que c'est comme ça que ça se passe.

<(...)in bashest la syntaxe pour la substitution de processus. Il est copié du même opérateur dans ksh.

<, (, ), |, &, ;Sont des jetons lexicaux spéciaux bashqui sont utilisés pour former des opérateurs spéciaux dans différentes combinaisons. <, <(, <<, <&... ont chacun leur rôle. <est pour la redirection. <file, < fileredirigerait l’entrée d’un fichier. <'(file)'redirige l'entrée d'un fichier appelé (file), mais <(file)est un opérateur différent qui n'est pas un opérateur de redirection.

< (file)serait <suivi de (file). Dans ce contexte, dans bash, (file)n'est pas valide. (...)peut être valide en tant que jeton unique dans certains contextes tels que:

(sub shell)
func () {
  ...
}
var=(foo bar)

Mais pas dans

sort < (cmd)

Dans la fishcoquille, c'est différent. In fish, (...)est utilisé pour la substitution de commande (l'équivalent de $(...)in bash). Et <est destiné à la redirection d’entrée comme dans les shells Bourne-like.

Alors dans fish:

sort <(echo file)

serait la même chose que:

sort < (echo file)

C'est:

sort < file

Mais c'est quelque chose de complètement différent de bashla substitution de processus.

Dans le yashshell, un autre shell POSIX <(...)n’est pas destiné à la substitution de processus, mais à la redirection de processus.

Là dedans,

sort <(ls -l)

Court pour:

sort 0<(ls -l)

est un opérateur de redirection. C'est plus ou moins équivalent à:

ls -l | sort

Alors que dans bash, le <(ls -l)est étendu au chemin d'un tuyau, il ressemble donc beaucoup à:

ls -l | sort /dev/fd/0

Dans zsh, (...)est surchargé en tant qu'opérateur de globbing ( (*.txt|*.png)développerait les fichiers txtet les pngfichiers) et en tant que qualificateur de globes ( *(/)par exemple, les fichiers de répertoires).

Dans zsh, dans:

sort < (ls -l)

Cela (ls -l)serait traité comme un qualificatif global. Le lqualificatif glob est de faire correspondre le nombre de liens et attend un nombre après l(comme dans la ls -ld ./*(l2)liste des fichiers avec 2 liens), c'est pourquoi vous obtenez une zsh: number expectederreur.

sort < (w)aurait donné une zsh: no matches found: (w)erreur à la place car (w)correspond aux fichiers avec un nom vide qui sont en écriture.

sort < (w|cat)aurait trié le contenu des fichiers wet / ou du catrépertoire en cours ...

Stéphane Chazelas
la source
pourquoi sort < $(ls -l)donne cette erreur:bash: $(ls -l): ambiguous redirect
Edward Torvalds
@edwardtorvalds, car $(ls -l)s'étend à plus d'un mot. Utilisez des guillemets pour empêcher split + glob ( sort < "$(echo file)"). Notez que le comportement ou bashdiffère de celui de POSIX sh dans le fait que bash effectue cette division + glob là aussi bien quand il n’est pas interactif (pas quand on l’appelle comme shsi).
Stéphane Chazelas
en regardant, ls -l | sort /dev/fd/0je peux dire que la sortie de ls -lest stockée dans /dev/fd/0et que la sortcommande la lit pour donner la sortie souhaitée. J'utilise tail -f --retry /dev/fd/0pour surveiller ce fichier mais je n'obtiens aucune sortie. Pourquoi? Comment puis-je lire ce fichier?
Edward Torvalds le
Dans le poisson, vous pouvez utiliser (foo | psub)pour obtenir une substitution du processus d’entrée; il n'y a pas encore de substitut (ha) pour la substitution du processus de sortie.
Zanchey