Посколькуread -n "$n"
(не является функцией POSIX ), и если stdin является терминальным устройством, read
выводит терминал из режима icanon
, иначе read
будет видеть только полные строки, возвращаемые встроенный строковый редактор терминальной строки, а затем считывает по одному байту за раз до тех пор, пока не будет прочитано $n
символов или новой строки (вы можете увидеть неожиданные результаты, если введены недопустимые символы ).
Читает до $n
символов из одной строки. Вам также нужно будет очистить $IFS
, чтобы он не удалял символы IFS из ввода.
Поскольку мы покидаем режим icanon
, ^D
больше не является особым. Поэтому, если вы нажмете Ctrl+D , будет прочитан символ ^D
.
Вы не увидите eof с терминального устройства, если терминал каким-либо образом не отключен. Если stdin — это другой тип файла, вы можете увидеть eof (, как в : | IFS= read -rn 1; echo "$?"
, где stdin — пустой канал, или с перенаправлением stdin из/dev/null
)
read
возвращает 0, если $n
символов (байтов, не являющихся частью допустимых символов, считаются за 1 символ )или была прочитана целая строка.
Таким образом, в особом случае запрашивается только один символ:
if IFS= read -rn 1 var; then
if [ "${#var}" -eq 0 ]; then
echo an empty line was read
else
printf %s "${#var} character "
(export LC_ALL=C; printf '%s\n' "made of ${#var} byte(s) was read")
fi
else
echo "EOF found"
fi
Сделать это с помощью POSIX довольно сложно.
Это было бы что-то вроде (, предполагая, что система ASCII -основана (, а не EBCDIC, например, )система):
readk() {
REPLY= ret=1
if [ -t 0 ]; then
saved_settings=$(stty -g)
stty -icanon min 1 time 0 icrnl
fi
while true; do
code=$(dd bs=1 count=1 2> /dev/null | od -An -vto1 | tr -cd 0-7)
[ -n "$code" ] || break
case $code in
000 | 012) ret=0; break;; # can't store NUL in variable anyway
(*) REPLY=$REPLY$(printf "\\$code");;
esac
if expr " $REPLY" : '.' > /dev/null; then
ret=0
break
fi
done
if [ -t 0 ]; then
stty "$saved_settings"
fi
return "$ret"
}
Обратите внимание, что мы возвращаемся только тогда, когда прочитан полный символ. Если ввод в неправильной кодировке (, отличной от кодировки локали ), например, если ваш терминал отправляет é
в кодировке iso8859 -1 (0xe9 ), когда мы ожидаем UTF -8 (0xc3 0xa9 ), тогда вы можете ввести сколько угодно é
, функция не вернется. bash
's read -n1
вернется после второго 0xe9 (и сохранит оба значения в переменной ), что немного лучше.
Если вы также хотите прочитать символ ^C
при Ctrl+C(вместо того, чтобы позволить ему убить ваш скрипт; также для ^Z
, ^\
... )или ^S
/ ^Q
после Ctrl+S/Q(вместо управления потоком )вы можете добавить -isig -ixon
к строке stty
. Обратите внимание, что bash
read -n1
тоже этого не делает (, он даже восстанавливает isig
, если он был выключен ).
Это не восстановит настройки tty, если скрипт будет убит (, например, если вы нажмете Ctrl+C . Вы можете добавить trap
, но это может переопределить другие trap
в скрипте.
Вы также можете использовать zsh
вместо bash
, где read -k
(, который предшествует ksh93
или bash
для read -n/-N
), считывает один символ с терминала и обрабатывает ^D
сам по себе (возвращает не -ноль, если этот символ введен )и не обрабатывает новую строку специально.
if read -k k; then
printf '1 character entered: %q\n' $k
fi
Конечно, это возможно. Просто настройте nc
как сопроцесс:
#!/bin/bash -x
# Greets the client with their hostname.
coproc ( nc -l -v localhost 3000 2>&1 )
{
declare "$( sed -e 's/^Connection from \([^ ]*\).*$/client="\1"/' -e 'q' )"
printf 'Hello there, user on %s\n' "$client"
} <&${COPROC[0]} >&${COPROC[1]}
kill "$COPROC_PID"
Для чего-то более сложного, например, обработки HTTP-запросов:
#!/bin/bash
# Handles one request, then kills nc and restarts
while true; do
coproc ( nc -l localhost 3000 2>&1 )
{
get_http_request_header
process_request
send_http_reply
} <&${COPROC[0]} >&${COPROC[1]}
kill "$COPROC_PID"
done
Команды get_http_request_header
,
process_request
, и
send_http_reply
могут быть функциями оболочки или отдельными сценариями, которые вы пишете.
Процедура get_http_request_header
будет получать свой стандартный ввод от клиента, подключенного к nc
, и анализировать запрос, возможно, сохраняя его во временном файле или устанавливая некоторые глобальные переменные или другой способ передать необходимую информацию в process_request
.
process_request
может обрабатывать компиляцию результата любым необходимым способом.
send_http_reply
будет печатать клиенту, просто записывая на стандартный вывод.
Другая возможная установка, при которой информация на стороне сервера просто передается по внутренним шагам через каналы:
#!/bin/bash
# Handles one request, then kills nc and restarts
while true; do
coproc ( nc -l localhost 3000 2>&1 )
{
get_http_request_header |
process_request |
send_http_reply
} <&${COPROC[0]} >&${COPROC[1]}
kill "$COPROC_PID"
done
На этот вопрос уже был дан ответ или его местонахождение в display-welcome -message-при-успешном-netcat-connection
Но, по сути, вы будете делать со своим слушателем следующее:
(echo "$VAR"; cat) |nc -nvlp 4444
Когда ваш клиент подключается, слушатель выводит эту переменную на стандартный вывод клиента.