Руководство пользователя для GNU Awk
6. Печатный выводОглавление
Одно из наиболее частых действий есть печать, или вывод, части или всего входа. Оператор print используется для простого вывода. Оператор printf употребляют знатоки форматирования. Оба оператора описаны в этой главе. 6.1 Оператор printОператор print производит печать в стандартном формате. Вы только указываете цепочки и номера для печати в списке, разделенном запятыми. Они печатаются с разделением пробелами и newline. Оператор выглядит так: print item1, item2, ... Весь список можно заключать в скобки. Они необходимы, если в каком-нибудь из выражений в списке используется операция отношения `?' ; в противном случае он может быть спутан с перенаправлением (см. раздел 6.6 [Перенаправления выхода print и printf], стр. 70). Аргументы (items) могут быть строковыми постоянными или числами, полями текущей записи (такими как $1), переменными или другими выражениями awks. Численные значения превращаются в цепочки и затем печатаются. Оператор print руководствуется общими правилами. Кроме двух исключений, вы не можете указывать как печатать данные --- в сколько столбцов, использовать ли экспоненциальные обозначения для чисел и пр. (По поводу исключений см. раздел 6.3 [Разделители вывода], стр.63, и раздел 6.4 [Управление численным выводом при печати], стр 64.) Для этого нужен оператор printf (см. раздел 6.5 [Употребление операторов printf для декоративной печати], стр. 64). Простой оператор `print' без аргументов эквивалентен `print $0': он печатает всю текущую запись. Для печати пустой строки употребляют `print ""', где "" означает пустую цепочку. Для печати фиксированного куска текста используют строковые постоянные, такие как "Don't Panic" в качестве одного аргумента. Если вы забудете указать символы двойных кавычек, ваш текст будет воспринят как выражение awk и вы, вероятно, получите ошибку. Помните, что пробел печатается между каждой парой значений аргументов. Каждый оператор print выдает по крайней мере одну строку, но не ограничивается одной строкой. Если значение аргумента есть цепочка, содержащая newline, то newline входит в вывод вместе с остатком цепочки. Поэтому один оператор print этим способом может выдать любое число строк. 6.2 Примеры операторов printВот примеры печати цепочек, содержащих внутренние newline (`"n' есть управляющая последовательность для представления символа newline; см. раздел 4.2 [Управляющие последовательности], стр. 24): $ awk 'BEGIN - print "line one"nline two"nline three" ""' a line one a line two a line three Следущий пример печатает два первых поля каждой входной записи с пробелом между ними: $ awk '- print $1, $2 ""' inventory-shipped a Jan 13 a Feb 15 a Mar 15... Обычная ошибка при употреблении оператора --- пропуск запятой между двумя аргументами оператора print. Это часто приводит к печати аргументов на выходе подряд, без пробелов. Причина в том, что расположение рядом двух строковых выражений в awk означает их конкатенацию. Вот результат той же самой программы с пропущенной запятой: $ awk '- print $1 $2 ""' inventory-shipped a Jan13 a Feb15 a Mar15... Для человека, незнакомого с файлом `inventory-shipped', результат обоих примеров имеет мало смысла. Заголовок в начале пояснит результат. Добавим заголовки к нашей таблице месяцев ($1) и зеленых корзин, отправленных морем ($2). Сделаем это с помощью образца BEGIN (см. раздел 8.1.5 [Специальные образцы BEGIN и END], стр. 100) для печати заголовков один раз: awk 'BEGIN - print "Month Crates" print "----- ------" "" - print $1, $2 ""' inventory-shipped Вы уже догадались, что получится? При исполнении программа напечатает следующее: Month Crates ----- ------ Jan 13 Feb 15 Mar 15... Заголовки и табличные данные не выравниваются! Можно исправить это, печатая несколько пробелов между полями: awk 'BEGIN - print "Month Crates" print "----- ------" "" - print $1, " ", $2 ""' inventory-shipped Нетрудно себе представить, что этот путь окажется сложным, если используется много столбцов. Подсчет пробелов для двух или трех довольно прост, но если их больше, вы можете сбиться в подсчете. Для таких целей был создан оператор printf (см. раздел 6.5 [Использование операторов printf для декоративной печати], стр. 64); одно из его назначений состоит в выравнивании столбцов данных. Заметим, что можно продолжать операторы print или printf, используя newline после любой запятой (см. раздел 2.6 [awk-операторы и строки], стр. 17). 6.3 Разделители выходаКак уже говорилось, операторы печати содержат списки аргументов, разделенных запятыми. На печати образы аргументов нормально разделяются одиночными пробелами. Но это не обязательно так, а только по умолчанию. Можно указывать любую цепочку символов для использования в качестве разделителя полей на выходе с помощью встроенной переменной OFS. Начальное значение этой переменной есть цепочка " ", т.е. один пробел. Результат отдельного оператора печати называется выходной записью. Каждый оператор печати выдает одну выходную запись, а затем выдает цепочку, называемую разделителем выходных записей. Встроенная переменная ORS указывает эту цепочку. Ее начальное значение есть ""n", т.e. символ newline; таким образом, нормально каждый оператор печати выдает отдельную строку. Можно изменять разделители выходных полей и записей присваиванием новых значений переменным OFS и/или ORS. Обычное место делать это есть правило BEGIN (см. раздел 8.1.5 [Специальные образцы BEGIN и END], стр. 100), т.е. до обработки любого входа. Можно также делать это присваиванием в командной строке, перед именами входных файлов, или с помощью параметра командной строки `-v' (см. раздел 14.1 [Параметры командной строки], стр. 161). Следующий пример печатает первое и второе поля каждой входной записи, разделенные точкой с запятой и добавляет пустую строку после каждой печатаемой строки: $ awk 'BEGIN - OFS = ";"; ORS = ""n"n" "" ? - print $1, $2 ""' BBS-list a aardvark;555-5553 a alpo-net;555-3412 a barfly;555-7685... Усли значение ORS не содержит newline, весь выход пойдет в одну строку, если вы не вставите newline каким-нибудь другим путем. 6.4 Управление печатью численного выводаЕсли используете оператор print для печати числовых значений, awk внутри себя превращает число в цепочку символов и печатает эту цепочку. awk использует для этого функцию sprintf (см. раздел 12.3 [Встроенные функции для действий со строками], стр. 137). В настоящий момент достаточно сказать, что sprintf воспринимает указания формата о форме числа (или цепочки), и что имеется много видов форматирования чисел. Различные спецификации форматов подробно обсуждаются в разделе 6.5.2 [Буквы, управляющие форматом], стр. 65. Встроенная переменная OFMT содержит спецификацию формата по умолчанию, которую print использует с sprintf при превращении числа в цепочку символов для печати. Это значение OFMT есть "%.6g". Используя различные значения OFMT в качестве спецификаций формата, можно менять форму чисел, печатаемых оператором print. Вот короткий пример: $ awk 'BEGIN - ? OFMT = "%.0f" # печатает числа как целые (округленные) ? print 17.23 ""' a 17 Соответственно стандарту POSIX, действия awk становятся неопределенными, если OFMT содержит что-нибудь кроме спецификаций преобразования чисел с плавающей запятой (d.c.). 6.5 Использование операторов printf для декоративной печатиЕсли необходим более полный контроль за форматом печати, который обеспечивает print, используйте printf. С его помощью можно указывать ширину печати каждого аргумента и различные форматы для чисел (такие как основание системы счисления, печатать ли экспоненту, знак и сколько цифр после десятичной точки). Это делается с помощью цепочки, называемой форматной цепочкой, которая определяет, как и где печатать другие аргументы. 6.5.1 Введение в описание оператора printfОператор printf имеет следующую форму: printf формат, аргумент1, аргумент2, ... Весь список аргументов можно заключать в скобки. Скобки необходимы, если в в выражениях какого-нибудь аргумента использован знак `?' оператора отношения, так как иначе он может быть спутан с перенаправлением (см. раздел 6.6 [Перенаправление выхода print и printf], стр. 70). Разница между printf и print состоит в аргументе format. Он представляет выражение, значением которого является цепочка; она определяет форму вывода каждого из остальных аргументов и называется форматной строкой. Эта цепочка очень похожа на форматную строку функции printf в библиотеке ANSI C. Большая часть форматного текста без изменений поступает на вывод. В нем содержатся также части, специфицирующие формат, по одной на каждый аргумент. Каждый спецификатор формата предписывает поместить вывод следующего аргумента из списка аргументов в соответствующем ему месте форматного текста. Оператор printf не завершает автоматически вывод переходом к новой строке. Он выдает только то, что указано в форматной строке. Так что если вы хотите перейти к новой строке, вы должны указать это в форматной строке. Переменные разделителей вывода OFS и ORS не действуют на операторы printf. Например: BEGIN - ORS = ""nOUCH!"n"; OFS = "!" msg = "Don't Panic!"; printf "%s"n", msg "" Эта программа все так же печатает знакомый совет `Don't Panic!'. 6.5.2 Буквы управления форматомСпецификатор формата начинается с символа `%' и кончается буквой управления форматом; она сообщает оператору printf, как выводить аргумент. (Если вы фактически хотите вывести `%', пишите `%%'.) Буква управления форматом указывает, какой вид значения печатать. Остальная часть спецификатора формата состоит из выборочных модификаторов, служащих параметрами действия, такими как ширина поля. Приведем список букв управления форматом: c Печатает число в виде одного символа ASCII. Так, `printf "%c", 65' выводит букву `A'. Выводом строкового значения будет первый символ цепочки. d и i эквивалентны. Обе печатают десятичное целое. Спецификация `%i' введена для совместимости с ANSI C. e или E означают число в научной (экспоненциальной) форме. Например, printf "%4.3e"n", 1950 напечатает `1.950e+03', с четырьмя значащими цифрами, из которых три расположены после десятичной точки. `4.3' представляют модификаторы, обсуждаемые ниже. `%E' использует `E' вместо `e' в печатаемом результате. f означает печать числа с плавающей точкой. Например, printf "%4.3f", 1950 выдает `1950.000', с четырьмя значащими цифрами, три из которых следуют за десятичной точкой. `4.3' есть модификаторы, обсуждаемые ниже. g G означает печать числа в экспоненциальной форме или с плавающей точкой, выбирая ту, которая содержит меньше символов. Если результат печатается в экспоненциальной форме, `%G' использует `E' вместо `e'. o означает печать восьмеричного целого без знака. (В этой форме с основанием восемь используются цифры от `0' до `7'; десятичное число восемь представляется ка восьмеричное `10'.) s означает печать цепочки. x X означает печать шестнадцатеричных целых без знака. (В шестнадцатеричной форме с основанием 16 цифрами служат цифры от `0' до `9' и буквы от `a' до `f'. Шестнадцатеричная цифра `f' представляет десятичное число 15.) `%X' предписывает использовать буквы от `A' до `F' вместо букв от `a' до `f'. % этот символ в действительности не есть буква управления форматом, но имеет особый смысл при использовании после `%': последовательность `%%' выдает один `%'. Он не использует аргумент и игнорирует всякие модификаторы. При использовании букв, управляющих форматом целых чисел, для чисел, выходящих за пределы, установленные для целых типа C long, gawk переключается на формат `%g'. Другие версии awk могут печатать неправильные значения или предпринимать совершенно иные действия (d.c.). 6.5.3 Модификаторы в форматах для printfСпецификатор формата может включать в себя модификаторы, определяющие, какая порция значения аргумента печатается и сколько места получает. Модификаторы располагаются между `%' и управляющей форматом буквой. В следующих ниже примерах мы используем маркер "ffl" для представления пробелов в выводе. Приведем возможные модификаторы в порядке, в котором они могут появиться: - Знак минуса, используется перед модификатором ширины (см. ниже), требует выравнивания аргумента по левому краю поля указанной ширины. Нормально аргумент выравнивается по правому краю. Так, printf "%-4s", "foo" печатает `fooffl'. пробел При выдаче чисел предваряет пробелом положительные значения и минусом отрицательные. + Знак плюса пишется перед модификатором ширины (см. ниже), требует всегда ставить знак числа во всех случаях. `+' подавляет модификатор пробел. # Означает "альтернативную форму" для некоторых управляющих букв. Для `%o' требует добавить ведущий ноль. Для For `%x' и `%X' добавить ведущий `0x' или`0X' в случае ненулевого результата. Для `%e', `%E'и `%f' результат всегда будет иметь десятичную точку. При `%g' и `%G' завершающие нули не удаляются из результата. 0 Ведущий `0' (ноль) действует как флажок, указывающий, что вывод должен заполняться нулями вместо пробелов. Это применяется даже к нечисловым форматам вывода (d.c.). Этот флажок действует только тогда, когда ширина поля больше печатаемого значения. ширина Это число, указывающее желаемую минимальную ширину поля. Помещение любого числа между знаком `%' управляющий форматом буквы требует расширения поля до указанного размера. Способ сделать это по умолчанию состоит в заполнении поля пробелами слева. Например, printf "%4s", "foo" печатает `fflfoo'. Значение ширины есть минимальная ширина, а не максимальная. Если значение аргумента требует знаков больше, чем указанная ширина, ширина увеличивается до необходимой. Так printf "%4s", "foobar" печатает `foobar'. Указание знака минус перед шириной предписывает пополнять вывод пробелами справа, а не слева. .prec (точность) Это число, которое указывает точность при печати. В случае форматов `e', `E' и `f' оно означает количество цифр, которые вы хотите напечатать справа от десятичной точки. Для форматов `g' и `G' оно указывает максимальное количество значащих цифр. Для форматов `d', `o', `i', `u', `x' и `X' оно указывает минимальное количество печатаемых цифр. Для цепочек оно указывает максимальное количество знаков из цепочки, которые должны быть напечатаны. Таким образом, printf "%.4s", "foobar" печатает `foob'. Поддерживаются также динамическая ширина и свойство prec оператора printf из библиотеки Си (for example, "%*.*s"). Вместо явного указания ширины и/или значений prec в форматной строке их можно передавать в списке аргументов. Например: w = 5 p = 3 s = "abcdefg" printf "%*.*s"n", w, p, s в точности эквивалентно s = "abcdefg" printf "%5.3s"n", s Обе программы напечатают `fflfflabc'. Ранние версии awk не имели этой возможности. Если вам приходится использовать такую версию, можно моделировать упомянутое свойство, используя конкатенацию при построении форматной строки, подобен следующему: w = 5 p = 3 s = "abcdefg" printf "%" w "." p "s"n", s Это не очень легко читать, но это работает. Си-программисты могли привыкнуть употреблять дополнительные флажки `l' и `h' в форматной строке printf. Но они отсутствуют в awk. Большинство реализаций awk молчаливо игнорируют эти флажки. Если `--lint' указано в командной строке (см. раздел 14.1 [Параметры командной строки], стр. 161), gawk предостережет от их использования. Если указано `--posix', их использование приведет к фатальной ошибке. 6.5.4 Примеры употребления printfawk '- printf "%-10s %s"n", $1, $2 ""' BBS-list печатает имена досок объявлений ($1) из файла `BBS-list' в виде цепочки 10 символов, выровненной по левому краю. Она также печатает телефонные номера ($2) дальше в строке. Это оформляется в виде выровненной двух-столбцовой таблицы имен и телефонных номеров: $ awk '- printf "%-10s %s"n", $1, $2 ""' BBS-list a aardvark 555-5553 a alpo-net 555-3412 a barfly 555-7685 a bites 555-1675 a camelot 555-0542 a core 555-2912 a fooey 555-1234 a foot 555-6699 a macfoo 555-6480 a sdace 555-3430 a sabafoo 555-2127 Заметили ли вы, что мы не указывали, чтобы телефонные номера печатались как числа? Они должны печататься как цепочки, потому что разделены знаком дефиса Если бы мы пытались напечатать телефонные номера как числа, все что мы могли бы получить, было тремя первыми цифрами, `555'. Это было бы очень плохо. Мы не указывали ширину телефонных номеров, потому что они располагаются в концах своих строк. После них не нужно располагать пробелы. Мы могли бы сделать нашу таблицу красивее, добавив заголовки вверху колонок. Чтобы сделать это, используем образец BEGIN (см. раздел 8.1.5 [Специальные образцы BEGIN и END], стр.100), чтобы заголовок напечатался только один раз, в начале awk-программы: awk 'BEGIN - print "Name Number" print "---- ------" "" - printf "%-10s %s"n", $1, $2 ""' BBS-list В этом примере мы смешали опереторы print и printf. Мы могли употребить только операторы printf для получения того же результата: awk 'BEGIN - printf "%-10s %s"n", "Name", "Number" printf "%-10s %s"n", "----", "------" "" - printf "%-10s %s"n", $1, $2 ""' BBS-list Печатая заголовок каждой колонки с теми же спецификациями формата, которые использовались для элементов столбца, мы обеспечили выравнивание заголовков так же как и столбцов. Факт, что та же спецификация формата используется три раза, может быть подчеркнут помещением ее в переменную, подобно следующему: awk 'BEGIN - format = "%-10s %s"n" printf format, "Name", "Number" printf format, "----", "------" "" - printf format, $1, $2 ""' BBS-list Попробуйте с помощью printf расположить в одой линии заголовки и табличные данные для нашей `inventory-shipped', рассмотренной ранее в разделе об операторе print (см. раздел 6.1 [Оператор print], стр. 61). 6.6 Перенаправление вывода от print и printfДо сих пор мы имели дело с выходом от печати только на стандартное устройство, обычно на терминал. И print и printf могут также посылать результаты своей работы в другие места. Это называется перенаправлением. Перенаправление ставится после операторов print или printf. Перенаправление в awk записывается точно так же как и в командах оболочки, только они пишутся внутри awk-программы. Имеются три формы перенаправления: в файл, в конец файла, и в вывод по конвейеру в другую команду. Мы покажем их для оператора print, но они точно такие же и для printf. print items ? output-file Этот тип перенаправления печатает в файле с именем output-file, заданном любым выражением. Его значение преобразуется в цепочку и затем используется как имя файла (см. Главу 7 [Выражения], стр. 77). Когда используется такой тип перенаправления, выходной файл очищается перед выводом в него. Во втором случае результаты дописываются в конец указанного файла без его предварительной очистки. Если файла не было, он создается. Вот пример того, как awk-программа напишет список имен BBS в файл `name-list' и список телефонных номеров в файл `phone-list'. Каждый выходной файл содержит по одному имени или номеру в каждой строке. $ awk '- print $2 ? "phone-list" ? print $1 ? "name-list" ""' BBS-list $ cat phone-list a 555-5553 a 555-3412... $ cat name-list a aardvark a alpo-net... print items ?? output-file Этот тип перенаправления выдает результаты в существующий выходной файл с именем output-file. Разница между этим и перенаправлением с одним только `?' состоит в том, что прежнее содержимое ( если оно было) выходного файла не стирается. Вместо этого выход от awk приписывается в конец файла. Если файла не было, он создается вновь. Команда print itemsМожно также посылать результаты печати вместо файла в другую программу через конвейер. Этот тип перенаправления открывает конвейер для команды и передает значения аргументов по этому конвейеру другому процессу, созданному для выполнения команды. Команда перенаправления аргумента фактически является выражением awk. Его значение преобразуется в цепочку, содержание которой служит командой оболочки, подлежащей исполнению. Например, следущее производит два файла, один --- несортированный список имен BBS и другой --- список, отсортированный в обратном алфавитном порядке: awk '- print $1 ? "names.unsorted" command = "sort -r ? names.sorted" print $1 -- command ""' BBS-list Здесь не отсортированный список пишется с обычным перенаправлением, а отсортированный записывается по конвейеру через утилиту сортировки. Следующий пример использует перенаправление для отправки сообщения в почтовый список `bug-system'. Это может быть полезным поддержки системы, если ошибки часто обнаруживаются при исполнении сценария awk. report = "mail bug-system" print "Awk script failed:", $0 -- report m = ("at record number " FNR " of " FILENAME) print m -- report close(report) Сообщение строится с использованием конкатенации строк и сохраняется в переменной m. Затем оно посылается через конвейер в почтовую программу. Мы вызываем функцию close, потому что полезно закрывать конвейер как только весь вывод поступил в него. См. раздел 6.8 [Закрытие входных и выходных файлов и конвейеров], стр. 74, для подробного ознакомления. Этот пример также иллюстрирует использование переменных для представления файлов или команд: необязательно использовать всегда строковые константы. Использование переменных является полезной идеей, так как awk требует, чтобы всегда строковые значения одинаково записывались. Перенаправление вывода с использованием `?', `??' или `--' просит систему открывать файл или конвейер только если соответствующий файл или команда, указанные вами, еще не использовались вашей программой или были закрыты после последнего обращения к ним. Как ранее уже говорилось (см. раздел 5.8.8 [Обзор вариантов getline], стр. 59), многие реализации awk ограничивают число конвейеров, которые может открыть awk-программа, только одним! В gawk такого ограничения нет. Вы можете открыть их столько, сколько позволяет используемая операционная система. 6.7 Специальные имена файлов в gawkВыполняемые программы обычно используют три входных и выходных потока, доступных им для для чтения и записи. Это стандартные ввод, вывод и сообщения об ошибках. По умолчанию они привязаны к вашему терминалу, но часто перенаправляются оболочкой через операторы `!', `!!', `?', `??', `?&' и `--'. Выход по ошибке обычно используется для печати сообщений об ошибках; причина того, что имеются отдельные потоки standard output и standard error состоит в том, что их можно отдельно пере направлять. В других реализациях awk единственный способ написать сообщение об ошибке в standard error в awk-программе таков: print "Serious error detected!" -- "cat 1?&2" Это открывает конвейер к команде оболочки, которая имеет доступ к потоку standard error, полученный от процесса awk. Это далеко от элегантности и неэффективно, поскольку требует отдельного процесса. Поэтому авторы awk-программ часто отказываются от этого. Вместо него они выводят сообщения об ошибках на терминал подобно следующему: print "Serious error detected!" ? "/dev/tty"Это обычно имеет тот же самый эффект, но не всегда: хотя стандартным выходом для ошибок служит терминал, он может быть перенаправлен. И когда это случается, запись на терминал происходит неправильно. В случае, когда awk исполняется из фонового задания, оно вообще может не иметь терминала. Тогда открытие /dev/tty невозможно, и gawk генерирует специальные файловые имена для доступа к трем стандартным потокам. Когда вы пере направляете ввод или вывод в gawk и имя файла соответствует одному из этих специальных имен, то gawk непосредственно использует поток, которому оно соответствует. `/dev/stdin' Стандартный ввод (file descriptor 0). `/dev/stdout' Стандартный вывод (file descriptor 1). `/dev/stderr' Стандартный выход в случае ошибки (file descriptor 2). `/dev/fd/N ' Файл, соответствующий файловому дескриптору N. Такой файл должен быть открыт программой, инициирующей выполнение awk (обычно это делает оболочка). Если вы не предприняли специальных мер в оболочке, из которой вы запускаете gawk, вам доступны только дескрипторы 0, 1 и 2. Имена файлов `/dev/stdin', `/dev/stdout' и `/dev/stderr' являются алиасами для `/dev/fd/0', `/dev/fd/1' и `/dev/fd/2' соответственно, но они больше самопонятны. Хороший способ писать сообщения об ошибках в программах gawk ---- пользоваться именем `/dev/stderr' подобно следующему: print "Serious error detected!" ? "/dev/stderr"gawk имеет также специальные файловые имена, дающие доступ к информации о исполняющихся процессах gawk. Каждый из этих "файлов" обеспечивает одну запись с информацией. Чтобы прочесть их более одного раза, нужно сначала закрыть их функцией close (см. раздел 6.8 [Закрытие входных и выходных файлов и конвейеров], стр. 74). Имена этих файлов: `/dev/pid'Чтение этого файла сообщает идентификатор ID текущего процесса, в десятичном виде, заканчивая переводом строки. `/dev/ppid'Чтение этого файла выдает идентификатор процесса, породившего текущий процесс, в десятичной форме, заканчивая его переводом строки. `/dev/pgrpid'Чтение этого файла дает идентификатор группы процессов для текущего процесса. `/dev/user'Чтение этого файла выдает одну запись с полями, разделенными пробелами. Поля содержат следующую информацию: $1 Ответ на на системный вызов getuid (идентифицирующий номер пользователя (real user ID number)). $2 Ответ на системный вызов geteuid (the effective user ID number). $3 Ответ на системный вызов getgid (the real group ID number). $4 Ответ на системный вызов getegid (the effective group ID number). Если имеются дополнительные поля, они содержат групповые идентификаторы, сообщаемые системным вызовом getgroups. (Кратные группы поддерживаются не на всех системах.) Указанные специальные имена файлов могут использоваться в командной строке как файлы с данными , а также для перенаправлений I/O в программе awk. Они не должны использоваться как исходные файлы с параметром `-f'. Распознавание этих специальных файлов имен невозможно, если gawk действует в совместимом режиме (см. раздел 14.1 [Параметры командной строки], стр. 161). Внимание: если ваша система имеет каталог `/dev/fd' (или с именем одного из перечисленных выше специальных файлов), интерпретация этих файловых имен производится самой gawk. Например, использование `/dev/fd/4' для вывода приведет к записи на дескриптор файла 4, а не на дескриптор нового файла, который был скопирован (dup'ed) с файлового дескриптора 4. В большинстве случаев это не имеет значения; однако, важно не закрывать никаких файлов, относящихся к файловым дескрипторам 0, 1 и 2. Если закрыть один из таких файлов, могут произойти непредсказуемые результаты. Специальные файлы, содержащие относящуюся к процессу информацию, могут быть удалены из последующих версий gawk. См. раздел C.3 [Вероятные будущие расширения], стр.299. 6.8 Закрытие входных и выходных файлов и конвейеровЕсли одно и то же имя файла или той же команды оболочки использовано в getline (см. раздел 5.8 [Явный ввод с getline], стр. 53) более одного раза во время выполнения awk-программы, то файл открывается (или команда исполняется) только в первый раз. В это время первая запись ввода читается из этого файла или команды. В следующий раз, когда тот же файл или команда используются в getline, другая запись читается из них и т.д. Подобно этому, когда файл или команда открывается для вывода, имя файла или команды запоминается и впоследствии awk дописывает информацию в тот же самый файл или команду. Файл или конвейер остаются открытыми до выхода из awk. Из этого следует, что если вы хотите читать тот же самый файл опять с начала или хотите снова запустить ту же команду оболочки (вместо продолжения чтения информации из команды), вы должны предпринять специальные шаги. Именно, вы должны употребить функцию close следующим образом: close(имя файла) или close(команда) Аргументом close может быть любое выражение. Его значение должно точно соответствовать цепочке, используемой для открытия файла или запуска команды (включая пробелы и другие дополнительные символы). Например, если вы открываете конвейер так: "sort -r names" -- getline foo то вы должны закрывать его так: close("sort -r names") Если такой вызов функции исполняется, то следующая getline из этого файла или команды, или следующий print или printf для этого файла или команды снова откроет этот файл или перезапустит эту команду. По той причине, что выражение, использованное для закрытия файла или конвейера, должно точно соответствовать выражению, использованному при открытии файла или запуске команды, полезно использовать переменную для запоминания имени файла или команды. Предыдущий пример превратится в sortcom = "sort -r names" sortcom -- getline foo ... close(sortcom) Это поможет избежать трудно обнаруживаемых типографических ошибок в ваших awk-программах. Приведем несколько причин, по которым может понадобиться закрывать выходной файл: а. Записать в файл и позднее прочесть записанное в той же самой программе awk. Для этого закройте файл после окончания записи в него. Затем вы можете начать чтение из него по getline. б. Писать во многие файлы последовательно в той же самой awk-программе. Если вы не будете закрывать их, в конце концов вы превысите системный предел на количество открытых файлов в одном процессе. Так что закрывайте каждый файл после окончания записи в него. в. Осуществить конец команды. Когда вы пере направляете вывод через конвейер, команда чтения конвейера обычно продолжает читать ввод до тех пор, пока конвейер открыт. Часто это значит, что команда не может фактически выполнить свою работу, пока конвейер не будет закрыт. Например, если вы пере направляете вывод в почтовую программу, сообщение фактически не будет послано до закрытия конвейера. г. Выполнить ту же самую программу во второй раз с теми же аргументами. Это не то же самое, что дать больше входных данных при первом прогоне! Предположим, например, что вы передаете результат по конвейеру в почтовую программу. Если вы выводите несколько строк, перенаправленных в этот конвейер, не закрывая его, создается одно сообщение из нескольких строк. Наоборот, если вы закрываете конвейер после выдачи каждой строки, то каждая строка становится отдельным сообщением. Операция close возвращает в ответ 0, если успешно завершилась. В противном случае ответ будет не ноль и gawk устанавливает в переменной ERRNO цепочку с описанием ошибки. Если вы используете больше файлов, чем позволяет открыть система, gawk будет пытаться мультиплексировать доступные открытые файлы среди ваших файлов с данными. Способности gawk в этой области зависят от возможностей вашей операционной системы, и это не всегда сработает. Поэтому для надежности программы и большей переносимости следует всегда закрывать файлы после окончания работы с ними. |
<<< | Оглавление | Страницы: 6 7 | >>> |