( set -e -- "$(command -v find)"
[ -x "${1:?}" ]
[ ! -e "$1cmd" ]
[ ! -L "$1cmd" ]
mv -- "$1" "$1cmd"
cat > "$1"
chmod +x -- "$1"
) <<""
#!/bin/sh -f
eval ' exec "$0cmd" '"${1$( # f!'"ing colors
unset i L O M rt IFS
chk() case ${O+$2}${2--} in # $O must be set or $2
(-maxdepth"$2") M= ;; # unset to match "$2$2"
([\(!]"$2"|-*"$2") # this is the last match
printf %s${1+%b}%.d\
"$rt" \\c 2>&- && # printf fails if ! $1
chk(){ ${1+:} exit; } ;; # chk() = !!$1 || exit
(-?*) shift $((OPTIND=1)) # handle -[HLP] for
while getopts :HLP O # path resolution w/
do case $O${L=} in # NU$L expansions
(P) unset L ;; # $[HL]=:- $P=-
(\?) rt= chk '' # opt unexpected &&
return ;; # abandon parse
esac; done; unset O ;; # $O is unset until
(${M-${O=?*}}) # above matches fail
! [ ! -L "${L-$2}" ] || # ! -P ||!! -L ||
[ ! / -ef "$2" ] || # ! / == $2 ||
rt=$rt' ! \( -path "${'$i'%/}/media/*" -prune \)'
esac
while chk ${1+$((i+=1)) "$1"} # loop while args remain
do printf ' "${'$i}\" # printf args to eval
shift # shift args away
done # done
)"
Есть сценарий оболочки для find
, который вставит несколько аргументов, чтобы запретить реальный find
из ищем в / media /
, если какой-либо из его аргументов пути равен /
.
Вышеупомянутый сценарий состоит из двух частей: это фактический сценарий (который следует за << "" \ n
) и бит установки однократного запуска вверху (это все, что находится между первой совпадающей парой (
скобки )
) .
( set -e -- "$(command -v find)" #get /path/to/find
[ -x "${1:?}" ] #else loudly fail
[ ! -e "$1cmd" ] #fail if /path/to/findcmd
[ ! -L "$1cmd" ] #and double-check
mv -- "$1" "$1cmd" #rename .../find -> .../findcmd
cat > "$1" #copy stdin to .../find
chmod +x -- "$1" #set new find's executable bit
) <<"" ###stdin
Бит установки требует небольшого внимания , а не для успешного завершения, если нет разумной возможности сделать это без прямого изменения чего-либо в вашей системе, кроме вашего $ PATH
'd найти имя исполняемого файла
- он хочет изменить его с / path / на / find
на / path / to / findcmd
и попытается, если / путь / к / findcmd
еще не существует. Если его тесты подтвердятся - и если у вас есть соответствующие разрешения для применения команд, - он переименует исполняемый файл find
и установит на его место новый сценарий оболочки с именем find
.
После этого установленный сценарий всегда будет полагаться на переименованный исполняемый файл findcmd
, оставшийся там, где он оставил (так что вы, вероятно, захотите сообщить об этом своему менеджеру пакетов, если вы его используете) , и каждый раз, когда он вызывается, он заменяет себя вызываемым $ 0cmd
со всеми своими аргументами после того, как взглянет на них.Если вы не сделаете все необходимое, чтобы установка стала постоянной, то в конечном итоге большинство менеджеров пакетов в какой-то момент закончат перезапись установленного сценария недавно обновленным двоичным файлом find
, и вы будете прямо там, где вы начали, за исключением того, что у вас также будет более старая find
с именем findcmd
в системном каталоге ../ bin
.
При наличии соответствующих разрешений и при условии, что ваша система не требует каких-либо неуместных сюрпризов, весь сценарий должен иметь возможность самостоятельной установки с копированием и вставкой в приглашение оболочки (хотя вам потребуется выполнить дополнительный RETURN в конце) . Если это не сработает, то, по крайней мере, попытка не должна причинить вреда.
#!/bin/sh -f
eval ' exec "$0cmd" '"${1+$( # f!'"ing colors
unset i L O M rt IFS
chk() case ${O+$2}${2--} in # $O must be set or $2
(-maxdepth"$2") M= ;; # unset to match "$2$2"
([\(!]"$2"|-*"$2") # this is the last match
printf %s${1+%b}%.d\
"$rt" \\c 2>&- && # printf fails if ! $1
chk(){ ${1+:} exit; } ;; # chk() = !!$1 || exit
(-?*) shift $((OPTIND=1)) # handle -[HLP] for
while getopts :HLP O # path resolution w/
do case $O${L=} in # NU$L expansions
(P) unset L ;; # $[HL]=:- $P=-
(\?) rt= chk '' # opt unexpected &&
return ;; # abandon parse
esac; done; unset O ;; # $O is unset until
(${M-${O=?*}}) # above matches fail
! [ ! -L "${L-$2}" ] || # ! -P ||!! -L ||
[ ! / -ef "$2" ] || # ! / == $2 ||
rt=$rt' ! \( -path "${'$i'%/}/media/*" -prune \)'
esac
while chk ${1+$((i+=1)) "$1"} # loop while args remain
do printf ' "${'$i}\" # printf args to eval
shift # shift args away
done # done
)}"
Мое первое и главное правило при написании сценария-оболочки: отключение . Если бы мне нужна была программа, я бы попытался ее написать, но, поскольку у меня уже есть программа, которую стоит обернуть, я постараюсь позволить ей делать то, что она уже делает, и как можно меньше изменять ее поведение, моя конечная цель. Это означает, что я не должен делать ничего, что могло бы повлиять на среду его выполнения каким-либо образом, не имеющим прямого отношения к цели переноса. Поэтому я не устанавливаю переменные, я не интерпретирую аргументы, я не трогаю потоки ввода-вывода и не изменяю группу процессов завернутой программы или ее родительский pid. В любом случае обертка должна быть максимально непродолжительной и прозрачной.
Приведенный выше сценарий достигает этой цели, в большей степени, чем раньше.Раньше я не был удовлетворен - особенно в отношении решения путей - но я считаю, что обратился к этому. Чтобы сделать это правильно, мне пришлось отслеживать состояние [HLP]
, чтобы я мог правильно сравнивать символические ссылки с /
, когда одно из -H
или Параметры] -L
были эффективными, и -P
не отменяли их. Если проверка ссылки проходит успешно, текущий аргумент проверяется на соответствие -ef
индексного дескриптора файла /
-это означает, что практически любое имя для /
будет работать (для включения символических ссылок, когда -H
или -L
эффективны) . Так что я чувствую себя лучше, и я настроил его на блокировку / proc
и / sys
и / dev
из /
] выполняет поиск по умолчанию.
Что он делает особенно хорошо, так это избегание изменения любого из вызываемых состояний перед передачей его в $ 0cmd
. Он явно отказывается интерпретировать набор аргументов, который включает любую опцию, которую он не готов обрабатывать, и в этих случаях передает весь набор в $ 0cmd
нетронутым, и поэтому, хотя в этих случаях он не может блокировать на поиск пути, он также не влияет на поведение find
каким-либо иным образом. Именно по этой причине метод eval "exec wrapped_program $ (arg-handler)"
является наиболее предпочтительным для такого рода вещей.
верхний уровень
Фактически, как указано выше, на своем верхнем уровне весь сценарий оболочки представляет собой только одну простую команду, которая сообщает ему о необходимости замены себя другим исполняемым файлом. Любая выполняемая работа выполняется в подоболочке $ (
command substitution )
, и все ее состояние - измененное или нет - полностью локализовано. Цель eval
вообще состоит в том, чтобы во второй раз взглянуть на аргументы скрипта без необходимости фактически влиять на них без надобности - и в этом вся суть этой оболочки.
Когда $ (
command sub )
выполнила свою работу, результирующая команда exec
'd будет иметь вид:
exec "$0cmd" "${1}" ... ! \( -path "${[num]%/}/media/*" -prune \) "${2}" ...
... где все ссылки на исходные аргументы - если таковые имеются - даются по порядку и по номеру в их исходной и неизмененной форме (даже нулевые аргументы) в дополнение к шести (!
, \ (
, -path
, "$ {[num]% /} / media / *"
, -prune
, \)
) вставки, набор которых выполняется для каждого успешного / -ef "$ {num}"
теста во время сканирования arg. В противном случае это будет просто:
exec "$0cmd" "${1}" "${2}" "${3}" "${4}" ...
... где все исходные аргументы упоминаются одинаково, без каких-либо вставок.
Таким образом, единственные две возможные модификации, которые эта оболочка может внести в среду своей обернутой цели, таковы:
Она изменяет имя процесса со своего собственного на его имя + cmd
. Так бывает всегда.
Он может вводить шесть аргументов для каждого корневого совпадения в список тех, с которыми он был вызван.
До тех пор, пока первое изменение считается приемлемым (хотя его можно избежать) , здесь есть единственная точка отказа в отношении модификации поведения - а именно, является ли вставка аргумента допустимой. Любые ошибки, иначе связанные с вызовом, просто выходят за рамки и должны обрабатываться целью переноса, и эта оболочка пытается заниматься своим делом.
arg-handler
При подстановке команд я сначала инициализирую переменные, которые нужно отключить, потому что строка ""
- лучший способ не сопоставить путь, когда это необходимо. Затем я объявляю функцию chk ()
, а затем вызываю ее для каждой итерации цикла while
, которая увеличивает $ i
на единицу для каждого из сценариев аргументы вызова. Он будет печатать каждое приращение $ i
, заключенное в кавычки и фигурные скобки, которым предшествуют пробел и знак доллара, в стандартный вывод команды sub:
printf ' "${'$i}\"
...
"${1}"
Он перебирает вызовы chk ()
, который получает копию своих аргументов за итерацию, затем сдвигает
его, пока не останется ни одного аргумента и цикл не завершится. chk ()
сравнивает свой аргумент со своими шаблонами и предпринимает соответствующие действия:
(- maxdepth "$ 2") M = ;;
Когда $ M
установлен, последний шаблон может соответствовать только нулевой строке, которая может только не пройти последующие проверки сравнения путей его блока, и поэтому rt = $ rt +! \ (
и т.д. никогда не встречается в этом случае. В противном случае ничего не будет сделано.
] Спецификация POSIX требует, чтобы только - [HL]
распознавались перед любыми операндами [... path ...]
, а любые другие не указаны. являются операндами [... путь ...]
и являются тестовыми операндами:
Первый операнд и последующие операнды до, но не включая первый операнд, который начинается с
-
, или!
или(
, должны интерпретироваться как[... путь ...]
операнды. Если первый операнд начинается с-
или является!
или(
, поведение не определено. Каждый операнд пути является путевым именем начальной точки в файловой иерархии.
([\ (!] "$ 2" | - * "$ 2 ")
Текущий аргумент - это единственный (
левый парен, или !
удар,или он начинается с тире - *
, но не равен -maxdepth
, и последний шаблон был сопоставлен хотя бы один раз.
printf% s $ {1 +% b}%. D "$ rt" \\ c 2> & - &&
$ rt
- если есть - в команду стандартный выход замещения с последующей успешной записью нулевой длины escape-последовательности \ c
% b
или неудачным преобразованием в %. d
ecimal такой же и той же длины если $ 1
не установлен и достигнут конец аргументов. Этот сбой завершит цикл while
. chk () {$ {1+:} выход; }
printf
успешно, то chk ()
сделал единственную попытку изменить какие-либо аргументы. С этого момента цикл while
может продолжать обрабатывать и печатать остальные аргументы, но chk ()
вообще ничего не будет делать, пока все они не будут исчерпаны, при этом точки, он просто выйдет из
подоболочки. И поэтому, как только второй шаблон совпадает хотя бы один раз, ни один из остальных больше никогда не совпадет. (-? *)
Текущий аргумент состоит как минимум из двух символов и начинается с тире. Этот шаблон более эксклюзивен, чем шаблон - * "$ 2"
над ним один раз $ O
, поэтому он может соответствовать только до тех пор, пока не будет найден хотя бы один аргумент не соответствует ему . Таким образом, все начальные параметры будут разделены с помощью getopts
и сопоставлены с [HPL]
.Если какой-либо начальный параметр не соответствует этому шаблону, функция вызывает себя рекурсивно, чтобы соответствовать шаблону над ним и переопределить chk ()
. Таким образом, любая последовательность arg, которая не обрабатывается явно, просто дословно передается, и findcmd
делает все, что пожелает, с результатами.
Для каждой начальной опции, которая соответствует - [HL]
, флаговая переменная $ L
устанавливается равной пустой строке. И для каждого из тех, которые соответствуют -P
, $ L
имеет значение не установлено
.
($ {M - $ {O =? *}})
Первый возникающий аргумент, который не соответствует -? *
, вызовет $ O
установлен на шаблон ? *
. После этого любой из первых двух шаблонов может соответствовать $ {O + $ 2} $ {2 -}
. Если когда-либо -maxdepth $ 2
совпадает и M =
устанавливается на пустую строку, этот шаблон никогда не сможет снова соответствовать другому ненулевому аргументу, а только одно совпадение второго шаблона требуется прекратить все попытки сопоставления любого из них.
Любой ненулевой аргумент, который встречается после первой последовательности параметров - [HLP]
и перед другой - *
или [\ (?!]
аргумент соответствует этому шаблону и проверяется на разрешение пути. Если $ L
не задано, то тест !! -L "$ {L- $ 2}"
пройдет успешно, если $ 2
является символической ссылкой или недопустимым именем пути, но в противном случае он неизменно терпит неудачу, поскольку никакое имя пути не может соответствовать нулевой строке $ {L =}
.
Только те аргументы, которые не прошли предыдущий тест, проверяются на !
отменяет совпадение inode с /
, и любой аргумент, который не проходит оба теста, приводит к установке $ rt
на себя плюс ! \ (-path "$ {[num]% /} / media / * -prune \)
строка, которая не записывается до совпадения второго шаблона или до конца аргументов, в зависимости от того, что наступит раньше.
No, no es necesario insertar retrasos, no tendrán ningún efecto sobre posibles errores.
Sin embargo, debe verificar si hay errores: la forma más sencilla de hacerlo en los sistemas actuales es agregar
set -e
al comienzo de su script. Consulte la guía de David Pashley para escribir scripts de shell robustos . Sin embargo, tenga en cuenta que set -e
no es una panacea, hay diferencias sutiles en el comportamiento según el shell que esté usando .