Как экранировать символы в sed

Скрипты программы sed

Для того, чтобы пользоваться редактором sed как полноценным текстовым редактором, необходимо освоить составление скриптов sed. Программа sed имеет собственный несложный язык программирования, позволяющий составлять скрипты, способные творить чудеса.

Эта статья не может вместить описания скриптов sed, как и ее автор не ставит себе задачу освоение языка программирования sed. В этой статье я делал акцент на использование редактора sed в командной строке, имея прицел на использование его в качестве фильтра в программных каналах (pipes). По этой причине я опустил многочисленные команды sed, применяющиеся только в его скриптах.

Существует множество любителей редактора sed, и множество статей на тему скриптописания, в том числе и в Рунете. Так что для заинтересовавшихся этой замечательной программой не составит труда пополнить свои знания.

Программа sed и символы кириллицы

Как видно из примеров в этой статье, программа sed на правильно русифицированной системе свободно владеет «великим и могучим» языком.

Использование кавычек в командной строке

Теперь, когда мы увидели, сколько много способов у оболочки для выполнения раскрытий, настало время научиться, как мы можем контролировать это. Например:

echo это просто      тест
это просто тест

или:

echo Общая сумма равна $100.00
Общая сумма равна 00.00

В первом примере разбитие слов оболочкой удалила дополнительные белые пробелы из списка аргументов команды echo. Во втором примере раскрытие параметра подставило пустую строку для значения «$1» поскольку это была неопределённая величина. Для подавления ненужных раскрытий в командах оболочки используются кавычки.

Двойные кавычки

