¿Por qué la sustitución del proceso BASH no funciona con algunos comandos?

29

En ocasiones, la sustitución del proceso no funcionará como se esperaba. Aquí hay un ejemplo:

Entrada:

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

Salida:

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

Entrada:

Pero funciona como se esperaba cuando se usa con un comando diferente:

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

Salida:

int main(){return 0;}

He notado fallas similares con otros comandos (es decir, el comando que espera que el archivo de la sustitución del proceso no pueda usar /dev/fd/63o similar). Este fracaso con gcces solo el más reciente. ¿Hay alguna regla general que deba tener en cuenta para determinar cuándo la sustitución del proceso fallará de esta manera y no debería usarse?

Estoy usando esta versión de BASH en Ubuntu 12.04 (también lo he visto en arch y debian):
GNU bash, versión 4.3.11 (1) -release (i686-pc-linux-gnu)

Lotney
fuente
1
illegal seekparece la respuesta: lo |pipeque bashapunta al programa ejecutado no es un archivo buscable. Probablemente si no puede tener éxito echo data | command /dev/fd/0en un programa, entonces tendrá una suerte similar w / <(cmd). No proporciona un archivo en el disco, solo sustituye un argumento que apunta a un descriptor de archivo de tubería.
mikeserv
2
En este caso particular, aunque gcc puede aceptar entradas estándar, (por defecto) usa la extensión del nombre de archivo para determinar el idioma. Así que intente gcc -xc <(echo 'int main(){return 0;}')(que establece el idioma Cexplícitamente).
Steeldriver
Fui dirigido aquí en respuesta a mi propia pregunta, que parece ser otro ejemplo de esto. superuser.com/questions/1243405 . Gracias por formular la pregunta mejor de lo que pude.
Jonathan Hartley el

Respuestas:

33

La sustitución del proceso da como resultado un archivo especial (como /dev/fd/63en su ejemplo) que se comporta como el final de lectura de una tubería con nombre. Este archivo puede abrirse y leerse, pero no puede escribirse ni buscarse.

Los comandos que tratan sus argumentos como flujos puros funcionan mientras que los comandos que esperan buscar en los archivos que reciben (o que les escriben) no funcionarán. El tipo de comando que el trabajo es lo que se considera generalmente como un filtro: cat, grep, sed, gzip, awk, etc ... Un ejemplo de un comando que no va a funcionar es un editor como vio una operación de archivo como mv.

gccdesea poder realizar un acceso aleatorio en sus archivos de entrada para detectar en qué idioma están escritos. Si, en cambio, da gccuna pista sobre el idioma del archivo de entrada, se complace en transmitir el archivo:

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

La forma más simple y directa sin sustitución del proceso también funciona:

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

Tenga en cuenta que esto no es específico de bash. Todos los shells que admiten la sustitución de procesos se comportan de la misma manera.

Celada
fuente
+1 para la solución alternativa de gcc pero no estoy seguro acerca de su punto con respecto a los archivos. El <()formato debe actuar como un archivo para todos los efectos. De hecho, no conozco ningún comando que espere un archivo con el que no esté satisfecho <(). Los que no funcionan son aquellos que esperan nombres de archivos , no archivos. Por ejemplo, grep -fespera un archivo y funciona bien con <().
terdon
44
@terndon Seguro <()produce un nombre de archivo (la construcción se expande /proc/self/fd/somethingen mi sistema). Este nombre, cuando se abre, actúa como el final de lectura de una canalización con nombre ( S_IFIFO) en lugar de un archivo normal ( S_IFREG) en el que se admiten read()y otros, pero no seek().
Celada
77
Tenga en cuenta que zshadmite una tercera forma de sustitución de procesos que utiliza archivos temporales especialmente para ese fin:gcc =(echo 'int main(){return 0;}')
Stéphane Chazelas
quizás relacionado , pero funciona con <(echo '...')pero no con <(git show ...). alguna idea de por qué podría ser?
Jörn Hees
2
GCC no "realiza acceso aleatorio en sus archivos de entrada para detectar en qué idioma están escritos". Solo mira la extensión del nombre de archivo. Si el nombre de archivo no tiene una extensión (o si tiene una que no se reconoce), GCC asume que el archivo es un archivo de objeto o script de enlace y lo pasa ld(que detecta formatos de objeto). -xno es una pista; Es una declaración. Si lo especifica -x f95, GCC pasará el archivo al compilador Fortran-95 independientemente de su nombre o contenido. Ver gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Overall-Options.html
rici