¿Cómo emular la sustitución de procesos en Dash?

18

En bash, puedo usar la sustitución de procesos y tratar la salida de un proceso como si fuera un archivo guardado en el disco:

$ echo <(ls)
/dev/fd/63

$ ls -lAhF <(ls)
lr-x------ 1 root root 64 Sep 17 12:55 /dev/fd/63 -> pipe:[1652825]

desafortunadamente, la sustitución de procesos no es compatible con dash.

¿Cuál sería la mejor manera de emular Process Substitutionen el tablero?

No quiero guardar el resultado como un archivo temporal en algún lugar ( /tmp/) y luego tengo que eliminarlo. ¿Hay una forma alternativa?

Martin Vegter
fuente
Dependiendo de lo que intente hacer, es posible que pueda usar tuberías.
Eric Renouf
Honestamente, si la portabilidad no es su principal preocupación aquí, ¿no sería más fácil instalarla bashen su dispositivo?
Arkadiusz Drabczyk
1
El ejemplo que proporciona en el aviso de recompensa resulta ser el tema de esta respuesta vinculada . Como se muestra allí, podría ser una versión simplificada de la respuesta de Gilles (suponiendo la disponibilidad /dev/fdy el uso en xz -cd <file>lugar de cat <file> | xz -d) xz -cd "$1" | { xz -cd "$2" | { diff /dev/fd/3 /dev/fd/4; } 3<&0; } 4<&0.
fra-san
1
@ fra-san: eso es realmente lo que necesitaba. Puedes darle una respuesta si quieres. Gracias.
Martin Vegter

Respuestas:

4

La pregunta en el aviso de recompensa actual:

El ejemplo general es demasiado complicado. ¿Alguien puede explicar cómo implementar el siguiente ejemplo?diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)

Parece tener una respuesta aquí .

Como se muestra en la respuesta de Gilles , la idea general es enviar la salida de los comandos "productor" a los nuevos archivos 1 del dispositivo en diferentes etapas de una tubería, dejándolos disponibles para los comandos "consumidores", que posiblemente puedan tomar nombres de archivos como argumentos ( asumiendo que su sistema le da acceso a descriptores de archivo como /dev/fd/X).

La forma más sencilla de lograr lo que está buscando es probablemente:

xz -cd file1.xz | { xz -cd file2.xz | diff /dev/fd/3 -; } 3<&0

(Utilizando file1.xzen lugar de "$1"legibilidad y en xz -cdlugar de cat ... | xz -dporque un solo comando es suficiente).

La salida del primer comando "productor" xz -cd file1.xz, se canaliza a un comando compuesto ( {...}); pero, en lugar de consumirse inmediatamente como la entrada estándar del siguiente comando, se duplica al descriptor de archivo 3y, por lo tanto, se hace accesible a todo lo que se encuentra dentro del comando compuesto como /dev/fd/3. La salida del segundo comando "productor" xz -cd file2.xz, que no consume ni su entrada estándar ni nada del descriptor de archivo 3, se canaliza al comando "consumidor" diff, que lee de su entrada estándar y de /dev/fd/3.

La duplicación de la tubería y del descriptor de archivo se puede agregar para proporcionar archivos de dispositivo para tantos comandos de "productor" como sea necesario, por ejemplo:

