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 $FILE
a cat
, cat emite el contenido de $FILE
la 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 $FILE
para 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 $FILE
stdin a stdout?
fuente
bash
interpretará eso comocat filename
", ¿quiere decir que este comportamiento es específico para la sustitución de comandos? Porque si corro< filename
solo, bash no lo atrapa. No generará nada y me devolverá a un mensaje.cat < filename
alcat filename
que 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.< file
no es lo mismo quecat < file
(excepto enzsh
donde es como$READNULLCMD < file
).< file
es perfectamente POSIX y solo se abrefile
para leer y luego no hace nada (por lo quefile
está cerca de inmediato). Es$(< file)
o`< file`
ese es un operador especial deksh
,zsh
ybash
(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 dondecmd2
está< 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 porzsh
ybash
. 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
> file
lo tanto, se abriría y truncaríafile
), pero luego no sucede nada. EntoncesSe abre
file
para leer, pero luego no sucede nada ya que no hay comando. Entonces elfile
se 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)
, siscript
consiste 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 (<file
o0< 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
zsh
ejecuta$READNULLCMD
(un buscapersonas por defecto), y cuando hay redirecciones de entrada y salida,$NULLCMD
(cat
por defecto), incluso si$(<&3)
no se reconoce como ese especial operador, todavía funcionará comoksh
invocando a un localizador para que lo haga (ese localizador actúa como sicat
su 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 dea
yb
(o simplementeb
si lamultios
opción está desactivada),$(< a > b)
sería copiara
ab
y ampliar a nada, etc.bash
tiene 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.bash
no 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)
dondecat
sería un edificiocat
.$(<${file=foo.txt})
mencionado anteriormente, por ejemplo, esa$file
asignació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/mapfile
módulo y solo para archivos normales) que también funciona con archivos binarios.Tenga en cuenta que las variantes basadas en pdksh
ksh
tienen 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
,zsh
ybash
, 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 defile
una 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<&m
yn>&m
hacer 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>&n
esdup(n, 1)
(abreviatura de1>&n
), mientras que<&n
esdup(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
bash
hace 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 elbash
código fuente para saber exactamente cómo funciona.Aquí la función para manejar esta característica (desde
bash
có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 abash
deBash-2.02
.fuente
cat filename
fallará 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$foo
enbar
; 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.