Обходной путь для замены процесса в mksh

Чрезвычайно полезная функция Bash, известная как подстановка процессов , отсутствует в оболочке Android, mksh . Это очень прискорбно, поскольку не позволяет вам делать такие вещи, как:

diff <(sort list1) <(sort list2)

Сайт mksh отметил это как «план на будущее» здесь . Итак, мои вопросы:

Есть ли обходные пути для этого? (И какие они?)

Насколько я знаю, вы можете использовать любую другую оболочку, если она совместима с Android.
Теперь есть тег mksh ;-)

Ответы (3)

По-видимому, единственный (?) способ сделать это - использовать именованный канал следующим образом:

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

Это необходимо поместить в функцию оболочки mksh , чтобы можно было реально использовать командную строку. Еще одна сложная часть, похоже, заключается в том, что AOS реализовал некий тайм-аут, который убивает или портит канал, если он не используется в течение нескольких секунд. (Причина неизвестна.)

Мы только что выяснили, как это сделать для случая Desktop Unix. На Android вам понадобится каталог для размещения временных FIFO (подойдет любой, например, /sqlite_stmt_journalв Android 2.x и /data/data(если у вас есть права на запись туда) в более новых). Вам также понадобятся mktempи mkfifo. ( catв наши дни это встроенный mksh, но на старом Android вам нужно будет добавить его или более новую версию mksh; все они работают как минимум до 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)

Вам нужна оболочка, такая как ksh, zsh или bash для подстановки процессов.Я предполагаю, что команда diff @user1147688 относится к busybox. Замена процесса не работает с приложениями busybox.Busybox diff обрабатывает именованные каналы иначе, чем diffutils diff. После небольшого дополнительного тестирования я обнаружил, что могу использовать подстановку процессов с diff busybox только после создания каталога / tmp с помощью этой команды supersu:

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

Это создает временный файл для всех пользователей. По какой-то причине busybox не использует переменную TMPDIR. В качестве альтернативы с zsh вы можете использовать diff busybox следующим образом:

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

Это похоже на diff <(...) <(...)использование временных файлов вместо именованных каналов. Zsh будет использовать любую доступную для чтения и записи временную директорию, которую вы назначите для TMPDIR. Однако использование TMPDIR=/sdcardздесь не сработает, поскольку вы не можете изменить владельца или права доступа к файлам /sdcard.

Diffutils diff работает без проблем, используя любую подстановку процессов.

Вот функция, которая реализует что-то вроде diff с подстановкой процессов. Вы можете использовать это с Busybox diff и любой современной sh-совместимой оболочкой, поддерживающей массивы, такой как ksh, bash, zsh или mksh. Это будет работать, если для HOME установлено место, доступное для чтения и записи, например /sdcard на 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
}

Вы можете добавлять каналы, |, к вашей COMMAND1 или COMMAND2, если вы заключаете их в кавычки или ставите обратную косую черту.

Это работает путем разделения ввода на два массива с --разделителем для команд. Некоторое evalзлоупотребление помогает разделить ввод и необходимо для оценки команд, использующих конвейеры.

Функцию можно дополнительно изменить, включив в нее опции для diff, используя либо третий массив, либо синтаксический анализ с помощью getopts / GNU getopt. Третий массив с другим --разделителем, вероятно, будет работать лучше всего, чтобы избежать использования GNU getopt для длинных опций.

Что-то не так с вашей командой монтирования. Я думаю так и должно быть: mount -t tmpfs -o rw,uid=0,gid=0,mode=1777 tmpfs /tmpа вдобавок нужно еще mkdir -p /tmpи перемонтировать /как RW . Тем не менее, спасибо за полезный ответ и сценарий.