Я бы использовал оболочку для этого используется функция , а не скрипт:
rm-all-or-none() {
for f; do
[ -f "$f" ] ||
{ printf '%s is not an existing file, no files removed\n' "$f" >&2
return 1;}
done
rm -fv -- "$@"
}
Тогда я бы вызвал его, используя фигурные скобки, а не глобус. Globs расширяются только до файлов, которые существуют , но в этом случае мы хотим указать файлы и удалить их только в том случае, если все из них существуют:
rm-all-or-none /tmp/bbsnode{1..4}
Более длинная эквивалентная версия:
rm-all-or-none() {
for f in "$@"; do
if [ -f "$f" ]; then
:
else
printf '%s is not an existing file, no files removed\n' "$f" >&2
return 1
fi
done
rm -fv -- "$@"
}
Также см .:
eval
и exec
- совершенно разные звери. (Кроме того факта, что оба выполняют команды, но и все, что вы делаете в оболочке)
$ help exec
exec: exec [-cl] [-a name] [command [arguments ...]] [redirection ...]
Replace the shell with the given command.
То, что делает exec cmd
, точно такое же, как просто запуск cmd
, за исключением того, что текущая оболочка заменяется командой, а не запускается отдельный процесс. Внутренне, запуск, скажем, /bin/ls
вызовет fork()
для создания дочернего процесса, а затем exec()
в дочернем процессе для выполнения /bin/ls
. exec /bin/ls
, с другой стороны, не форк, а просто заменяет оболочку.
Сравните:
$ bash -c 'echo $$ ; ls -l /proc/self ; echo foo'
7218
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7219
foo
с
$ bash -c 'echo $$ ; exec ls -l /proc/self ; echo foo'
7217
lrwxrwxrwx 1 root root 0 Jun 30 16:49 /proc/self -> 7217
echo $$
выводит PID запущенной оболочки, а листинг /proc/self
дает нам PID ls
, который был запущен из оболочки. Обычно идентификаторы процессов отличаются, но в случае с exec
shell и ls
имеют один и тот же идентификатор процесса. Кроме того, команда, следующая за exec
, не выполнялась, поскольку оболочка была заменена.
С другой стороны:
$ help eval
eval: eval [arg ...]
Execute arguments as a shell command.
eval
запустит аргументы как команду в текущей оболочке. Другими словами, eval foo bar
- это то же самое, что просто foo bar
. Но переменные будут расширены перед выполнением, поэтому мы можем выполнять команды, сохраненные в переменных оболочки:
$ unset bar
$ cmd="bar=foo"
$ eval "$cmd"
$ echo "$bar"
foo
Это не создаст дочерний процесс, поэтому переменная устанавливается в текущей оболочке. (Конечно, eval /bin/ls
создаст дочерний процесс, так же как и старый добрый /bin/ls
.)
Или мы можем иметь команду, которая выводит команды оболочки. Выполнение ssh-agent
запускает агента в фоновом режиме и выводит кучу назначений переменных, которые могут быть установлены в текущей оболочке и использованы дочерними процессами (команды ssh
, которые вы бы запустили). Следовательно, ssh-agent
может быть запущен с помощью:
eval $(ssh-agent)
И текущая оболочка получит переменные для наследования другими командами.
Конечно, если переменная cmd
будет содержать что-то вроде rm -rf $HOME
, то выполнение eval "$cmd"
не будет тем, что вы захотите сделать. Даже такие вещи, как подстановки команд внутри строки, будут обработаны, поэтому следует действительно убедиться, что входные данные для eval
безопасны, прежде чем использовать его.
Часто можно избежать eval
и даже случайно не смешать код и данные неправильным образом.
создание нового дочернего процесса, выполнение аргументов и возврат статуса выхода.
Что? Весь смысл eval
в том, что он никоим образом не создает дочерний процесс. Если я выполню
eval "cd /tmp"
eval в оболочке, то после этого текущая оболочка сменит каталог. Также exec
не создает новый дочерний процесс, вместо этого он меняет текущий исполняемый файл (а именно оболочку) на данный; идентификатор процесса (и открытые файлы и прочее) остаются прежними. В отличие от eval
, exec
не возвращается в вызывающую оболочку, если только сам exec
не потерпит неудачу из-за невозможности найти или загрузить исполняемый файл или из-за проблем с расширением аргументов.
eval
в основном интерпретирует свой аргумент(ы) как строку после конкатенации, то есть он будет делать дополнительный уровень расширения подстановочных знаков и разделения аргументов. exec
не делает ничего подобного.
exec
не создает новый процесс. Он заменяет текущий процесс новой командой. Если вы сделали это в командной строке, то это фактически завершит вашу сессию shell (и, возможно, выведет вас из системы или закроет окно терминала!)
например
ksh% bash
bash-4.2$ exec /bin/echo hello
hello
ksh%
Здесь я нахожусь в ksh
(моя обычная оболочка). Я запускаю bash
, а затем внутри bash я exec /bin/echo
. Мы видим, что после этого я был сброшен обратно в ksh
, потому что процесс bash
был заменен на /bin/echo
.
exec
используется для замены текущего процесса оболочки новым и обработки перенаправления потока/дескрипторов файлов, если команда не указана. eval
используется для оценки строк как команд. Оба могут использоваться для создания и выполнения команды с аргументами, известными во время выполнения, но exec
заменяет процесс текущей оболочки в дополнение к выполнению команд.
Синтаксис:
exec [-cl] [-a name] [command [arguments]]
Согласно мануалу, если указана команда, эта встроенная
...заменяет оболочку. Новый процесс не создается. Аргументы становятся аргументами команды.
Другими словами, если вы запускали bash
с PID 1234 и запускали exec top -u root
в этой оболочке, top
команда будет иметь PID 1234 и заменит ваш процесс оболочки.
Где это полезно? В чем-то, известном как сценарии-оболочки. Такие сценарии создают наборы аргументов или принимают определенные решения о том, какие переменные передавать в среду, а затем используют exec
для замены себя любой указанной командой и, конечно же, предоставляют те же аргументы, что и сценарий-оболочка. построенный по пути.
В руководстве также говорится следующее:
Если команда не указана, любые перенаправления вступают в силу в текущей оболочке.
Это позволяет нам перенаправлять что-либо из выходных потоков текущей оболочки в файл. Это может быть полезно для регистрации или фильтрации, когда вы не хотите видеть stdout
команд, а только stderr
. Например, вот так:
bash-4.3$ exec 3>&1
bash-4.3$ exec > test_redirect.txt
bash-4.3$ date
bash-4.3$ echo "HELLO WORLD"
bash-4.3$ exec >&3
bash-4.3$ cat test_redirect.txt
2017年 05月 20日 星期六 05:01:51 MDT
HELLO WORLD
Это поведение делает его удобным для регистрации в сценариях оболочки, перенаправления потоков в отдельные файлы или процессы и других забавных вещей. с файловыми дескрипторами.
На уровне исходного кода по крайней мере для bash
версии 4.3 встроенный exec
определен в builtins/exec.def
. Он анализирует полученные команды и, если они есть, передает их функции shell_execve()
, определенной в файле execute_cmd.c
.
Короче говоря, существует семейство команд exec
в языке программирования C, и shell_execve()
по сути представляет собой функцию-оболочку для execve
:
/* Call execve (), handling interpreting shell scripts, and handling
exec failures. */
int
shell_execve (command, args, env)
char *command;
char **args, **env;
{
В руководстве по bash 4.3 указано (выделение добавлено мной):
Аргументы считываются и объединяются в одну команду. Затем эта команда считывается и выполняется оболочкой, и ее статус выхода возвращается как значение eval.
Обратите внимание, что замены процесса не происходит.В отличие от exec
, где целью является имитация функциональности execve()
, встроенный eval
служит только для «оценки» аргументов, как если бы пользователь набрал их в командной строке. Таким образом, создаются новые процессы.
Где это может быть полезно? Как указал Жиль в этом ответе , «... eval используется не очень часто. В некоторых оболочках наиболее распространенным использованием является получение значения переменной, имя которой неизвестно до времени выполнения». Лично я использовал его в паре скриптов на Ubuntu, где было необходимо выполнить/оценить команду на основе конкретной рабочей области, которую в данный момент использовал пользователь.
На уровне исходного кода он определен в builtins/eval.def
и передает проанализированную входную строку в функцию evalstring()
.
Помимо прочего, eval
может назначать переменные, которые остаются в текущей среде выполнения оболочки, в то время как exec
не может:
$ eval x=42
$ echo $x
42
$ exec x=42
bash: exec: x=42: not found
Оценка
Эти работы:
$ echo hi
hi
$ eval "echo hi"
hi
$ exec echo hi
hi
Однако это не:
$ exec "echo hi"
bash: exec: echo hi: not found
$ "echo hi"
bash: echo hi: command not found
Замена образа процесса
Этот пример демонстрирует, как exec
заменяет образ своего вызывающего процесса:
# Get PID of current shell
sh$ echo $$
1234
# Enter a subshell with PID 5678
sh$ sh
# Check PID of subshell
sh-subshell$ echo $$
5678
# Run exec
sh-subshell$ exec echo $$
5678
# We are back in our original shell!
sh$ echo $$
1234
Обратите внимание, что exec echo $$
выполняется с PID подоболочки! Кроме того, после того, как он был завершен, мы вернулись в нашу исходную sh$
оболочку.
С другой стороны, eval
не не заменяет образ процесса. Скорее, он запускает данную команду, как обычно в самой оболочке. (Конечно, если вы запускаете команду, которая требует запуска процесса... она делает именно это!)
sh$ echo $$
1234
sh$ sh
sh-subshell$ echo $$
5678
sh-subshell$ eval echo $$
5678
# We are still in the subshell!
sh-subshell$ echo $$
5678