Например, если вы хотите найти переменную HISTFILE и ее значение или хотите узнать, какие переменные определены для истории, вы должны ввести это в оболочке:
set | grep HIST
env | grep HIST
printenv | grep HIST
Поскольку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
В f()
измените %s
на %q
:
f() { read -rn 1 -p "Enter a character: " char && \
printf "\nYou entered '%q'\n" "$char"; }
f;f
Вывод, если пользователь вводит новую строку , затем ' Ctrl -D ':
Enter a character:
You entered ''''
Enter a character: ^D
You entered '$'\004''
Из `man printf:
%q ARGUMENT is printed in a format that can be reused as shell input,
escaping non-printable characters with the proposed POSIX $'' syntax.
read -r var
status=$?
echo "\$var='$var':\$?=$status"
Случаи новой строки и Ctrl -D различаются переменной состояния.
В случае перехода на новую строку статус true (0 ), в то время как при задании Ctrl -D, статус ложный (1)
На самом деле, если вы запустите read -rn1
в Bash и нажмете ^D
, он будет рассматриваться как буквальный управляющий символ, а не как условие конца строки. Управляющий символ просто не виден при печати, поэтому он не отображается с printf "'%s'"
. Направление вывода на что-то вроде od -c
показало бы это, как и printf "%q"
, которые уже упоминались в других ответах.
Если на входе ничего нет, результат будет другим, здесь пусто даже сprintf "%q"
:
$ f() { read -rn 1 x ; printf "%q\n" "$x"; }
$ printf "" | f
''
Здесь символ новой строки не возвращает read
по двум причинам. Во-первых, это разделитель строк по умолчанию для чтения, и, следовательно, он возвращается в качестве вывода. Во-вторых, это также часть IFS
по умолчанию, а read
удаляет начальные и конечные пробелы, если они являются частью IFS
.
Итак, нам нужно read -d
изменить разделитель по умолчанию,и делают IFS
пустым:
$ g() { IFS= read -rn 1 -d '' x ; printf "%q\n" "$x"; }
$ printf "\n" | g
$'\n'
read -d ""
делает разделитель эффективным байтом NUL, что означает, что это по-прежнему не указывает на разницу между вводом ничего и вводом байта NUL:
$ printf "" | g
''
$ printf "\000" | g
''
Несмотря на отсутствие входных данных, read
возвращает false, поэтому мы можем проверить $?
, чтобы обнаружить это.