Pseudo archivos para datos temporales

98

A menudo quiero alimentar datos de cadenas relativamente cortas (aunque podrían ser varias líneas) a los programas de línea de comandos que aceptan solo la entrada de archivos (por ejemplo, wdiff) de manera repetida. Claro que puedo crear uno o más archivos temporales, guardar la cadena allí y ejecutar el comando con el nombre del archivo como parámetro. Pero me parece que este procedimiento sería muy ineficiente si los datos se escriben realmente en el disco y también podría dañar el disco más de lo necesario si repito este procedimiento muchas veces, por ejemplo, si quiero alimentar líneas individuales de texto largo archivos a wdiff. ¿Hay alguna forma recomendada de evitar esto, por ejemplo, mediante el uso de pseudo archivos como tuberías para almacenar los datos temporalmente sin escribirlos en el disco (o escribirlos solo si exceden una longitud crítica). Tenga en cuenta que wdiff toma dos argumentos y,wdiff <"text".

Highsciguy
fuente
¿Se puede resolver esto a través de xargs?
NN
No lo sé, pero no sería obvio para mí cómo. Según tengo entendido xargs, las líneas de entrada de los argumentos de cadena de archivo para el comando. Pero necesito lo contrario.
highsciguy
@rahmu Eché un vistazo, pero creo que la configuración del problema es un poco diferente allí. Al menos no veo cómo ayudarían las respuestas. La respuesta aceptada para producir archivos temporales correctamente es esencialmente lo que no quiero evitar, si no es que hay algún tipo de almacenamiento en búfer que realmente impide escribir los archivos. ¡Tengo una comprensión limitada de cómo funcionan los archivos temporales!
highsciguy
¿Qué tiene de malo echo $data_are_here | dumb_program?
vonbrand
1
Esto admitiría solo un archivo de entrada y no todos los programas leerían desde stdin.
highsciguy

Respuestas:

55

Use una tubería con nombre . A modo de ilustración:

mkfifo fifo
echo -e "hello world\nnext line\nline 3" > fifo

El -eecho le dice a interpretar correctamente el escape de nueva línea ( \n). Esto bloqueará, es decir, su shell se bloqueará hasta que algo lea los datos de la tubería.

Abra otro shell en algún lugar y en el mismo directorio:

cat fifo

Leerás el eco, que liberará el otro shell. Aunque la tubería existe como un nodo de archivo en el disco, los datos que la atraviesan no existen; Todo tiene lugar en la memoria. Puede fondo ( &) el eco.

La tubería tiene un búfer de 64k (en Linux) y, como un zócalo, bloqueará el escritor cuando esté lleno, por lo que no perderá datos mientras no mate prematuramente al escritor.

encerrada dorada
fuente
Ok, gracias, esto funciona también con dos tubos con nombre y wdiff. Pero pensé entender que hay una cierta (pequeña) cantidad de memoria disponible para la tubería como buffer. ¿Qué sucede si excedo el tamaño del búfer?
highsciguy
Agregué un párrafo final sobre ese tema.
Ricitos de oro
3
/tmpestá configurado en la mayoría de las distribuciones para usar un tmpfssistema de archivos que está en RAM. Cuando escribe un archivo en /tmpél, va directamente a su RAM, lo que es una buena respuesta para los archivos semi-resistentes a los que se debe acceder rápidamente y reescribirse muchas veces.
129

En Bash, puede usar la command1 <( command0 )sintaxis de redireccionamiento, que redirige command0la salida estándar y la pasa a una command1que toma un nombre de archivo como argumento de línea de comandos. Esto se llama sustitución de proceso .

Algunos programas que toman argumentos de la línea de comandos del nombre de archivo realmente necesitan un archivo de acceso aleatorio real, por lo que esta técnica no funcionará para ellos. Sin embargo, funciona bien con wdiff:

user@host:/path$ wdiff <( echo hello; echo hello1 ) <( echo hello; echo hello2 )
hello
[-hello1-]
{+hello2+}

En segundo plano, esto crea un FIFO, canaliza el comando dentro <( )del FIFO y pasa el descriptor de archivo del FIFO como argumento. Para ver qué está pasando, intente usarlo echopara imprimir el argumento sin hacer nada con él:

user@host:/path$ echo <( echo hello )
/dev/fd/63

Crear una canalización con nombre es más flexible (si desea escribir una lógica de redireccionamiento complicada utilizando múltiples procesos), pero para muchos propósitos esto es suficiente y obviamente es más fácil de usar.

También existe la >( )sintaxis para cuando desea usarlo como salida, por ejemplo

$ someprogram --logfile >( gzip > out.log.gz )

Consulte también la hoja de trucos de redireccionamientos de Bash para conocer las técnicas relacionadas.

Caracol mecánico
fuente
Esto no es compatible con KSH
chanchal1987
55
ksh inventó esto. Estás utilizando una variante de ksh que no es compatible
Neil McGuigan el
2
Algunos programas que toman argumentos de la línea de comandos del nombre de archivo realmente necesitan un archivo de acceso aleatorio real, por lo que esta técnica no funcionará para ellos. ¿Qué haces en estos casos? Por ejemplo ssh -F <(vagrant ssh-config) default, sería realmente agradable, pero por desgracia.
Sukima
10

wdiff es un caso especial debido a que requiere 2 argumentos de nombre de archivo, pero para todos los comandos que solo requieren 1 argumento y que se niegan obstinadamente a tomar cualquier cosa que no sea un argumento de nombre de archivo, hay 2 opciones:

  • El nombre de archivo '-' (es decir, un signo menos) funciona aproximadamente la mitad del tiempo. Parece depender del comando en cuestión y si el desarrollador del comando atrapa ese caso y lo maneja como se esperaba. p.ej

    $> ls | gato -

  • Hay un archivo psuedo llamado / dev / stdin que existe en Linux y se puede usar si un comando requiere absolutamente un nombre de archivo. Es más probable que funcione, ya que no requiere ningún manejo especial de nombre de archivo del comando. Si un fifo funciona, o el método de sustitución del proceso bash funciona, entonces esto también debería funcionar y no es específico del shell. p.ej

    $> ls | gato / dev / stdin

dabuntu
fuente
1
less y openssl como / dev / stdin en lugar de / dev / fd / NUM :-)
eel ghEEz