Деякі люди мають таке помилкове поняття, що readце команда читати рядок. Це не.
readчитає слова з (можливо, продовження зворотної косої риски), де слова $IFSрозмежовані і зворотна косою рисою може бути використана для виходу з роздільників (або продовження рядків).
Родовим синтаксисом є:
read word1 word2... remaining_words
readчитає STDIN один байт в той час , поки він не знайде неекранований символ нового рядка (або кінець вхідного тексту), розщеплюється , що в відповідності зі складними правилами і зберігає результат цього поділу на $word1, $word2... $remaining_words.
Наприклад, на вході, як-от:
<tab> foo bar\ baz bl\ah blah\
whatever whatever
і зі значенням за замовчуванням $IFS, read a b cпризначить:
$a ⇐ foo
$b ⇐ bar baz
$c ⇐ blah blahwhatever whatever
Тепер, якщо було передано лише один аргумент, цього не стане read line. Це все ще read remaining_words. Обробка зворотного косого ряду все ще виконується, символи пробілів IFS все ще видаляються з початку і в кінці.
-rОпція видаляє обробку зворотної косою. Тож та сама команда, що була наведена вище -r, замість цього призначить
$a ⇐ foo
$b ⇐ bar\
$c ⇐ baz bl\ah blah\
Тепер для розділення частини важливо усвідомити, що існує два класи символів $IFS: символи пробілу IFS (а саме пробіл та вкладка (і новий рядок, хоча тут це не має значення, якщо ви не використовуєте -d), що також трапляється бути у стандартному значенні $IFS) та інші. Лікування цих двох класів персонажів різне.
З IFS=:( :будучи не IFS символ пробілу), вхід як :foo::bar::би розщеплюється на "", "foo", "", barі ""(і додатково ""з деякими реалізаціями , хоча це не має значення , за винятком read -a). Хоча якщо ми замінимо це :на простір, розщеплення робиться лише на fooі bar. Тобто провідні та відсталі ігноруються, і послідовності з них трактуються як одна. Існують додаткові правила, коли символи пробілів та пробілів не поєднуються $IFS. Деякі реалізації можуть додавати / видаляти спеціальну обробку, подвоюючи символи в IFS ( IFS=::або IFS=' ').
Тож тут, якщо ми не хочемо, щоб провідні та відмінні символи пробілу були зняті, нам потрібно видалити ці символи пробілу IFS з IFS.
Навіть із символами IFS-непробільного простору, якщо рядок введення містить один (і лише один) цих символів, і це останній символ у рядку (як IFS=: read -r wordна вході, як foo:) із оболонками POSIX (не, zshані деякі pdkshверсії), цей вхід вважається одним fooсловом, оскільки в цих оболонках символи $IFSрозглядаються як термінатори , тому wordміститимуть foo, не foo:.
Отже, канонічним способом зчитування одного рядка введення з readвбудованим є:
IFS= read -r line
(зауважте, що для більшості readреалізацій це працює лише для текстових рядків, оскільки символ NUL не підтримується, за винятком zsh).
Використовуючи var=value cmdсинтаксис, переконайтеся, що IFSвін встановлюється по-різному лише протягом тривалості цієї cmdкоманди.
Примітка історії
readВбудований був введений Bourne оболонки і вже читати слова , а НЕ лінії. У сучасних оболонок POSIX є кілька важливих відмінностей.
Оболонка Bourne readне підтримувала -rпараметр (який був введений оболонкою Korn), тому немає жодного способу відключити обробку зворотної косої риси, окрім попередньої обробки вводу чимось подібним sed 's/\\/&&/g'.
Оболонка Борна не мала такого поняття про два класи персонажів (що знову було введено ksh). В оболонці Борна все символи пройти таке ж лікування , як IFS пробільні символи роблять в KSH, тобто IFS=: read a b cна вході , як foo::barби призначити barна $b, а не пустити рядок.
У оболонці Борна:
var=value cmd
Якщо cmdце вбудований (як readє), varзалишається встановленим valueпісля cmdзакінчення. Це особливо важливо, $IFSтому що в оболонці Борна $IFSвикористовується для розділення всього, а не тільки розширень. Крім того, якщо ви видалите пробіл із символу $IFSоболонки Борна, він "$@"більше не працює.
У оболонці Bourne перенаправлення складеної команди змушує її запускатись в нижній частині корпусу (у ранніх версіях навіть такі речі, як, read var < fileабо exec 3< file; read var <&3не працювали), тому в оболонці Bourne рідко можна було використовувати readбудь-що, крім введення користувача в термінал (де сенс обробки продовження рядка мав сенс)
Деякі Unices (наприклад, HP / UX, також є один в util-linux) все ще мають lineкоманду зчитувати один рядок входу (який раніше був стандартною командою UNIX до єдиної специфікації UNIX версії 2 ).
Це в основному те саме, що head -n 1за винятком того, що він читає один байт за один раз, щоб переконатися, що він не читає більше одного рядка. У цих системах ви можете:
line=`line`
Звичайно, це означає нерестування нового процесу, виконайте команду і прочитайте її вихід через трубу, так що набагато менш ефективний, ніж ksh IFS= read -r line, але все ж набагато більш інтуїтивно зрозумілий.