Estoy tratando de entender cómo exactamente Bash trata la siguiente línea:
$(< "$FILE")
Según la página de manual de Bash, esto es equivalente a:
$(cat "$FILE")
y puedo seguir la línea de razonamiento para esta segunda línea. Bash realiza una expansión variable en $FILE, ingresa la sustitución del comando, pasa el valor de $FILEa cat, cat emite el contenido de $FILEla salida estándar, la sustitución del comando finaliza reemplazando toda la línea con la salida estándar resultante del comando interno, y Bash intenta ejecutarlo como Un simple comando.
Sin embargo, para la primera línea que mencioné anteriormente, lo entiendo como: Bash realiza la sustitución de variables en $FILE, Bash se abre $FILEpara leer en la entrada estándar, de alguna manera la entrada estándar se copia en la salida estándar , finaliza la sustitución de comandos y Bash intenta ejecutar el estándar resultante salida.
¿Puede alguien explicarme cómo va el contenido de $FILEstdin a stdout?
fuente

bashinterpretará eso comocat filename", ¿quiere decir que este comportamiento es específico para la sustitución de comandos? Porque si corro< filenamesolo, bash no lo atrapa. No generará nada y me devolverá a un mensaje.cat < filenamealcat filenameque me opongo y puedo volver.|crea una tubería entre dos subprocesos (o, con algunos shells, desde un subproceso hasta la entrada estándar del shell). El operador de shell$(…)crea una tubería desde un subproceso al propio shell (no a su entrada estándar). El operador de shell<no involucra una tubería, solo abre un archivo y mueve el descriptor de archivo a la entrada estándar.< fileno es lo mismo quecat < file(excepto enzshdonde es como$READNULLCMD < file).< filees perfectamente POSIX y solo se abrefilepara leer y luego no hace nada (por lo quefileestá cerca de inmediato). Es$(< file)o`< file`ese es un operador especial deksh,zshybash(y el comportamiento se deja sin especificar en POSIX). Vea mi respuesta para más detalles.$(cmd1) $(cmd2)generalmente será lo mismo que$(cmd1; cmd2). Pero mira el caso dondecmd2está< file. Si decimos$(cmd1; < file), el archivo no se lee, pero$(cmd1) $(< file)sí. Por lo tanto, es incorrecto decir que$(< file)es solo un caso ordinario$(command)con un comando de< file.$(< …)es un caso especial de sustitución de comando, y no un uso normal de redirección.$(<file)(también funciona con`<file`) es un operador especial del shell Korn copiado porzshybash. Se parece mucho a la sustitución de comandos, pero en realidad no lo es.En shells POSIX, un comando simple es:
Todas las partes son opcionales, puede tener solo redirecciones, solo comandos, solo asignaciones o combinaciones.
Si hay redirecciones pero no hay comando, las redirecciones se realizan (por
> filelo tanto, se abriría y truncaríafile), pero luego no sucede nada. EntoncesSe abre
filepara leer, pero luego no sucede nada ya que no hay comando. Entonces elfilese cierra y eso es todo. Si$(< file)fuera una simple sustitución de comando , entonces se expandiría a nada.En la especificación POSIX , en
$(script), siscriptconsiste solo en redirecciones, eso produce resultados no especificados . Eso es para permitir ese comportamiento especial del shell Korn.En ksh (aquí probado con
ksh93u+), si el script consta de uno y solo un comando simple (aunque se permiten comentarios antes y después) que consiste solo en redirecciones (sin comando, sin asignación) y si la primera redirección es un stdin (fd 0) solo entrada (<,<<o<<<) redirección, entonces:$(< file)$(0< file)$(<&3)(también en$(0>&3)realidad, ya que es el mismo operador)$(< file > foo 2> $(whatever))pero no:
$(> foo < file)$(0<> file)$(< file; sleep 1)$(< file; < file2)luego
<&3) menos los caracteres de línea nueva.como si estuviera usando
$(cat < file)excepto quecat$(<${file=foo.txt})o$(<file$((++n))))En
zsh, es el mismo, salvo que ese comportamiento especial sólo se activa cuando sólo hay una redirección de entrada de archivo (<fileo0< file, sin<&3,<<<here,< a < b...)Sin embargo, excepto al emular otras conchas, en:
es decir, cuando solo hay redirecciones de entrada sin comandos, fuera de la sustitución de comandos, se
zshejecuta$READNULLCMD(un buscapersonas por defecto), y cuando hay redirecciones de entrada y salida,$NULLCMD(catpor defecto), incluso si$(<&3)no se reconoce como ese especial operador, todavía funcionará comokshinvocando a un localizador para que lo haga (ese localizador actúa como sicatsu stdout fuera una tubería).Sin embargo, mientras que
ksh's$(< a < b)se expandirían al contenido dea, enzsh, se expande con el contenido deayb(o simplementebsi lamultiosopción está desactivada),$(< a > b)sería copiaraaby ampliar a nada, etc.bashtiene un operador similar pero con algunas diferencias:Se permiten comentarios antes pero no después:
funciona pero:
se expande a la nada.
como en
zsh, solo una redirección de stdin de archivo, aunque no hay retroceso a a$READNULLCMD, por lo tanto$(<&3),$(< a < b)realice las redirecciones pero expanda a nada.bashno se invocacat, aún se bifurca un proceso que alimenta el contenido del archivo a través de una tubería, lo que lo convierte en una optimización mucho menor que en otros shells. En efecto, es como un lugar$(cat < file)dondecatsería un edificiocat.$(<${file=foo.txt})mencionado anteriormente, por ejemplo, esa$fileasignación se pierde después).En
bash,IFS= read -rd '' var < file(también funcionazsh) es una forma más efectiva de leer el contenido de un archivo de texto en una variable. También tiene la ventaja de preservar los caracteres de la nueva línea final. Consulte también$mapfile[file]enzsh(en elzsh/mapfilemódulo y solo para archivos normales) que también funciona con archivos binarios.Tenga en cuenta que las variantes basadas en pdksh
kshtienen algunas variaciones en comparación con ksh93. De interés, enmksh(uno de esos shells derivados de pdksh), enestá optimizado porque el contenido del documento here (sin los caracteres finales) se expande sin utilizar un archivo o canalización temporal, como es el caso de los documentos here, lo que lo convierte en una sintaxis efectiva de citas de varias líneas.
Ser portátil para todas las versiones de
ksh,zshybash, lo mejor es limitarse a$(<file)evitar solo comentarios y tener en cuenta que las modificaciones a las variables realizadas dentro pueden o no conservarse.fuente
$(<)sea un operador en los nombres de archivo? ¿Está<en$(<)un operador de redireccionamiento, o no es un operador por sí solo, y debe ser parte de todo el operador$(<)?$(<file)está destinado a expandir el contenido defileuna manera similar a como lo$(cat < file)haría. La forma en que se realiza varía de un shell a otro, que se describe en detalle en la respuesta. Si lo desea, puede decir que es un operador especial que se activa cuando lo que parece una sustitución de comando (sintácticamente) contiene lo que parece una única redirección de stdin (sintácticamente), pero nuevamente con advertencias y variaciones dependiendo del shell como se enumera aquí .n<&myn>&mhacer lo mismo? No lo sabía, pero supongo que no es demasiado sorprendente.dup(m, n). Puedo ver alguna evidencia de ksh86 usando stdio y algofdopen(fd, "r" or "w"), por lo que podría haber importado entonces. Pero usar stdio en un shell no tiene mucho sentido, así que no espero que encuentres ningún shell moderno en el que eso haga la diferencia. Una diferencia es que>&nesdup(n, 1)(abreviatura de1>&n), mientras que<&nesdup(n, 0)(abreviatura de0<&n).dup2();dup()toma solo un argumento y, comoopen(), usa el descriptor de archivo más bajo disponible. (Hoy aprendí que hay unadup3()función )Porque lo
bashhace internamente para usted, expande el nombre del archivo y agrega el archivo a la salida estándar, como si fuera a hacerlo$(cat < filename). Es una función bash, tal vez necesites mirar elbashcódigo fuente para saber exactamente cómo funciona.Aquí la función para manejar esta característica (desde
bashcódigo fuente, archivobuiltins/evalstring.c):Una nota que
$(<filename)no es exactamente equivalente a$(cat filename); este último fallará si el nombre del archivo comienza con un guión-.$(<filename)era originalmente deksh, y fue agregado abashdeBash-2.02.fuente
cat filenamefallará si el nombre del archivo comienza con un guión porque cat acepta las opciones. Puede solucionar eso en la mayoría de los sistemas modernos concat -- filename.Piense en la sustitución de comandos como ejecutar un comando como de costumbre y volcar la salida en el punto donde ejecuta el comando.
foo=$(echo "bar")establecerá el valor de la variable$fooenbar; La salida del comandoecho bar.Sustitución de comando
fuente
$(< file), y él no necesita un tutorial sobre el caso general. Si está diciendo que$(< file)es solo un caso ordinario$(command)con un comando de< file, entonces está diciendo lo mismo que Adam Katz dice , y ambos están equivocados.