Pourquoi la substitution de processus BASH ne fonctionne-t-elle pas avec certaines commandes?

29

Parfois, la substitution de processus ne fonctionnera pas comme prévu. Voici un exemple:

Contribution:

gcc <(echo 'int main(){return 0;}')

Sortie:

/dev/fd/63: file not recognized: Illegal seek
collect2: error: ld returned 1 exit status

Contribution:

Mais cela fonctionne comme prévu lorsqu'il est utilisé avec une commande différente:

grep main <(echo 'int main(){return 0;}')

Sortie:

int main(){return 0;}

J'ai remarqué des échecs similaires avec d'autres commandes (c'est-à-dire que la commande qui attend le fichier de la substitution de processus ne peut pas utiliser /dev/fd/63ou similaire). Cet échec avec gccn'est que le plus récent. Existe-t-il une règle générale que je devrais connaître pour déterminer quand la substitution de processus échouera de cette manière et ne devrait pas être utilisée?

J'utilise cette version BASH sur Ubuntu 12.04 (j'ai également vu cela dans arch et debian):
GNU bash, version 4.3.11 (1) -release (i686-pc-linux-gnu)

Lotney
la source
1
illegal seekressemble à la réponse - celui |pipequi bashpointe le programme exécuté n'est pas un fichier recherché. probablement si vous ne pouvez pas réussir echo data | command /dev/fd/0un programme, vous aurez une chance similaire avec <(cmd). Il ne fournit pas de fichier sur disque - il remplace simplement un argument qui pointe vers un descripteur de fichier pipe.
mikeserv
2
Dans ce cas particulier, bien que gcc puisse accepter une entrée standard, il (par défaut) utilise l'extension de nom de fichier pour déterminer la langue. Essayez donc gcc -xc <(echo 'int main(){return 0;}')(qui définit Cexplicitement la langue ).
Steeldriver
J'ai été dirigé ici en réponse à ma propre question, qui semble être un autre exemple de cela. superuser.com/questions/1243405 . Merci d'avoir formulé la question mieux que je n'ai pu.
Jonathan Hartley

Réponses:

33

La substitution de processus entraîne un fichier spécial (comme /dev/fd/63dans votre exemple) qui se comporte comme l'extrémité en lecture d'un canal nommé. Ce fichier peut être ouvert et lu, mais pas écrit, pas recherché.

Les commandes qui traitent leurs arguments comme de purs flux fonctionnent tandis que les commandes qui s'attendent à rechercher dans les fichiers qui leur sont donnés (ou à y écrire) ne fonctionneront pas. Le genre de commande qui fonctionnera est ce qui est généralement considéré comme un filtre: cat, grep, sed, gzip, awk, etc ... Un exemple d'une commande qui ne fonctionnera pas est un éditeur comme viou une opération de fichier comme mv.

gccsouhaite pouvoir effectuer un accès aléatoire sur ses fichiers d'entrée pour détecter dans quelle langue ils sont écrits. Si vous donnez plutôt gccun indice sur la langue du fichier d'entrée, il est heureux de diffuser le fichier:

gcc -x c <(echo 'int main(){return 0;}')

La forme plus simple et plus simple sans substitution de processus fonctionne également:

echo 'int main(){return 0;}' | gcc -x c -

Notez que cela n'est pas spécifique à bash. Tous les shells qui prennent en charge la substitution de processus se comportent de la même manière.

Celada
la source
+1 pour la solution de contournement gcc mais je ne suis pas sûr de votre point concernant les fichiers. Le <()format doit agir comme un fichier à toutes fins utiles. En fait, je ne connais pas de commandes qui attendent un fichier qui ne sera pas satisfait <(). Ceux qui ne fonctionnent pas sont ceux qui attendent des noms de fichiers , pas des fichiers. Par exemple, grep -fattend un fichier et fonctionne correctement avec <().
terdon
4
@terndon Produit à coup sûr <()un nom de fichier (la construction se développe /proc/self/fd/somethingsur mon système). Ce nom, lorsqu'il est ouvert, agit comme l'extrémité de lecture d'un tube nommé ( S_IFIFO) plutôt que d'un fichier normal ( S_IFREG) dans lequel il est pris en charge read()et d'autres mais pas seek().
Celada
7
Notez que zshprend en charge une troisième forme de substitution de processus qui utilise des fichiers temporaires spécialement à cet effet:gcc =(echo 'int main(){return 0;}')
Stéphane Chazelas
peut-être lié , mais fonctionne avec <(echo '...')mais pas avec <(git show ...). une idée pourquoi cela pourrait être?
Jörn Hees
2
GCC "n'effectue pas d'accès aléatoire sur ses fichiers d'entrée pour détecter dans quelle langue ils sont écrits". Il regarde juste l'extension du nom de fichier. Si le nom de fichier n'a pas d'extension (ou s'il en a une qui n'est pas reconnue), GCC suppose que le fichier est un fichier objet ou un script de l'éditeur de liens et le transmet à ld(qui détecte les formats d'objet). -xn'est pas un indice; c'est une déclaration. Si vous spécifiez -x f95, GCC transmettra le fichier au compilateur Fortran-95 indépendamment de son nom ou de son contenu. Voir gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Overall-Options.html
rici