Возможность тестирования с помощью кода ниже, используя Perl, чтобы избежать буферизации, попробуйте, работает ли это для вас
Пример версии P
$ cat /tmp/P
#!/bin/bash
read input
if [[ $input = "Hello" ]]
then
echo "Why?"
else
exit 1
fi
echo "Got Hello from client, working ..."
sleep 10
echo "Need to read some input"
read x
echo "Got: $x"
Программа-оболочка
$ cat /tmp/wrapper
#!/usr/bin/zsh
coproc /tmp/P
print -p Hello # Send Hello to P
read -pr line # Read what P has to say
if [[ "$line" = "Why?" ]]; then
perl -e '$|=1;print $_ while(<>);' <& p &
perl -e '$|=1;print $_ while(<>);' >& p
else
echo "Could not get P's attention."
fi
Тестовый запуск
$ / tmp / wrapper {{1 }} Получил привет от клиента, работает ... Необходимо прочитать вводные данные Привет, P! <== Набрано на теминале Получил: привет, П!
set -u
или
set -o nounset
Это заставит текущую оболочку интерпретировать раскрытие неустановленных переменных как ошибку:
$ unset build
$ set -u
$ rm -rf "$build"/*
bash: build: unbound variable
set -u
и set -o nounset
являются параметрами оболочки POSIX .
Однако пустое значение , а не вызовет ошибку.
Для этого используйте
$ rm -rf "${build:?Error, variable is empty or unset}"/*
bash: build: Error, variable is empty or unset
Расширение ${variable:?word}
будет расширено до значения variable
, если только оно не пусто или не установлено. Если он пуст или не установлен, word
будет отображаться при стандартной ошибке, и оболочка будет рассматривать расширение как ошибку (, команда не будет выполнена, а при запуске в не -интерактивной оболочке это будет прекратить ). Отсутствие :
вызовет ошибку только для неустановленного значения, как и в set -u
.
${variable:?word}
— это расширение параметра POSIX .
Ни один из них не приведет к завершению работы интерактивной оболочки, если только не будут действоватьset -e
(или set -o errexit
). ${variable:?word}
вызывает завершение работы скриптов, если переменная пуста или не установлена.set -u
вызовет завершение работы скрипта, если используется вместе с set -e
.
Что касается вашего второго вопроса. Невозможно ограничить rm
, чтобы он не работал за пределами текущего каталога.
Реализация GNU rm
имеет параметр --one-file-system
, который предотвращает рекурсивное удаление смонтированных файловых систем, но я полагаю, что это самое близкое, что мы можем получить, не заключая вызов rm
в функцию, которая фактически проверяет аргументы..
В качестве примечания:${build}
в точности эквивалентно $build
, за исключением случаев, когда раскрытие происходит как часть строки, где непосредственно следующий символ является допустимым символом в имени переменной, например, в "${build}x"
.
Я собираюсь предложить обычные проверки с использованием test
/[ ]
Вы были бы в безопасности, если бы написали свой сценарий как таковой:
build="build"
...
[ -n "${build}" ] || exit 1
rm -rf "${build}/"*
...
[ -n "${build}" ]
проверяет, что "${build}"
является строкой не -нулевой длины .
||
— это логический оператор ИЛИ в bash. Это вызывает запуск другой команды, если первая не удалась.
Таким образом, если бы ${build}
было пустым/неопределенным/и т.д. сценарий завершился бы (с кодом возврата 1, что является общей ошибкой ).
Это также защитит вас в случае, если вы удалите все варианты использования ${build}
, потому что [ -n "" ]
всегда будет ложным.
Преимущество использования test
/ [ ]
заключается в том, что существует множество других более значимых проверок, которые он также может использовать.
Например:
[ -f FILE ] True if FILE exists and is a regular file.
[ -d FILE ] True if FILE exists and is a directory.
[ -O FILE ] True if FILE exists and is owned by the effective user ID.
Вы мыслите в терминах языка программирования, но bash — это язык сценариев :-)Итак, используйте совершенно другую парадигму инструкций для совершенно другого языка парадигма.
В этом случае:
rmdir ${build}
Поскольку rmdir
откажется удалить не -пустой каталог, вам необходимо сначала удалить элементы. Вы знаете, что это за участники, верно? Вероятно, это параметры вашего скрипта или производные от параметра, поэтому:
rm -rf ${build}/${parameter}
rmdir ${build}
Теперь, если вы поместите туда какие-либо другие файлы или каталоги, например, временный файл или что-то, чего вам не следовало, rmdir
выдаст ошибку. Обращайтесь с этим должным образом, затем:
rmdir ${build} || build_dir_not_empty "${build}"
Этот способ мышления сослужил мне хорошую службу, потому что... да, я был там, делал это.
В вашем конкретном случае я переработал «удаление» в прошлом, чтобы вместо этого перемещать файлы/каталоги (при условии, что /tmp находится в том же разделе, что и ваш каталог):
# mktemp -d is also a good, reliable choice
trashdir=/tmp/.trash-$USER/trash-`date`
mkdir -p "$trashdir"
...
mv "${build}"/* "$trashdir"
...
За кулисами это перемещает ссылки на файлы/каталоги верхнего уровня из источника в структуры каталогов назначения $trashdir
в одном и том же разделе и не тратит время на просмотр структуры каталогов и освобождение каждого файла -. блокирует диск прямо здесь и сейчас. Это обеспечивает гораздо более быструю очистку, пока система активно используется, в обмен на более медленную перезагрузку (/tmp очищается при перезагрузке ).
В качестве альтернативы запись cron для периодической очистки /tmp/.trash -$USER предотвратит заполнение /tmp для процессов (, например сборок ), которые занимают много места на диске.Если ваш каталог находится в другом разделе, чем /tmp, вы можете создать аналогичный каталог /tmp -в своем разделе и вместо этого очистить его с помощью cron.
Но самое главное, если вы каким-либо образом испортите переменные, вы сможете восстановить содержимое до того, как произойдет очистка.
Используйте подстановку параметров bash для назначения значений по умолчанию, когда переменная не инициализирована, например:
rm -rf ${переменная :-"/несуществующая"}
Я всегда стараюсь начинать свои сценарии Bash со строки #!/bin/bash -ue
.
-e
означает «сбой при первой непосещенной e ошибке»;
-u
означает "сбой при первом использовании u nобъявленной переменной".
Подробнее см. в отличной статье Используйте неофициальный строгий режим Bash (Если вы не любите отладку). Также автор рекомендует использовать set -o pipefail; IFS=$'\n\t'
, но для моих целей это перебор.
Общий совет проверить, установлена ли ваша переменная, является полезным инструментом для предотвращения такого рода проблем. Но в этом случае было более простое решение.
Скорее всего, для удаления содержимого каталога $build
нет необходимости, но не самого фактического каталога $build
. Таким образом, если вы пропустите лишнее *
, тогда неустановленное значение превратится в rm -rf /
, которое по умолчанию большинство реализаций rm за последнее десятилетие откажутся выполнять (, если вы не отключите эту защиту с помощью опции GNU rm --no-preserve-root
). ].
Пропуск конечного /
также приведет к rm ''
, что приведет к сообщению об ошибке:
rm: can't remove '': No such file or directory
Это работает, даже если ваша команда rm не реализует защиту для /
.