Solución alternativa para la sustitución de procesos en mksh

Falta una característica extremadamente útil de Bash, conocida como sustitución de procesos , en el shell de Android, mksh . Esto es muy desafortunado ya que le impide hacer cosas como:

diff <(sort list1) <(sort list2)

El sitio mksh ha marcado esto como un "plan futuro" aquí . Entonces mi pregunta es:

¿Hay alguna solución para esto? (¿Y qué son?)

AFAIK, eres libre de usar cualquier otro shell, suponiendo que sea compatible con Android.
Ahora hay una etiqueta mksh ;-)

Respuestas (3)

Aparentemente, la única (?) forma de hacer esto es mediante el uso de una canalización con nombre como esta:

mkfifo myp1 || exit
mkfifo myp2 || exit
sort list1 >myp1 &
sort list2 >myp2 &
diff myp1 myp2 
rm -f myp1 myp2

Esto debe colocarse en una función de shell mksh para que sea de cualquier uso real de la línea de comandos. Otra parte complicada parece ser que AOS ha implementado algún tipo de tiempo de espera que mata o estropea la tubería, si no se usa en unos segundos. (Razón desconocida.)

Acabamos de descubrir cómo hacer esto para el caso de Desktop Unix. En Android, necesitará un directorio para colocar FIFO temporales (cualquiera servirá, como /sqlite_stmt_journalen Android 2.x y /data/data(si tiene los derechos para escribir allí) en los más nuevos). También necesitará mktempy mkfifo. ( cates un mksh incorporado en estos días, pero en Android antiguo deberá agregar eso o una versión mksh más nueva; todos funcionan al menos hasta Android 1.5)

function die {
        print -ru2 -- "E: $*"
        exit 1
}

function psubin {
        local stdin=$(cat; echo .) pipe

        pipe=$(mktemp /tmp/psub.XXXXXXXXXX) || die mktemp

        # this is racy
        rm -f "$pipe"
        mkfifo "$pipe" || die mkfifo

        (
                # don’t block parent
                exec <&- >&- 2>&-
                # write content to FIFO
                print -nr -- "${stdin%.}" >"$pipe"
                # signal EOF to reader, ensure it’s not merged
                sleep 0.1
                :>"$pipe"
                # clean up
                (sleep 1; rm -f "$pipe") &
        ) &
        print -nr -- "$pipe"
}

diff -u $(sort list1 | psubin) $(sort list2 | psubin)

Necesita un shell como ksh, zsh o bash para la sustitución de procesos.Supongo que el comando diff al que se refiere @user1147688 es de busybox. La sustitución de procesos no funciona con las aplicaciones de busybox.Busybox diff maneja las canalizaciones con nombre de manera diferente a diffutils diff. Después de un poco más de pruebas, descubrí que solo podía usar la sustitución de procesos con la diferencia de busybox después de crear el directorio /tmp con este comando supersu:

su -mm -d -c 'mount -t tmpfs -o rw,uid=0,gid=0,mode=1777 /tmp /tmp'

Esto crea un temporal para que lo usen todos los usuarios. Por alguna razón, busybox no usa la variable TMPDIR. Alternativamente con zsh, puede usar la diferencia de busybox de esta manera:

busybox diff =(sort ./a) =(sort ./b)

Esto es como diff <(...) <(...)pero usa archivos temporales en lugar de canalizaciones con nombre. Zsh utilizará cualquier directorio temporal de lectura y escritura que asigne a TMPDIR. Sin embargo, el uso TMPDIR=/sdcardno funcionará aquí, ya que no puede cambiar la propiedad o los permisos en los archivos / sdcard.

Diffutils diff funciona sin problemas utilizando cualquier tipo de sustitución de procesos.

Aquí hay una función que implementa algo como diff con sustitución de procesos. Puede usar esto con busybox diff y cualquier shell moderno compatible con sh que admita matrices, como ksh, bash, zsh o mksh. Funcionará suponiendo que HOME esté configurado en una ubicación de lectura y escritura, como /sdcard en Android.

# usage: diff2 COMMANDS1 -- COMMANDS2
diff2() {
        local i=1 j k cmd1 cmd2 list1 list2
        cmd1=()
        cmd2=()
        while (( i < $# )); do
                eval j="\$$i"
                if [[ $j = -- ]]; then
                        k=$i
                        break
                else
                        cmd1+=("$j")
                fi
                let i++
        done
        shift $k
        cmd2=("$@")
        list1=$HOME/diff$RANDOM
        list2=$HOME/diff$RANDOM
        eval "${cmd1[@]}" > $list1 2>&1
        eval "${cmd2[@]}" > $list2 2>&1
        diff $list1 $list2
        rm $list1 $list2
}

Puede agregar conductos, |, a su COMMAND1 o COMMAND2 siempre que los entrecomilla o barra invertida.

La forma en que esto funciona es dividiendo la entrada en dos matrices con --el separador de los comandos. Cierto evalabuso ayuda a separar la entrada y es necesario para evaluar comandos que usan canalizaciones.

La función podría modificarse aún más para incluir opciones para diff, ya sea utilizando una tercera matriz o analizando con getopts / GNU getopt. Una tercera matriz con otro --separador probablemente funcionaría mejor para evitar tener que usar GNU getopt para opciones largas.

Hay algo mal con su comando de montaje. Creo que debería serlo: mount -t tmpfs -o rw,uid=0,gid=0,mode=1777 tmpfs /tmpy además necesitas volver a mkdir -p /tmpmontarlo /como RW . Sin embargo, gracias por la respuesta útil y el guión.