2015-08-23 9 views
4

Согласно «читать -n» описание на странице руководства:чтения -N и IFS

-N nchars вернуться только после прочтения именно символы NCHARS, если EOF не встречается или читать раз вне, игнорируя любой разделитель

Однако, в ответ на следующую команду:

$ echo 'a b' | while read -N1 c; do echo ">>>$c<<<"; done 
>>>a<<< 
>>><<< 
>>>b<<< 
>>><<< 

как пространство и символ новой строки были переведены на пустую строку, в то время как в команде:

$ echo 'a b' | while IFS= read -N1 c; do echo ">>>$c<<<"; done 
>>>a<<< 
>>> <<< 
>>>b<<< 
>>> 
<<< 

пространство и новая линия были правильно сохранены в переменной.

Итак, у разделителей все еще есть обработка в команде «читать» или «пока», что я не понимаю.

Мы могли бы сравнить эти результаты с теми, с помощью «чтений -n», что руководство описывается как:

-n nchars вернуться после прочтения символов NCHARS вместо того, чтобы ждать новой строки, но если меньше чтить разделитель чем NCHARS символы читаются перед разделителем

$ echo 'a b' | while read -n1 c; do echo ">>>$c<<<"; done 
>>>a<<< 
>>><<< 
>>>b<<< 
>>><<< 

$ echo 'a b' | while IFS= read -n1 c; do echo ">>>$c<<<"; done 
>>>a<<< 
>>> <<< 
>>>b<<< 
>>><<< 

ответ

2

hexdump Использование позволяет увидеть именно символы, составляющие выходного сигнала, так что это может быть полезно, чтобы немного изменить свои запросы:

(1) При нормальном МФС и с помощью опции -N

$ (echo 'a b' | while read -N1 c; do c="$c<"; echo -n "$c"; done | hexdump -C) 
00000000 61 3c 3c 62 3c 3c         |a<<b<<| 
00000006 

В этом первом случае прочитанное встроенное значение для 0x0a и символ пробела возвращает пустую строку, так как символы находятся в IFS по умолчанию, а символы в IFS игнорируются на выходе по причине, объясненной в ans cdarke WER.

(2) С пустым МФСОМ и опцией -N

$ (IFS=""; echo 'a b' | while read -N1 c; do c="$c<"; echo -n "$c"; done | hexdump -C) 
00000000 61 3c 20 3c 62 3c 0a 3c        |a< <b<.<| 
00000008 

В этом случае чтение встроенное будет соответствовать каждому из четырех символов, что эхо-команда выводит, и оба 0x0a и пространство рассматриваются в едином вывод, потому что с пустым IFS считываемые символы могут быть назначены локальной переменной c.

(3) с нормальным МФСОМ и опцией -n

$ (echo 'a b' | while read -n1 c; do c="$c<"; echo -n "$c"; done | hexdump -C) 
00000000 61 3c 3c 62 3c 3c         |a<<b<<| 
00000006 

Это дает точно такой же результат, как при (1), хотя семантика немного отличается: для чтения и для встроенного 0x0a и пространства символ возвращает пустую строку, так как (i) оба этих символа находятся в IFS по умолчанию, и (ii) опция -n для считывания встроенного языка в любом случае не проходит по символу 0x0a

(4) С пустая опция IFS и -n

$ (IFS=""; echo 'a b' | while read -n1 c; do c="$c<"; echo -n "$c"; done | hexdump -C) 
00000000 61 3c 20 3c 62 3c 3c        |a< <b<<| 
00000007 

Здесь мы наблюдаем разницу между параметрами -n и -N для чтения: с опцией -n, новая строка обрабатывается специально с помощью встроенного чтения и отбрасывается, поэтому исключениеиз IFS не имеет возможность разрешить его передать локальной переменной c.

+0

Отличное объяснение, но еще одна открытая точка, новая строка в случае (4), которая обрабатывается иначе, чем пространство, даже если ни одно из них не относится к IFS. То есть, я думаю, что в случае (3) мы не можем сказать «0x0a, а символ пробела возвращает пустую строку, так как оба эти символа находятся в IFS по умолчанию». –

+0

@pasabaporaqui - Вы совершенно правы: семантика ключа -n означает, что он избыточен, что '0x0a' находится в IFS. Я изменил обсуждение, чтобы сделать эту избыточность ясной. –

+0

Отлично, просто редакционный комментарий в случае (2): «IFS unset» должен быть «IFS empty». –

3

на мой взгляд, при использовании опции -N поведение read отличается при

  • Чтение разделителя в качестве входных данных
  • Назначения, что разделитель в переменную

Когда он читает символ, разделитель угощение, как же, как и не-ограничитель и read будет считать их. Но, когда read назначает разделитель, он считает, что если считываемый ввод является разделителем или нет, если он является разделителем, он присваивает нуль соответствующей переменной.

Таким образом, IFS= изменит поведение присвоения пробела переменной и вызовет присвоение пространства c, а не null.

4

Это POSIX поведение. При назначении переменной символы IFS должны быть удалены: результаты должны быть разделены на поля, как в оболочке, для результатов расширения параметров (конечно, -n и -N не являются POSIX).

Это рождённый из кодовых комментариев read источника:

/* This code implements the Posix.2 spec for splitting the words 
    read and assigning them to variables. */ 
    orig_input_string = input_string; 

    /* Remove IFS white space at the beginning of the input string. If 
    $IFS is null, no field splitting is performed. */ 
+0

Очень интересно. Только преобразование новой строки в пустую строку в случае «в то время как IFS = read -n1 c» представляется трудно сопоставимым с этими описаниями. Я ожидал или «конец цикла» и ничего не печатал или не назначал новую строку. Фактически, этот случай является единственной разницей, обнаруженной между -n1 и -N1. –

+0

Новая строка является частью настройки 'IFS' по умолчанию, другими словами, это разделитель. Я не вижу никакой несогласованности. – cdarke

+0

Да, но по умолчанию IFS (unset IFS) не совпадает с пустым IFS, который используется в этом наборе. Кроме того, пространство также находится в наборе разделителей по умолчанию, и в этом тесте обрабатывается по-другому. –

1

read не может решить, если символ является разделителем (игнорировать его) до тех пор, пока уже прочитали характер, и read должен назначить some значение c, даже если это значение является пустой строкой. Когда разделитель считывается и затем отбрасывается, значение c должно быть установлено в чем-то, поэтому ему назначается пустая строка.

Это согласуется с read, используемым без опций -n/-N; разделители отбрасываются только после, они считываются и если нет необходимости устанавливать значение предоставленных параметров. Самый простой случай, когда вы не предоставляют никаких аргументов в read:

$ read <<< " a b c " 
$ echo ">>>$REPLY<<<" 
>>> a b c <<< 

С помощью одного явного аргумента, начальные и конечные разделители раздели:

$ read line <<< " a b c " 
$ echo ">>>$line<<<" 
>>>a b c<<< 

С двух аргументов, первый разделитель игнорируется после его чтения. Второй сохраняется, потому что строка должна быть разбита на два слова, чтобы заполнить предоставленные параметры.

$ read field1 field2 <<< " a b c """ 
$ echo ">>>$field1<<<" 
>>>a<<< 
$ echo ">>>$field2<<<" 
>>>b c<<<