$(<file)
(también funciona con `<file`
) es un operador especial del shell Korn copiado por zsh
y bash
. Se parece mucho a la sustitución de comandos, pero en realidad no lo es.
En shells POSIX, un comando simple es:
< file var1=value1 > file2 cmd 2> file3 args 3> file4
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ía file
), pero luego no sucede nada. Entonces
< file
Se abre file
para leer, pero luego no sucede nada ya que no hay comando. Entonces el file
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)
, si script
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)
- ni
$(0<> file)
- ni
$(< file; sleep 1)
- ni
$(< file; < file2)
luego
- todos menos la primera redirección se ignoran (se analizan)
- y se expande al contenido del archivo / heredoc / herestring (o lo que pueda leerse del descriptor de archivo si se usan cosas como
<&3
) menos los caracteres de línea nueva.
como si estuviera usando $(cat < file)
excepto que
- la lectura se realiza internamente por el shell y no por
cat
- no hay tubería ni proceso adicional involucrado
- Como consecuencia de lo anterior, dado que el código interno no se ejecuta en una subshell, cualquier modificación permanece a partir de entonces (como en
$(<${file=foo.txt})
o $(<file$((++n)))
)
- los errores de lectura (aunque no errores al abrir archivos o duplicar descriptores de archivos) se ignoran silenciosamente.
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
o 0< file
, sin <&3
, <<<here
, < a < b
...)
Sin embargo, excepto al emular otras conchas, en:
< file
<&3
<<< here...
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á como ksh
invocando a un localizador para que lo haga (ese localizador actúa como si cat
su stdout fuera una tubería).
Sin embargo, mientras que ksh
's $(< a < b)
se expandirían al contenido de a
, en zsh
, se expande con el contenido de a
y b
(o simplemente b
si la multios
opción está desactivada), $(< a > b)
sería copiar a
a b
y ampliar a nada, etc.
bash
tiene un operador similar pero con algunas diferencias:
Se permiten comentarios antes pero no después:
echo "$(
# getting the content of file
< file)"
funciona pero:
echo "$(< file
# getting the content of file
)"
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.
- por alguna razón, aunque
bash
no se invoca cat
, 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)
donde cat
sería un edificio cat
.
- Como consecuencia de lo anterior, cualquier cambio realizado dentro se pierde después (en el
$(<${file=foo.txt})
mencionado anteriormente, por ejemplo, esa $file
asignación se pierde después).
En bash
, IFS= read -rd '' var < file
(también funciona zsh
) 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]
en zsh
(en el zsh/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, en mksh
(uno de esos shells derivados de pdksh), en
var=$(<<'EOF'
That's multi-line
test with *all* sorts of "special"
characters
EOF
)
está 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
y bash
, 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.
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.