Я написал функцию оболочки POSIX , которая может быть использована для локального пространства имен встроенной команды оболочки или функции в любом из ksh93, dash, mkshили bash (названных именно потому , что я лично подтвердил его работу во всех из них) . Из оболочек, в которых я его тестировал, он не оправдал моих ожиданий yash, и я никогда не ожидал, что он вообще сработает zsh. Я не проверял posh. poshНекоторое время назад я потерял всякую надежду и некоторое время не устанавливал ее. Может быть, это работает в posh...?
Я говорю, что это POSIX, потому что, читая спецификацию, она использует преимущества определенного поведения базовой утилиты, но, по общему признанию, спецификация в этом отношении расплывчата, и, по крайней мере, один человек явно со мной не согласен. Обычно у меня было разногласие с этим, в конечном итоге я обнаружил, что ошибка - моя собственная, и, возможно, на этот раз я тоже ошибаюсь насчет спецификации, но когда я допрашивал его, он не ответил.
Как я уже сказал, это определенно работает в вышеупомянутых оболочках и работает, в основном, следующим образом:
some_fn(){ x=3; echo "$x"; }
x=
x=local command eval some_fn
echo "${x:-empty}"
3
empty
Команда commandуказана в качестве доступной в основном утилиты и одной из $PATHпредустановленных. Одна из указанных функций заключается в переносе специальных встроенных утилит в собственную среду при их вызове и т. Д.
{ sh -c ' x=5 set --; echo "$x"
x=6 command set --; echo "$x"
exec <""; echo uh_oh'
sh -c ' command exec <""; echo still here'
}
5
5
sh: 3: cannot open : No such file
sh: 1: cannot open : No such file
still here
... поведение обоих назначений командной строки выше корректно по спецификации. Поведение обоих состояний ошибки также является правильным, и на самом деле оно почти полностью дублируется в спецификации. Задания с префиксом в командной строке функций или специальных встроенных функций указываются для воздействия на текущую среду оболочки. Аналогично, ошибки перенаправления указываются как фатальные, когда указываются на любой из них. commandуказывается для подавления особой обработки специальных встроенных функций в этих случаях, а случай перенаправления фактически демонстрируется примером в спецификации.
Обычные встроенные функции, как command, с другой стороны, предназначены для работы в среде подоболочек - что не обязательно означает, что это другой процесс , просто то, что он должен быть принципиально неотличим от одного. Результаты вызова обычного встроенного всегда должны напоминать то, что может быть получено из команды с аналогичными возможностями $PATH. И так...
na=not_applicable_to_read
na= read var1 na na var2 <<"" ; echo "$var1" "$na" "$var2"
word1 other words word2
word1 not_applicable_to_read word2
Но commandкоманда не может вызывать функции оболочки, и поэтому не может использоваться для отображения их особого отношения к лечению, как для обычных встроенных функций. Это тоже спец. Фактически, спецификация говорит, что основная утилита commandсостоит в том, что вы можете использовать ее в функции оболочки оболочки, названной для другой команды, чтобы вызывать эту другую команду без саморекурсии, потому что она не будет вызывать функцию. Как это:
cd(){ command cd -- "$1"; }
Если бы вы не использовали commandэту cdфункцию, она почти наверняка стала бы segfault для самостоятельной рекурсии.
Но как обычные встроенные функции, которые могут вызывать специальные встроенные функции, commandмогут делать это в среде с подоболочками . И поэтому, в то время как текущее состояние оболочки, определенное внутри, может придерживаться текущей оболочки - конечно read, так $var1и $var2было - по крайней мере результаты командной строки, вероятно, не должны ...
Простые команды
Если имя команды не появляется или если имя команды является специальной встроенной функцией или функцией, назначение переменных должно влиять на текущую среду выполнения. В противном случае назначения переменных должны быть экспортированы для среды выполнения команды и не должны влиять на текущую среду выполнения.
Теперь, может ли commandспособность быть как обычной встроенной функцией, так и напрямую вызывать специальные встроенные функции, это просто какая-то неожиданная лазейка в отношении командной строки, я не знаю, но я знаю, что по крайней мере четыре оболочки уже упоминается честь commandпространства имен.
И хотя он commandне может напрямую вызывать функции оболочки, он может вызывать, evalкак показано, и поэтому может делать это косвенно. Поэтому я построил обертку пространства имен на этой концепции. Требуется список аргументов, таких как:
ns any=assignments or otherwise=valid names which are not a command then all of its args
... за исключением того, что указанное commandвыше слово распознается как единое целое, если его можно найти с пустым $PATH. Кроме того , локально-обзорные оболочки переменных , названные в командной строке, он также локально-прицелы все переменный с одиночными строчными буквенными именами и списком других стандартных, такие как $PS3, $PS4, $OPTARG, $OPTIND, $IFS, $PATH, $PWD, $OLDPWDи некоторыми другие.
И да, локально обзорного $PWDи $OLDPWDпеременные , а потом явно cdИНГ $OLDPWDи $PWDон может достаточно надежно охват текущий рабочий каталог , а также. Это не гарантировано, хотя и старается изо всех сил. Он сохраняет дескриптор, 7<.и когда его целевая оболочка возвращает, это делает cd -P /dev/fd/7/. Если текущий рабочий каталог находился unlink()в промежуточный период, он все равно должен хотя бы вернуться в него, но в этом случае выдаст ужасную ошибку. И поскольку он поддерживает дескриптор, я не думаю, что здравомыслящее ядро должно позволять размонтировать его корневое устройство (???) .
Он также локально ограничивает параметры оболочки и восстанавливает их до состояния, в котором он их обнаружил, когда возвращается его служебная программа. Это $OPTSособенно относится к тому, что он поддерживает копию в своей области видимости, которой он изначально присваивает значение $-. После обработки всех назначений в командной строке он выполнит это set -$OPTSнепосредственно перед вызовом своей цели переноса. Таким образом, если вы определили -$OPTSв командной строке, вы можете определить параметры оболочки цели wrap. Когда цель возвращается, она будет set +$- -$OPTSсо своей собственной копией $OPTS (на которую не влияет определение командной строки) и восстановит все в исходное состояние.
Конечно, ничто не мешает вызывающей стороне каким-либо образом явно returrnвыйти из функции посредством цели wrap или ее аргументов. Это предотвратит любое восстановление / очистку состояния, которое он в противном случае предпримет.
Чтобы сделать все это, нужно пройти три evalглубины. Сначала он оборачивается в локальную область видимости, затем изнутри читает аргументы, проверяет их на наличие допустимых имен оболочки и завершает работу с ошибкой, если находит то, чего нет. Если все аргументы являются действительными и в конечном итоге один из них command -v "$1"возвращает true ( $PATHв данный момент : пусто), он будет evalопределять и передавать все оставшиеся аргументы целевому объекту в командной строке (хотя он игнорирует особый случай для ns- потому что это не не очень полезно, а три evalглубины более чем достаточно) .
Это в основном работает так:
case $- in (*c*) ... # because set -c doesnt work
esac
_PATH=$PATH PATH= OPTS=$- some=vars \
command eval LOCALS=${list_of_LOCALS}'
for a do i=$((i+1)) # arg ref
if [ "$a" != ns ] && # ns ns would be silly
command -v "$a" &&
! alias "$a" # aliases are hard to run quoted
then eval " PATH=\$_PATH OTHERS=$DEFAULTS $v \
command eval '\''
shift $((i-1)) # leave only tgt in @
case $OPTS in (*different*)
set \"-\${OPTS}\" # init shell opts
esac
\"\$@\" # run simple command
set +$- -$OPTS "$?" # save return, restore opts
'\''"
cd -P /dev/fd/7/ # go whence we came
return "$(($??$?:$1))" # return >0 for cd else $1
else case $a in (*badname*) : get mad;;
# rest of arg sa${v}es
esac
fi; done
' 7<.
Есть некоторые другие переназначения и, и несколько странных тесты , чтобы сделать с тем , как некоторые оболочками положить cв , $-а затем отказываются принимать его в качестве опции set (???) , но все подчиненности, и в основном используются только для сохранения от излучающих нежелательный вывод и аналогичные в крайних случаях. И вот как это работает. Он может делать эти вещи, потому что он устанавливает собственную локальную область видимости, прежде чем вызывать свою служебную программу в виде вложенного объекта.
Это долго, потому что я стараюсь быть здесь очень осторожным - три evalsсложно. Но с этим вы можете сделать:
ns X=local . /dev/fd/0 <<""; echo "$X" "$Y"
X=still_local
Y=global
echo "$X" "$Y"
still_local global
global
Продвинуться на шаг дальше и упорствовать в именовании локальной области видимости служебной программы не должно быть очень сложно. И даже как написано, он уже определяет $LOCALSпеременную для упакованной утилиты, которая состоит только из списка разделенных пробелами всех имен, которые она определила в среде упакованной утилиты.
Подобно:
ns var1=something var2= eval ' printf "%-10s%-10s%-10s%s\n" $LOCALS '
... который абсолютно безопасен - $IFSбыл очищен до значения по умолчанию, и только допустимые имена оболочки могут его использовать, $LOCALSесли вы сами не зададите его в командной строке. И даже если в разделенной переменной могут быть символы glob, вы можете также указать OPTS=fв командной строке, чтобы служебная программа-оболочка запретила их расширение. В любом случае:
LOCALS ARG0 ARGC HOME
IFS OLDPWD OPTARG OPTIND
OPTS PATH PS3 PS4
PWD a b c
d e f g
h i j k
l m n o
p q r s
t u v w
x y z _
bel bs cr esc
ht ff lf vt
lb dq ds rb
sq var1 var2
А вот и функция. Все команды имеют префикс w /, \чтобы избежать aliasрасширений:
ns(){ ${1+":"} return
case $- in
(c|"") ! set "OPTS=" "$@"
;; (*c*) ! set "OPTS=${-%c*}${-#*c}" "$@"
;; (*) set "OPTS=$-" "$@"
;; esac
OPTS=${1#*=} _PATH=$PATH PATH= LOCALS= lf='
' rb=\} sq=\' l= a= i=0 v= __=$_ IFS=" ""
" command eval LOCALS=\"LOCALS \
ARG0 ARGC HOME IFS OLDPWD OPTARG OPTIND OPTS \
PATH PS3 PS4 PWD a b c d e f g h i j k l m n \
o p q r s t u v w x y z _ bel bs cr esc ht ff \
lf vt lb dq ds rb sq'"
for a do i=$((i+1))
if \[ ns != "$a" ] &&
\command -v "$a" >&9 &&
! \alias "${a%%=*}" >&9 2>&9
then \eval 7>&- '\' \
'ARGC=$((-i+$#)) ARG0=$a HOME=~' \
'OLDPWD=$OLDPWD PATH=$_PATH IFS=$IFS' \
'OPTARG=$OPTARG PWD=$PWD OPTIND=1' \
'PS3=$PS3 _=$__ PS4=$PS4 LOCALS=$LOCALS' \
'a= b= c= d= e= f= g= i=0 j= k= l= m= n= o=' \
'p= q= r= s= t= u= v= w= x=0 y= z= ht=\ ' \
'cr=^M bs=^H ff=^L vt=^K esc=^[ bel=^G lf=$lf' \
'dq=\" sq=$sq ds=$ lb=\{ rb=\}' \''"$v' \
'\command eval 9>&2 2>&- '\' \
'\shift $((i-1));' \
'case \${OPTS##*[!A-Za-z]*} in' \
'(*[!c$OPTS]*) >&- 2>&9"'\' \
'\set -"${OPTS%c*}${OPTS#*c}"' \
';;esac; "$@" 2>&9 9>&-; PS4= ' \
'\set +"${-%c*}${-#*c}"'\'\" \
-'$OPTS \"\$?\"$sq";' \
' \cd -- "${OLDPWD:-$PWD}"
\cd -P ${ANDROID_SYSTEM+"/proc/self/fd/7"} /dev/fd/7/
\return "$(($??$?:$1))"
else case ${a%%=*} in
([0-9]*|""|*[!_[:alnum:]]*)
\printf "%s: \${$i}: Invalid name: %s\n" \
>&2 "$0: ns()" "'\''${a%%=*}'\''"
\return 2
;; ("$a") v="$v $a=\$$a"
;; (*) v="$v ${a%%=*}=\${$i#*=}"
;; esac
case " $LOCALS " in (*" ${a%%=*} "*)
;; (*) LOCALS=$LOCALS" ${a%%=*}"
;; esac
fi
done' 7<. 9<>/dev/null
}
( easiest thing ever ). Но это не совсем то, что вы после. Я думаю, вы могли бы сделать( stuff in subshell; exec env ) | sed 's/^/namespace_/'иevalрезультат в родительской оболочке, но это отчасти неприятно.