xz -cd file1.xz | { xz -cd file2.xz | { diff /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0

Si bien puede ser irrelevante en el contexto de su pregunta específica, vale la pena señalar que:

  1. cmd1 <(cmd2) <(cmd3), cmd2 | { cmd3 | { cmd1 /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0y ( cmd2 | ( cmd3 | ( cmd1 /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 )tienen diferentes efectos potenciales en el entorno de ejecución inicial.

  2. Al contrario de lo que sucede en cmd1 <(cmd2) <(cmd3), cmd3y cmd1en cmd2 | { cmd3 | { cmd1 /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0no podrá leer ninguna entrada del usuario. Eso requerirá más descriptores de archivo. Por ejemplo, para que coincida

    diff <(echo foo) <(read var; echo "$var")
    

    necesitarás algo como

    { echo foo | { read var 0<&9; echo "$var" | diff /dev/fd/3 -; } 3<&0; } 9<&0
    

1 Puede encontrar más información sobre ellos en U&L, por ejemplo, en Understanding / dev y sus subdirecciones y archivos .

fra-san
fuente
12

Puede reproducir lo que hace el caparazón debajo del capó haciendo la plomería manualmente. Si su sistema tiene entradas, puede usar la combinación aleatoria de descriptores de archivo: puede traducir/dev/fd/NNN

main_command <(produce_arg1) <(produce_arg2) >(consume_arg3) >(consume_arg4)

a

{ produce_arg1 |
  { produce_arg2 |
    { main_command /dev/fd5 /dev/fd6 /dev/fd3 /dev/fd4 </dev/fd/8 >/dev/fd/9; } 5<&0 3>&1 |
    consume_arg3; } 6<&0 4>&1; |
  consume_arg4; } 8<&0 9>&1

He mostrado un ejemplo más complejo para ilustrar múltiples entradas y salidas. Si no necesita leer de la entrada estándar, y la única razón por la que está usando la sustitución del proceso es que el comando requiere un nombre de archivo explícito, simplemente puede usar/dev/stdin :

main_command <(produce_arg1)
produce_arg1 | main_command /dev/stdin

Sin , debe usar una tubería con nombre . Una canalización con nombre es una entrada de directorio, por lo que debe crear un archivo temporal en alguna parte, pero ese archivo es solo un nombre, no contiene ningún dato./dev/fd/NNN

tmp=$(mktemp -d)
mkfifo "$tmp/f1" "$tmp/f2" "$tmp/f3" "$tmp/f4"
produce_arg1 >"$tmp/f1" &
produce_arg2 >"$tmp/f2" &
consume_arg3 <"$tmp/f3" &
consume_arg4 <"$tmp/f4" &
main_command "$tmp/f1" "$tmp/f2" "$tmp/f3" "$tmp/f4"
rm -r "$tmp"
Gilles 'SO- deja de ser malvado'
fuente
2
</dev/fd/8 >/dev/fd/9no son equivalentes a <&8 >&9en Linux y deben evitarse allí.
Stéphane Chazelas
@Gilles: estoy confundido por el complejo ejemplo. ¿Podría mostrar cómo emular diff <(cat "$2" | xz -d) <(cat "$1" | xz -d):?
Martin Vegter
3

¿Alguien puede explicar cómo implementar el siguiente ejemplo?
diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)

Creo que las canalizaciones con nombre son más fáciles de asimilar que las redirecciones, así que en términos más simples:

mkfifo p1 p2               # create pipes
cat "$2" | xz -d > p1 &    # start the commands in the background
cat "$1" | xz -d > p2 &    #    with output to the pipes
diff p1 p2                 # run 'diff', reading from the pipes
rm p1 p2                   # remove them at the end

p1 y p2 son tuberías temporales con nombre, podrían llamarse cualquier cosa.

Sería más inteligente para crear las tuberías en /tmp, por ejemplo, en un directorio como Gilles' respuesta espectáculos, y nota que no es necesario cataquí, así:

tmpdir=$(mktemp -d)
mkfifo "$tmpdir/p1" "$tmpdir/p2"
xz -d < "$2" > "$tmpdir/p1" &
xz -d < "$1" > "$tmpdir/p2" &
diff "$tmpdir/p1" "$tmpdir/p2"
rm -r "$tmpdir"

(Probablemente también puedas escapar sin las comillas, ya que mktemp es probable que cree nombres de archivo "agradables").

La solución de tubería tiene la advertencia de que si el comando principal ( diff) muere antes de leer toda la entrada, los procesos en segundo plano pueden quedar pendientes.

ilkkachu
fuente
0

Qué pasa:

cat "$2" | xz -d | diff /dev/sdtin /dev/stderr 2<<EOT
`cat "$1" | xz -d`
EOT
defdefred
fuente