Это прекрасный пример того, почему sed
является s tream ed itor, и никогда не заменит возможности ex
для редактирования файлов на месте.
ex -c '/address *172.29.16.103/
?{?,/}/d
x' input
Эта команда представляет собой упрощенную форму, которая не так надежна, как могла бы быть, но служит для иллюстрации.
Первая команда находит указанное регулярное выражение и перемещает курсор на эту строку.
Вторая команда состоит из двух адресов, разделенных запятой, по которым выполняется команда d
elete. ? {?
выполняет поиск в обратном направлении от текущей строки для открытой фигурной скобки, а /} /
выполняет поиск вперед от текущей строки для закрытой фигурной скобки. Все, что находится между ними, удаляется (линейно, поэтому также удаляется начало открытой фигурной скобки).
x
сохраняет изменения и выходит. И input
, конечно же, имя файла.
Для введенных вами данных эта команда работает точно так, как вы хотите.
Я упомянул, что это можно значительно улучшить. Начнем с регулярного выражения. Самая очевидная особенность здесь в том, что точки - это символы подстановки; данное регулярное выражение также может соответствовать «172329-16 103». Таким образом, точки должны быть экранированы обратной косой чертой, чтобы они соответствовали только буквальным точкам.
Далее следует пробел. Я поставил два пробела, за которыми следует * (я мог бы использовать \ +
, но я не знаю, требуется ли эта функция в POSIX), но что, если в файле есть вкладки? Лучшее решение - использовать [[: space:]]
.(Это выглядело бы намного лучше с \ +
; если кто-нибудь обнаружит, что это POSIX, напишите комментарий.)
Наконец, что, если регулярное выражение не найдено в файле ? Что ж, тогда файл просто откроется для редактирования, и команда "search" завершится неудачно, и будет напечатано сообщение об ошибке, а остальные данные команды не будут выполнены - вы останетесь в ex
редактор, чтобы вы могли вносить изменения вручную. Однако, если вы хотите автоматизировать редактирование в сценарии, вы, вероятно, захотите, чтобы редактор выходил из , если не нужно вносить никаких изменений. Ответ - использовать команду lobal g
, а также использовать флаг -s
для подавления любого вывода из ex
:
ex -sc 'g/address[[:space:]][[:space:]]*172\.29\.16\.103/ ?{?,/}/d
x' input
Это не вполне эквивалент предыдущей команды; если имеется более одного блока фигурных скобок с совпадающей линией, глобальная команда удалит их все. В любом случае, вероятно, вы этого хотите.
Если вы хотите удалить только первое совпадение при выходе без изменения файла, если совпадений нет вообще, вы можете использовать команду x
как часть аргумента g
(для выхода из файла после выполнения первой команды удаления) и добавить q!
внизу на случай, если команда g
не выполняется из-за отсутствия совпадающих строк.
ex -sc 'g/address[[:space:]][[:space:]]*172\.29\.16\.103/ ?{?,/}/d | x
q!' input
Если честно, эти команды делают процесс намного сложнее, чем он есть на самом деле; надежность достигается за счет предельной ясности и удобочитаемости кода. Это компромисс.
Я рекомендую отредактировать несколько файлов в интерактивном режиме с помощью ex
, чтобы немного почувствовать это. Таким образом вы сможете увидеть , что вы делаете. Такой сеанс редактирования в ex
для интерактивного выполнения этого исправления выглядит примерно так:
$ ex input
"input" 23L, 843C
Entering Ex mode. Type "visual" to go to Normal mode.
:/103
host_name ServerB_172.29.16.103
:?{?,/}/d # This deletes the current block
:$p # Print and move to last line
:-5,.p # Print some more lines to check result
notification_interval 120
notification_period 24x7
}
:?}?+,.d # Trim whitespace
}
:x # Save and exit
$
Спецификации POSIX для ex
содержат дополнительную информацию.
[ "$OPTARG" -eq "$OPTARG" ]...
не является правильным способом проверить, является ли $OPTARG
числом --он может вывести пользователю неприятную непостижимую ошибку, если это не так, или он может просто вернуть true во всех случаях (вksh
)или также вернуть true для пустого$OPTARG
(вzsh
).
Кроме того, вариант, принимающий аргумент, может быть задан как -d12
или -d 12
, так что вслепую shift 2
не подойдет. И выполнение shift
внутри цикла может плохо взаимодействовать с getopts
, который сам использует список активных аргументов.
Принимая это во внимание, вот что я предлагаю:
die(){ echo >&2 "$@"; exit 1; }
usage(){ echo >&2 "usage: $0 [-h] [-d num] files..."; exit 0; }
depth=0
while getopts :hd: opt; do
case $opt in
h) usage ;;
d) case $OPTARG in
''|*[!-0-9]*|-|*?-*) die "invalid number $OPTARG" ;;
*) depth=$OPTARG ;;
esac
;;
:) die "argument needed to -$OPTARG" ;;
*) die "invalid switch -$OPTARG" ;;
esac
done
shift "$((OPTIND - 1))"
echo depth="$depth"
echo files="$@"
Одинарная квадратная скобка [
совместима с posix -, но ее сложнее использовать, чем [[
, которую обычно можно использовать в bash. Согласно стандарту одинарной -квадратной скобки -, -eq
предназначен только для целых чисел, а не для строк. Например, следующее работает нормально:if [ "2" -eq "2" ] ; then echo foo; fi
. Вы можете использовать либо двойные -квадратные -скобки, для которых -eq
принимает строки, либо использовать =
и форму одинарной -квадратной скобки:
if [ "bar" = "bar" ] ; then echo foo; fi
См.https://ryanstutorials.net/bash-scripting-tutorial/bash-if-statements.phpдля получения информации о тесте POSIX if иhttp://tldp.org/LDP/abs/html/testconstructs.htmlдля получения информации о различных типах тестовых конструкций, таких как ((
и [[
.