05.10.2008

Обработка текстов в *nix

Источник

Очень часто администраторам *nix-систем приходится выполнять различные выборки, сортировки, группировки неструктурированных данных. Благо система имеет уйму утилит для этого.

Рассмотрим в примерах выборку определённых полей файла. Надо отобрать из файла данные размещённые в первой, пятой и шестой колонках файла /etc/passwd. Существует множество решений, ниже приведены лишь несколько из них:

1. cat /etc/passwd | awk -F\: '{print $1" "$5" "$6}'

...
avahi Avahi mDNS daemon,,, /var/run/avahi-daemon
haldaemon Hardware abstraction layer,,, /home/haldaemon
...

2. cat /etc/passwd | awk -F\: '{print $1":"$5":"$6}'

...
avahi:Avahi mDNS daemon,,,:/var/run/avahi-daemon
haldaemon:Hardware abstraction layer,,,:/home/haldaemon
...

3. cat /etc/passwd | cut -d":" -f1,5,6

...
avahi:Avahi mDNS daemon,,,:/var/run/avahi-daemon
haldaemon:Hardware abstraction layer,,,:/home/haldaemon
...

4. cut -d":" -f1,5,6 /etc/passwd

...
avahi:Avahi mDNS daemon,,,:/var/run/avahi-daemon
haldaemon:Hardware abstraction layer,,,:/home/haldaemon
...

Чем отличаются друг от друга эти варианты?

Первые два варианта: 1 и 2, - используют cat и awk. Первая утилита выводит содержимое файла /etc/passwd, вторая отбирает необходимые поля. "-F\:" указывает на то, что разделителем полей в потоке служит двоеточие. Обратный слэш (\) перед двоеточием предписывает читать двоеточие, как двоеточие и не пытаться обработать его, как спецсимвол. В общем-то, обратный слэш используется в данном случае для перестраховки. Обратите внимание, что в первом случае мы вывели поля (нумеруются начиная с 1), разделённые пробелом, а во втором - двоеточием. Если вам необходимо модифицировать вывод, используйте эту связку, если вам нужно просто вывести требуемые поля пользуйтесь вариантами три или четыре.

В третьем варианте используется связка cat и cut. Первый выводит, второй режет :) Эту связку можно можифицировать так, как указано в варианте 4, так как cut может принимать на вход и поток (вариант три), и файл (вариант четыре). -d":" - указывает считать разделителем полей двоеточие. Здесь разделитель указан в кавычках, поэтому использовать обратный слэш (\) нет необходимости. -f1,5,6 говорит о том, что нужно отобразить 1-ое, 5-ое и 6-ое поля. Имейте ввиду, что для cut порядок полей, пречисленных после -f, не имеет значения. Поля будут выводится так, как они расположены в файле. Другими словами, вы просто указываете какие поля вас интересуют, а не порядок вывода полей. Если вам необходимо изменить порядок полей, воспользуйтесь awk.Например, так:
cat /etc/passwd | awk -F\: '{print $6" "$5" "$1}'

...
/var/run/avahi-daemon Avahi mDNS daemon,,, avahi
/home/haldaemon Hardware abstraction layer,,, haldaemon
...

Кроме того, awk также умеет получать данные не только из потока, но и напрямую из файла. Т.е. последний пример мы можем переписать так:
awk -F\: '{print $6" "$5" "$1}' /etc/passwd

...
/var/run/avahi-daemon Avahi mDNS daemon,,, avahi
/home/haldaemon Hardware abstraction layer,,, haldaemon
...


Почему я чаще пользуюсь конструкцией "cat | awk" или "cat | cut"? Всё просто. Когда я пытаюсь отобрать поля, я не всегда помню их точное положение в файле. Тогда я делаю:
cat /etc/passwd

Получаю полный список. После чего, уточнив нужные поля, нажимаю стрелочку вверх (в bash это вызов предыдущей команды) и дописываю | awk........

Мне так удобнее. Я не говорю, что так нужно делать всем. Но если кто-то решит, что способ, которым он пользовался до сих пор, хуже предложенного мною и станет пользоваться им, значит не так уж и плох мой способ. :)