Чрезвычайно полезная функция Bash, известная как подстановка процессов , отсутствует в оболочке Android, mksh . Это очень прискорбно, поскольку не позволяет вам делать такие вещи, как:
diff <(sort list1) <(sort list2)
Сайт mksh отметил это как «план на будущее» здесь . Итак, мои вопросы:
Есть ли обходные пути для этого? (И какие они?)
По-видимому, единственный (?) способ сделать это - использовать именованный канал следующим образом:
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 . Тем не менее, спасибо за полезный ответ и сценарий.
ГигантДерево
мирабилос