Мендель Купер - Искусство программирования на языке сценариев командной оболочки
do
output=$(sed -n /"$1"/p $file) # Подстановка команд.
if [ ! -z "$output" ] # Что произойдет, если кавычки вокруг "$output" убрать?
then
echo -n "$file: "
echo $output
fi # эквивалент: sed -ne "/$1/s|^|${file}: |p"
echo
done
echo
exit 0
# Упражнения:
# ---------
# 1) Добавьте вывод символов перевода строки, если найдено более одного совпадения в любом из файлов.
# 2) Добавьте обработку различных ключей.
egrep -- то же самое, что и grep -E. Эта команда использует несколько отличающийся, расширенный набор регулярных выражений, что позволяет выполнять поиск более гибко.
fgrep -- то же самое, что и grep -F. Эта команда выполняет поиск строк символов (не регулярных выражений), что несколько увеличивает скорость поиска.
Утилита agrep имеет более широкие возможности поиска приблизительных совпадений. Образец поиска может отличаться от найденной строки на указанное число символов.
Для поиска по сжатым файлам следует использовать утилиты zgrep, zegrep или zfgrep. Они с успехом могут использоваться и для не сжатых файлов, но в этом случае они уступают в скорости обычным grep, egrep и fgrep. Они очень удобны при выполнении поиска по смешенному набору файлов -- когда одни файлы сжаты, а другие нет.
Для поиска по bzip-файлам используйте bzgrep.
look
Команда look очень похожа на grep, и предназначена для поиска по "словарям" -- отсортированным файлам. По-умолчанию, поиск выполняется в файле /usr/dict/words, но может быть указан и другой словарь.
Пример 12-13. Поиск слов в словаре
#!/bin/bash
# lookup: Выполняется поиск каждого слова из файла в словаре.
file=words.data # Файл с искомыми словами.
echo
while [ "$word" != end ] # Последнее слово в файле.
do
read word # Из файла, потому, что выполнено перенаправление в конце цикла.
look $word > /dev/null # Подавление вывода строк из словаря.
lookup=$? # Код возврата команды 'look'.
if [ "$lookup" -eq 0 ]
then
echo "Слово "$word" найдено."
else
echo "Слово "$word" не найдено."
fi
done <"$file" # Перенаправление ввода из файла $file, так что "чтение" производится оттуда.
echo
exit 0
# ----------------------------------------------------------------
# Строки, расположенные ниже не будут исполнены, поскольку выше стоит команда "exit".
# Stephane Chazelas предложил более короткий вариант:
while read word && [[ $word != end ]]
do if look "$word" > /dev/null
then echo "Слово "$word" найдено."
else echo "Слово "$word" не найдено."
fi
done <"$file"
exit 0
sed, awk
Скриптовые языки, специально разработанные для анализа текстовых данных.
sed
Неинтерактивный "потоковый редактор". Широко используется в сценариях на языке командной оболочки.
awk
Утилита контекстного поиска и преобразования текста, замечательный инструмент для извлечения и/или обработки полей (колонок) в структурированных текстовых файлах. Синтаксис awk напоминает язык C.
wc
wc -- "word count", счетчик слов в файле или в потоке:
bash $ wc /usr/doc/sed-3.02/README
20 127 838 /usr/doc/sed-3.02/README
[20 строк 127 слов 838 символов]
wc -w подсчитывает только слова.
wc -l подсчитывает только строки.
wc -c подсчитывает только символы.
wc -L возвращает длину наибольшей строки.
Подсчет количества .txt-файлов в текущем каталоге с помощью wc:
$ ls *.txt | wc -l
# Эта команда будет работать, если ни в одном из имен файлов "*.txt" нет символа перевода строки.
# Альтернативный вариант:
# find . -maxdepth 1 -name *.txt -print0 | grep -cz .
# (shopt -s nullglob; set -- *.txt; echo $#)
# Спасибо S.C.
Подсчет общего размера файлов, чьи имена начинаются с символов, в диапазоне d - h
bash$ wc [d-h]* | grep total | awk '{print $3}'
71832
От переводчика: в случае, если у вас локаль отлична от "C", то вышеприведенная команда может не дать результата, поскольку wc вернет не слово "total", в конце вывода, а "итого". Тогда можно попробовать несколько измененный вариант:
bash$ wc [d-h]* | grep итого | awk '{print $3}'
71832
Использование wc для подсчета количества вхождений слова "Linux" в основной исходный файл с текстом этого руководства.
bash$ grep Linux abs-book.sgml | wc -l
50
См. также Пример 12-30 и Пример 16-7.
Отдельные команды располагают функциональностью wc в виде своих ключей.
... | grep foo | wc -l
# Часто встречающаяся конструкция, которая может быть сокращена.
... | grep -c foo
# Ключ "-c" ("--count") команды grep.
# Спасибо S.C.
tr
Замена одних символов на другие.
В отдельных случаях символы необходимо заключать в кавычки и/или квадратные скобки. Кавычки предотвращают интерпретацию специальных символов командной оболочкой. Квадратные скобки должны заключаться в кавычки.
Команда tr "A-Z" "*" <filename или tr A-Z * <filename заменяет все символы верхнего регистра в filename на звездочки (вывод производится на stdout). В некоторых системах этот вариант может оказаться неработоспособным, тогда попробуйте tr A-Z '[**]'.
Ключ -d удаляет символы из заданного диапазона.
echo "abcdef" # abcdef
echo "abcdef" | tr -d b-d # aef
tr -d 0-9 <filename
# Удалит все цифровые символы из файла "filename".
Ключ --squeeze-repeats (-s) удалит все повторяющиеся последовательности символов. Может использоваться для удаления лишних пробельных символов.
bash$ echo "XXXXX" | tr --squeeze-repeats 'X'
X
Ключ -c "complement" заменит символы в соответствии с шаблоном. Этот ключ воздействует только на те символы, которые НЕ соответствуют заданному шаблону.
bash$ echo "acfdeb123" | tr -c b-d +
+c+d+b++++
Обратите внимание: команда tr корректно распознает символьные классы POSIX[ 29 ].
bash$ echo "abcd2ef1" | tr '[:alpha:]' -
----2--1
Пример 12-14. toupper: Преобразование символов в верхний регистр.
#!/bin/bash
# Преобразование символов в верхний регистр.
E_BADARGS=65
if [ -z "$1" ] # Стандартная проверка командной строки.
then
echo "Порядок использования: `basename $0` filename"
exit $E_BADARGS
fi
tr a-z A-Z <"$1"
# Тот же эффект можно получить при использовании символьных классов POSIX:
# tr '[:lower:]' '[:upper:]' <"$1"
# Спасибо S.C.
exit 0
Пример 12-15. lowercase: Изменение имен всех файлов в текущем каталоге в нижний регистр.
#! /bin/bash
#
# Изменит все имена файлов в текущем каталоге в нижнй регистр.
#
for filename in * # Обход всех файлов в каталоге.
do
fname=`basename $filename`
n=`echo $fname | tr A-Z a-z` # Перевести символы в нижний регистр.
if [ "$fname" != "$n" ] # Переименовать только те файлы, имена которых изменились.
then
mv $fname $n
fi
done
exit 0
# Сироки приведенные ниже не будут исполняться, поскольку выше стоит команда "exit".
#--------------------------------------------------------#
# Запустите эту часть сценария, удалив строки , стоящие выше.
# Сценарий, приведенный выше, не работает с именами файлов, содержащими пробелы или символы перевода строки.
# В связи с этим, Stephane Chazelas предложил следующий вариант:
for filename in * # Нет необходимости использовать basename,
# поскольку "*" возвращает имена, не содержащие "/".