Чтобы уточнить еще несколько вещей. В:
echo foo >&3
echo
не записывает "foo\n"
в свой файловый дескриптор 3. echo
всегда пишет в стандартный вывод, в свой файловый дескриптор 1. Он вызывает системный вызов write()
с целым числом 1
в качестве первого аргумента, указатель на область в памяти, которая начинается с foo\n
в качестве второго аргумента и4
(длиныfoo\n
)в качестве третьего.
В C вы бы написали это write(1, "foo\n", 4)
. В приведенном выше коде оболочка перенаправляет fd 1 на то же описание открытого файла , что и в fd 3, перед вызовомecho
(с помощью системного вызова dup2()
). Таким образом, хотя это функционально эквивалентно, это не то же самое, что выполнение write(3, "foo\n", 4)
. По сути, это что-то вроде (упрощенного):
if (pid = fork())
waitpid(pid,...);
else {
dup2(3, 1);
execlp("echo", "foo", 0);
}
И echo
делаетwrite(1, "foo\n", 4)
За исключением того, что практически во всех оболочках echo
встроен, поэтому нет fork
или exec
. Вместо этого оболочка делает:
saved_stdout = dup(1);
dup2(3, 1);
builtin_echo("foo");
dup2(saved_stdout, 1); close(saved_stdout); /* restore stdout */
(где builtin_echo()
— функция, выполняющая write(1, "foo\n", 4)
в том же процессе ).
Для команды, которая выполняет write(3, "foo\n", 4)
, вы можете взглянуть на ksh
/ zsh
встроенную команду print -u3 foo
.
Теперь каждый процесс может использовать файловые дескрипторы по своему усмотрению. За исключением того, что 0, 1 и 2 по соглашению зарезервированы для stdin, stdout и stderr. Другие fds, как правило, не являются специальными, но в оболочках (, которые являются всего лишь одним типом приложений ), fds от 0 до <some-value>
, где некоторое значение равно как минимум 9, зарезервированы для использования пользователем (оболочки. ). Скорлупа не будет смешиваться с ними для собственного внутреннего супа. Например, мой saved_stdout = dup(1)
выше был приблизительным. В результате,оболочка позаботится о том, чтобы saved_stdout
было значение больше, чем <some-value>
.
Теперь, поскольку к fds за пределами 0,1,2 не прилагается соглашение, вы не можете ожидать, что что-либо будет открыто на fd 3. Скорее всего, оно будет закрыто. Или, если это не так, вероятно, вызывающая сторона вашего скрипта просто забыла закрыть его (или добавить к нему флаг O_CLOEXC
), так как не было причин, по которым он оставил его открытым для вас, как никто не ожидает. все, что должно быть открыто на fd 3.
Вы бы использовали fd 3, если бы знали, что он был открыт на что-то, как правило, вами заранее в том же скрипте, как в:
{
var=$(cmd 2>&1 >&3)
} 3>&1
Там, где fd 3 было определено как dup()
fd 1 до , мы используем его для cmd
–dup()
обратно на fd 1.
Похоже, я могу указать зашифрованный пароль, чтобы добавить пользователя с
useradd -p '*' testuser
Раньше это не работало, потому что я забыл, что оболочка пыталась расширить звездочку, когда я пропускал одинарные кавычки.
Похоже, я мог бы также использовать
usermod -p '*' testuser
для существующего пользователя, чтобы также установить пароль на звездочку.