Considera este script:
tmpfile=$(mktemp)
cat <<EOS > "$tmpfile"
line 1
line 2
line 3
EOS
cat <(tail -1 "$tmpfile") "$tmpfile"
Esto funciona y produce:
line 3
line 1
line 2
line 3
Digamos que nuestra fuente de entrada, en lugar de ser un archivo real, era stdin:
cat <<EOS | # what goes here now?
line 1
line 2
line 3
EOS
¿Cómo modificamos el comando?
cat <(tail -1 "$tmpfile") "$tmpfile"
¿De modo que todavía produce la misma salida, en este contexto diferente?
NOTA: El Heredoc específico que estoy buscando, así como el uso de un Heredoc en sí mismo, es meramente ilustrativo. Cualquier respuesta aceptable debe suponer que está recibiendo datos arbitrarios a través de stdin .

Respuestas:
Tratar:
Ejemplo
Defina una variable con nuestra entrada:
Ejecute nuestro comando:
Alternativamente, por supuesto, podríamos usar un documento aquí:
Cómo funciona
x=x $0 ORSEsto agrega cada línea de entrada a la variable
x.En awk,
ORSes el separador de registro de salida . Por defecto, es un carácter de nueva línea.END{printf "%s", $0 ORS x}Después de haber leído todo el archivo, imprime la última línea
$0, seguida del contenido de todo el archivox.Como esto lee toda la entrada en la memoria, no sería apropiado para entradas grandes ( por ejemplo, gigabytes).
fuente
tee, pero de un stdin y un archivo, estaríamos canalizando el mismo stdin en dos sustituciones de proceso diferentes. o algo que sería más o menos equivalente a eso?Si stdin apunta a un archivo que se puede buscar (como en el caso de los documentos aquí de bash (pero no todos los demás shell) que se implementan con archivos temporales), puede obtener la cola y luego buscar antes de leer el contenido completo:
Los operadores de búsqueda están disponibles en los shells
zshoksh93, o los lenguajes de script como tcl / perl / python, pero no enbash. Pero siempre puede llamar a esos intérpretes más avanzadosbashsi tiene que usarbash.O
Ahora, eso no funcionará cuando stdin apunte a archivos no buscables como una tubería o un socket. Entonces, la única opción es leer y almacenar (en la memoria o en un archivo temporal ...) toda la entrada.
Ya se han dado algunas soluciones para almacenar en la memoria.
Con un archivo temporal, con
zsh, puedes hacerlo con:Si en Linux, con
basho conzshcualquier shell que use archivos temporales para documentos aquí, en realidad podría usar el archivo temporal creado por un documento aquí para almacenar la salida:fuente
El problema con traducir esto a algo que usa
tailes quetailnecesita leer todo el archivo para encontrar el final. Para usar eso en su tubería, necesitatail.cat.La parte difícil no es duplicar el contenido del documento (lo
teehace) sino lograr que la salidatailsuceda antes de que salga el resto del documento, sin usar un archivo temporal intermedio.El uso
sed(oawk, como lo hace John1024 ) elimina el doble análisis de los datos y el problema de ordenar almacenando los datos en la memoria.La
sedsolución que propongo es1{h;d;}, almacene la primera línea en el espacio de espera, tal cual, y salte a la siguiente línea.H, agregue entre sí la línea al espacio de espera con una nueva línea incrustada.${G;p;}, agregue el espacio de espera a la última línea con una nueva línea incrustada e imprima los datos resultantes.Esta es una traducción bastante literal de la solución de John1024
sed, con la advertencia de que el estándar POSIX solo garantiza que el espacio de retención sea de al menos 8192 bytes (8 KiB; pero recomienda que este búfer se asigne dinámicamente y se expanda según sea necesario, lo que tanto GNUsedy BSD losedestá haciendo).Si te permites usar una tubería con nombre:
Esto se utiliza
teepara enviar los datos hacia abajomypipey al mismo tiempo acat. Lacatutilidad primero leerá la salida detail(que leemypipe, queteeestá escribiendo), y luego agregará la copia del documento que viene directamentetee.Sin embargo, hay una falla grave en esto, ya que si el documento es demasiado grande (más grande que el tamaño del búfer de la tubería),
teela escrituramypipey elcatbloqueo se bloquearán mientras se espera que la tubería (sin nombre) se vacíe. No se vaciaría hasta que secatleyera.catno leería de él hasta quetailhubiera terminado. Ytailno terminaría hasta queteehubiera terminado. Esta es una situación clásica de punto muerto.La variación
tiene el mismo problema
fuente
seduno no funciona si la entrada tiene solo una línea (tal vezsed '1h;1!H;$!d;G'). También tenga en cuenta que variassedimplementaciones tienen un límite bajo en el tamaño de su patrón y tienen espacio.Hay una herramienta nombrada
peeen una colección de utilidades de línea de comandos generalmente empaquetadas con el nombre "moreutils" (o recuperables de su sitio web de origen ).Si puede tenerlo en su sistema, entonces el equivalente para su ejemplo sería:
El orden de los comandos ejecutados
peees importante porque se ejecutan en la secuencia proporcionada.fuente
Tratar:
Dado que todo es información literal (un "documento aquí es"), y la diferencia entre este y la salida deseada es trivial, simplemente masajee esa información literal allí para que coincida con la salida.
Ahora supongamos que
line 3proviene de algún lugar y se almacena en una variable llamadalastline:En un documento here, podemos generar texto sustituyendo variables. No solo eso, sino que podemos calcular el texto usando la sustitución de comandos:
Podemos interpolar múltiples líneas:
En general, evite el procesamiento de texto de la plantilla here doc; intenta generarlo usando código interpolado.
fuente
cat <<EOS...en el OP fue solo un ejemplo de "capturar un archivo arbitrario", para hacer que la publicación sea específica y la pregunta sea clara. ¿No fue realmente obvio para usted, o simplemente pensó que sería inteligente interpretar la pregunta literalmente?Si no te importa el pedido. Entonces esto funcionará
cat lines | tee >(tail -1). Como han dicho otros. Debe leer el archivo dos veces, o guardar el archivo completo en el búfer, en el orden que solicitó.fuente