Первым типом кавычек, которые мы начнём рассматривать, являются двойные кавычки. Если мы разместите текст внутри двойных кавычках, все специальные символы, используемые оболочкой, потеряют своё специальное значение и будут обрабатываться как обычные символы. Исключением являются «$», «\» (обратный слеш) и «`» (обратная кавычка). Это означает, что подавляются разбитие слов, раскрытие пути к файлу, раскрытие тильды и раскрытие фигурных скобок, но раскрытие параметра, арифметическое раскрытие и подстановка команд всё ещё выполняются. Используя двойные кавычки, мы можем совладать с именами файлов, содержащими включённые пробелы. Допустим, мы стали несчастной жертвой файла под названием два слова.txt. Если мы пытались использовать его в командной строке, разбитие слов приводило к тому, что это имя обрабатывалось как два раздельных аргумента, а не как желаемый единый аргумент:

ls два слова.txt
ls: невозможно получить доступ к 'два': Нет такого файла или каталога
ls: невозможно получить доступ к 'слова.txt': Нет такого файла или каталога

Используя двойные кавычки, мы останавливаем разбитие слов и получаем желаемый результат; более того, мы даже можем восстановить повреждение:

ls -l "два слова.txt"
-rw-rw-r-- 1 mial mial 1 июл 29 06:57 два слова.txt
mv "два слова.txt" два_слова.txt

Вот оно! Теперь нам даже не нужно печатать эти надоедливые двойные кавычки.

Помните, раскрытие параметров, арифметические выражения и подстановка команд всё равно имеют место внутри двойных кавычек:

echo "$USER $((2+2)) $(cal)"

Нам следует затратить немного времени, чтобы взглянуть на эффект двойных кавычек на подстановку команд. Начнём с того, что заглянем глубже, как работает разделение слов. В одном из наших предыдущих примерах мы видели, как разбитие слов проявляется в удалении дополнительных пробелов в нашем тексте:

echo это просто      тест
это просто тест

По умолчанию, разбитие слов ищет присутствие пробелов, табуляций, символов новой строки и разрыва страницы и трактует их как разделители между словами. Это означает, что без кавычек пробелы, табуляции, символы новой строки не расцениваются как часть текста. Они служат только разделителями. Поскольку они разделяют слова на различные аргументы, строка команды в нашем примере содержит команду, за которой следует четыре раздельных аргумента. Если мы добавим двойные кавычки:

echo "это просто      тест"
это просто      тест

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

Тот факт, что символы новой строки механизмом разбития слов рассматриваются как разделители, приводит к интересному, хотя и тонкому эффекту на подстановку команд. Рассмотрим следующее:

echo $(cal)
Июль 2017 Вс Пн Вт Ср Чт Пт Сб 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
echo "$(cal)"
     Июль 2017        
Вс Пн Вт Ср Чт Пт Сб  
                   1  
 2  3  4  5  6  7  8  
 9 10 11 12 13 14 15  
16 17 18 19 20 21 22  
23 24 25 26 27 28 29  
30 31           

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

Одинарные кавычки

Если нам нужно подавить все раскрытия, мы используем одинарные кавычки. Далее сравниваются отсутствие кавычек, двойные кавычки и одинарные кавычки:

echo text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER
text /home/mial/два_слова.txt a b foo 4 mial
echo "text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER"
text ~/*.txt {a,b} foo 4 mial
echo 'text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER'
text ~/*.txt {a,b} $(echo foo) $((2+2)) $USER

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

Напечатать Usage и Прервать Скрипт при Незаданных Аргументах

Используйте следующую проверку в shell скриптах, чтобы :

1. проверить количество входящих аргументов

2. вывести сообщение об ошибке, если количество входящих аргументов некорректно

3. прервать shell скрипт со статусом ошибки

&& { echo «Usage: $0 argument»; exit 1; }

Параметр Описание
$# переменная, содержащая количество аргументов, преданных скрипту
-eq 0 проверяем, равняется ли количество переданных аргументов нулю
$0 возвращает путь к shell скрипту

Пример Bash Скрипта

Следующий скрипт использует команду dig для поиска DNS-сервера доменного имени. Проверяемый домен должен передаваться скрипту в качестве аргумента.

#!/bin/bash
domain=$1
 $# -eq 0 ] && { echo "Usage: $0 domain_name"; exit 1; }
dig NS $domain @8.8.8.8 +short

Пример выполнения скрипта, если аргумент не указан :

$ ./find_ns.shUsage: ./find_ns.sh domain_name

Пример работы скрипта при заданном аргументе :
$ ./find_ns.sh shellhacks.com
ns2.ukraine.com.ua.
ns3.ukraine.com.ua.
ns1.ukraine.com.ua.

Примеры использования sed

Теперь рассмотрим примеры sed Linux, чтобы у вас сложилась целостная картина об этой утилите. Давайте сначала выведем из файла строки с пятой по десятую. Для этого воспользуемся командой -p. Мы используем опцию -n чтобы не выводить содержимое буфера шаблона на каждой итерации, а выводим только то, что нам надо. Если команда одна, то опцию -e можно опустить и писать без неё:

Или можно вывести весь файл, кроме строк с первой по двадцатую:

Здесь наоборот, опцию -n не указываем, чтобы выводилось всё, а с помощью команды d очищаем ненужное. Дальше рассмотрим замену в sed. Это самая частая функция, которая применяется вместе с этой утилитой. Заменим вхождения слова root на losst в том же файле и выведем всё в стандартный вывод:

Флаг g заменяет все вхождения, также можно использовать флаг i, чтобы сделать регулярное выражение sed не зависимым от регистра. Для команд можно задавать адреса. Например, давайте выполним замену 0 на 1000, но только в строках с первой по десятую:

Переходим ещё ближе к регулярным выражениям, удалим все пустые строки или строки с комментариями из конфига Apache:

Под это регулярное выражение (адрес) подпадают все строки, которые начинаются с #, пустые, или начинаются с пробела, а за ним идет решетка. Регулярные выражения можно использовать и при замене. Например, заменим все вхождения p в начале строки на losst_p:

Если вам надо записать результат замены в обратно в файл можно использовать стандартный оператор перенаправления вывода > или утилиту tee. Например:

Также можно использовать опцию -i, тогда утилита не будет выполнять изменения в переданном ей файле:

Если надо сохранить оригинальный файл, достаточно передать опции -i в параметре расширение для файла резервной копии.

5 ответов

27

Ваша оболочка интерпретирует кавычки, как , так и , прежде чем они дойдут до . Обычно я просто добавляю двойные кавычки вокруг моего аргумента, чтобы откликнуться, даже если они не нужны; например:

Итак, в первом примере, если вы хотите включить литеральные метки кавычек в свой вывод, они либо должны быть экранированы:

Или они должны использоваться в уже цитированном аргументе (но это не может быть однотипная цитата, или вам все равно нужно ее избежать):

Во втором примере вы выполняете подстановку команд в середине строки:

Вещи, которые начинаются с , также обрабатываются специально оболочкой — она ​​рассматривает их как переменные и заменяет их своими значениями. Поскольку, скорее всего, ни одна из этих переменных не задана в вашей оболочке, на самом деле она просто запускает

Так как видит только один аргумент, он предполагает, что аргумент — это шаблон, который вы ищете, и что место, которое оно должно читать данные из stdin, поэтому он блокирует ожидание ввода. Вот почему ваша вторая команда просто зависает.

Это не произойдет, если вы укажете один аргумент (вот почему ваш первый пример почти сработал), так что это один из способов получить нужный результат:

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

6

Я не буду вдаваться в подробности о том, почему ваши попытки ведут себя так, как они делают, потому что охватывает это хорошо. В двух словах все между одинарными кавычками () интерпретируется буквально (и, в частности, первый обозначает конец строки), а и сохраняют свое особое значение между .

В одинарных кавычках нет кавычек, поэтому вы не можете поставить одну цитату внутри строки с одной кавычкой. Однако есть идиома, которая выглядит так:

Это печатает , потому что аргумент состоит из однокасканной трехсимвольной строки , объединенной с единственным символом (полученный путем защиты символа от его специального значения с помощью предыдущего ), объединенный одноколейной трехсимвольной строкой . Так что, хотя это не совсем то, что происходит за кулисами, вы можете думать о как способ включить одну цитату внутри однокасканного строка.

Если вы хотите печатать сложные многострочные строки, лучшим инструментом является здесь документ . Документ здесь состоит из двух символов , за которым следует маркер, например , затем некоторые строки текста, а затем маркер конца на своей линии. Если маркер указан каким-либо образом ( или или или «E» «OF» или …), текст интерпретируется буквально (например, внутри одиночных кавычек, за исключением того, что даже является обычным символом). Если маркер вообще не цитируется, текст интерпретируется как строка с двойными кавычками, при этом сохраняя свой особый статус ( но и символы новой строки интерпретируются буквально).

2

Хорошо, это сработает: —

Тестирование здесь: —

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

Конечно, этот пример немного надуман, но я нашел его полезным методом, например, для отправки операторов SQL в (вместо ).

(Обратите внимание, что любой ненулевой символ без пробела можно использовать вместо в общей конструкции цитирования выше, но хэш, похоже, отлично подходит для этого примера и рассматривается как not как комментарий.)

-1

Возьмем вашу команду

и сначала разделите его на слова, используя пробелы без кавычек в качестве разделителя:

Далее, мы делаем расширение параметра: expand , но не внутри . Поскольку пуст (если вы не установите его, например, ), он будет заменен пустой строкой.

Далее, удалите цитату.

Затем эти аргументы передаются в echo, которые будут печатать их один за другим, разделенные одним пробелом.

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

Это закончит строку с одним кавычком, а затем добавит (сбежавшую) одинарную кавычку к слову, а затем запустит новую строку с одним кавычком, без разрыва слова.

Выполнение наборов команд при вызове sed

Для выполнения нескольких действий с данными, используйте ключ 
-e при вызове sed. Например, вот как организовать замену двух фрагментов текста:

$ sed -e ‘s/This/That/; s/test/another test/’ ./myfile

1 $sed-e’s/This/That/; s/test/another test/’.myfile

Использование ключа -e при вызове sed

К каждой строке текста из файла применяются обе команды. Их нужно разделить точкой с запятой, при этом между окончанием команды и точкой с запятой не должно быть пробела.Для ввода нескольких шаблонов обработки текста при вызове sed, можно, после ввода первой одиночной кавычки, нажать Enter, после чего вводить каждое правило с новой строки, не забыв о закрывающей кавычке:

$ sed -e ‘
> s/This/That/
> s/test/another test/’ ./myfile

1
2
3

$sed-e’

> s/This/That/

> s/test/another test/’.myfile

Вот что получится после того, как команда, представленная в таком виде, будет выполнена.

Другой способ работы с sed

Пример

Представим себе такую задачу. Есть файл, в котором имеется некая последовательность символов, сама по себе бессмысленная, которую надо заменить на данные, взятые из другого файла. А именно, пусть это будет файл 
newfile, в котором роль указателя места заполнения играет последовательность символов 
DATA. Данные, которые нужно подставить вместо 
DATA, хранятся в файле 
data.

Решить эту задачу можно, воспользовавшись командами 
r и 
d потокового редактора sed:

$ Sed ‘/DATA>/ {
r newfile
d}’ myfile

1
2
3

$Sed’/DATA>/ {

r newfile

d}’myfile

Замена указателя места заполнения на реальные данные

Как видите, вместо заполнителя 
DATA sed добавил в выходной поток две строки из файла 
data.

Вопросы и ответы по awk

Как вывести только строку определённого номера в awk

Чтобы вывести строки с определённым номером, используйте if() и переменную NR.

Например, чтобы вывести только вторую строку:

free | awk '{ if (NR == 2) print $7 }'

Чтобы вывести вторую и все последующие строки:

free | awk '{ if (NR >= 2) print $7 }'

Чтобы вывести все строки с 10 по 20:

awk '{ if (NR >= 10 && NR <= 20) print }' /etc/passwd

Как перенаправить вывод в файл в awk

Команду print можно использовать с перенаправлением вывода в файл.

К примеру, следующая команда сохранит строки с 10 по 20 из файла /etc/passwd в файл pswd.txt:

awk '{ if (NR >= 10 && NR <= 20) print>"pswd.txt" }' /etc/passwd

Следующая команда ищет в строке слово «mial» и если оно там встречается, то сохраняет всю строку в файл pswd.txt:

awk '/mial/{ print>"pswd.txt" }' /etc/passwd

Как использовать переменные в awk

Следующая команда посчитает количество строк содержащих слово «bash» в файле /etc/passwd, выведет каждую из этих строк и затем выведет общее количество найденных строк:

awk -v y=0 '/bash/{ y++; print $0 } END { print "Всего найдено строк с bash: " y }' /etc/passwd

При запуске программы инициируется переменная y со значением 0. При каждом совпадении (найдена строка «bash»), значение y увеличивается на единицу и выводится найденная строка. В конце выводится значение y.

Как вывести скобки и другие специальные в awk

Скобки и другие специальные символы необходимо помещать в двойные кавычки.

awk -F ',' '{ print "(" $1, $2 ")" }'

В предыдущих примерах имя файла также помещено в двойные кавычки из-за содержащейся в нём точки, которая является специальным символом.

Часть вторая: Что может делать AWK?

Главная цель в жизни AWK – это манипулировать её вводом на построчной основе. Программа awk обычно работает в стиле

Обработать строку. Двигаться дальше
Обработать строку. Двигаться дальше
Обработать строку…

Если то, что вы хотите сделать, не вписывается в эту модель, значит awk, может быть, не подходит к вашей задумке.

Обычный используемый в программировании awk синтаксис можно описать так:

awk образец {команда(ы)}

Это означает, что

«Посмотреть на каждую строку ввода, нет ли там ОБРАЗЦА. Если он там есть, запустить то, что между {}»

Можно пропустить или ОБРАЗЕЦ или КОМАНДУ

Если не указать образец, то команда будет применяться к КАЖДОЙ строке.

Если пропущена команда, то это эквивалентно указанию (просто напечатать строку):

{ print }

Конкретные примеры:

awk '/#/ {print "В этой строке есть комментарий"}' /etc/hosts

будет печатать «В этой строке есть комментарий» для каждой строки, которая содержит хотя бы один ‘#’ в любом месте строки в /etc/hosts

Модификация для наглядности

awk '/#/ {print $0 ":\tВ этой строке есть комментарий"}' /etc/hosts

Элемент ‘//’ в образце – это один из способов задать совпадение. Есть также другие способы задать, совпадает ли строка. Например,

awk '$1 == "#" {print "строка начинается с хеша"}' /etc/hosts

будет соответствовать строкам, первый столбец в которых является единичным ‘#’. Последовательность символов ‘==’ означает ТОЧНОЕ СОВПАДЕНИЕ ВСЕГО первого столбца.

Модификация для наглядности:

awk '$1 == "#" {print $0 "\tстрока начинается с хеша"}' /etc/hosts

С другой стороны, если вы хотите частичное совпадение конкретного столбца, используйте оператор ‘~’

awk '$1 ~ /#/ {print "ГДЕ-ТО в столбце 1 есть хеш"}' /etc/hosts

ПОМНИТЕ, ЧТО ПЕРВЫЙ СТОЛБЕЦ МОЖЕТ БЫТЬ ПОСЛЕ БЕЛОГО ПРОБЕЛА.

Модификация для наглядности:

awk '$1 ~ /#/ {print $0 "\tГДЕ-ТО в столбце 1 есть хеш"}' /etc/hosts

Ввод «# comment» будет соответствовать

Ввод » # comment» будет ТАКЖЕ соответствовать

Если вам нужно конкретное совпадение «строка, которая начинается точно с # и пробела», вы должны использовать

awk '/^# / {делай что-то}'

Множественное совпадение

Awk обработает ВСЕ ОБРАЗЦЫ, которые соответствуют текущей строке. Поэтому если использовать следующий пример,

  awk '
     /#/ {print "Есть комментарий"}
     $1 == "#" {print "Комментарий в первом столбце"}
     /^# /  {print "Комментарий в самом начале"}
   ' /etc/hosts

ТРИ записи будет выведено для строки вроде следующей:

# This is a comment

ДВЕ записи для

  # This is an indented comment

и только одна для

1.2.3.4 hostname # a final comment

Отслеживание контекста

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

Здесь быстрый пример, который печатает строки «ADDR» если вы не в секции «secret»

   awk '

   /secretstart/  	{ secret=1}
   /ADDR/		{ if(secret==0) print $0 } /* $0 – это полная строка */
   /secretend/		{ secret=0} '

Следующее напечатает содержимое, которое содержит внутри «ADDR» кроме случаев, если была увидена строка «secretstart». ПОРЯДОК ИМЕЕТ ЗНАЧЕНИЕ. Например, если записать так:

   awk '

   /ADDR/		{ if(secret==0) print $0 } /* $0 – это полная строка */
   /secretstart/  	{ secret=1}

   /secretend/		{ secret=0} '

и дать следующий ввод

ADDR a normal addr
secretstart ADDR a secret addr
ADDR another secret addr
a third secret ADDR
secretend
ADDR normal too

то будет напечатан первый «secret» addr. При том, что первоначальный пример скроет оба секрета.

Экранирование символов в sed

Специальные символы экранируются с помощью \

Что включать в специальные символы зависит от того, какой
sed вы используете, но $.*[\^ а также пробелы и кавычки
советую экранировать всегда.

Пробел также можно заменять на \s
. в регулярных выражениях обозначает один любой символ кроме начала новой строки \n
поэтому, если вы хотите написать url используйте \

Пример экранирования точек и кавычек для смены локали в

CentOS

можете изучить

Предположим, что есть файл

input.txt

следующего содержания

Мы хотим отбросить всё, что находится левее /a, включая /a, и записать в файл.

sed ‘s/^.*/a//’ > output.txt

В результате получим ошибку

-e expression #1, char 15: unknown option to `s’

Чтобы команда заработала нужно добавить \ перед /

sed ‘s/^.*\/a//’ > output.txt

Результат:

Экранирование пробелов может пригодиться при замене одной фразы на другую

Чтобы в скрипте

sites.sh

из директории
/opt/andrei/scripts/ заменить фразу
Bike website topbicycle.ru
на
Travel website heihei.ru
нужно выполнить

sed -i s/Bike\ website\ topbicycle.ru/Travel\ website\ heihei.ru/ /opt/andrei/scripts/sites.sh

Два условия одновременно в Sed

Предположим, что у нас есть файл

input.txt

следующего содержания

Мы хотим отбросить всё, что находится левее /b, включая /b, и всё, что правее
has.

Таким образом, в каждой строчке должно остаться только слово it.

Нужно учесть необходимость экранирования специального символа / а также мы хотим
направить вывод в файл.

sed ‘s/^.*\/b//
;
s/has.*//’ input.txt > output.txt

Результат:

Получить диапазон строк

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

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

Например, Вам нужны строки с 9570 по 9721

sed -n ‘9570,9721p;9722q’ project-2019-10-03.log > bugFound.txt

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Все про сервера
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: