Заменить несколько строк, идентифицирующих конечный символ
У меня есть код ниже
CREATE TABLE Table1(
column1 double NOT NULL,
column2 varchar(60) NULL,
column3 varchar(60) NULL,
column4 double NOT NULL,
CONSTRAINT Index1 PRIMARY KEY CLUSTERED
(
column2 ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON PRIMARY
) ON PRIMARY
GO
GO
и я хочу заменить
CONSTRAINT Index1 PRIMARY KEY CLUSTERED
(
column2 ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON PRIMARY
) ON PRIMARY
GO
с
)
Вы не можете предположить, что GO является последним символом файла. После Go может быть другая таблица script.
Как я могу сделать это с помощью одного sed или awk.
Ответы
Ответ 1
Update:
Вы можете использовать следующую команду sed
для замены даже последнего ,
перед блоком CONSTRAINT
:
sed -r '/,/{N;/CONSTRAINT/{:a;N;/GO/!ba;s/([^,]+).*/\1\n)/};/CONSTRAINT/!n}' input.sql
Позвольте мне объяснить это как многострочный script:
# Search for a comma
/,/ {
# If a command was found slurp in the next line
# and append it to the current line in pattern buffer
N
# If the pattern buffer does not contain the word CONSTRAINT
# print the pattern buffer and go on with the next line of input
# meaning start searching for a comma
/CONSTRAINT/! n
# If the pattern CONSTRAINT was found we loop until we find the
# word GO
/CONSTRAINT/ {
# Define a start label for the loop
:a
# Append the next line of input to the pattern buffer
N
# If GO is still not found in the pattern buffern
# step to the start label of the loop
/GO/! ba
# The loop was exited meaning the pattern GO was found.
# We keep the first line of the pattern buffer - without
# the comma at the end and replace everything else by a )
s/([^,]+).*/\1\n)/
}
}
Вы можете сохранить указанный выше многострочный файл script в файле и выполнить его с помощью
sed -rf script.sed input.sql
Вы можете использовать следующую команду sed
:
sed '/CONSTRAINT/{:a;N;/GO/!ba;s/.*/)/}' input.sql
Образец ищет строку, содержащую /CONSTRAINT/
. Если шаблон найден, блок команд запускается между { }
. В блоке мы сначала определяем метку a
через :a
. Мы получаем следующую строку ввода через N
и добавляем ее в буфер шаблонов. Если мы не найдем шаблон /GO/!
, мы продолжим работу с меткой a
с помощью команды branch b
. Если найден шаблон /GO/
, мы просто заменим буфер на )
.
Альтернатива может использоваться с использованием диапазона, например, предложенного FredPhil:
sed '/CONSTRAINT/,/GO/{s/GO/)/;te;d;:e}'
Ответ 2
С GNU awk для multi- char RS и предполагая, что вы хотите избавиться от запятой перед "CONSTRAINT":
$ cat tst.awk
BEGIN{ RS="^$"; ORS="" }
{
gsub(/\<GO\>/,"\034")
gsub(/,\s*CONSTRAINT[^\034]+\034/,")")
gsub(/\034/,"GO")
print
}
$ gawk -f tst.awk file
CREATE TABLE Table1(
column1 double NOT NULL,
column2 varchar(60) NULL,
column3 varchar(60) NULL,
column4 double NOT NULL)
GO
Вышеупомянутое работает путем замены каждого автономного "GO" с помощью элемента управления char, который вряд ли появится на вашем входе (в этом случае я использовал то же значение, что и SUBSEP по умолчанию), поэтому мы можем использовать этот char в списке отрицательных символов в середине gsub() для создания регулярного выражения, которое заканчивается первым "GO" после "CONSTRAINT". Это один из способов сделать "неживое" соответствие в awk.
Если не существует char, который вы не знаете на своем входе, вы можете создать его следующим образом:
$ cat tst.awk
BEGIN{ RS="^$"; ORS="" }
{
gsub(/a/,"aA"); gsub(/b/,"aB"); gsub(/\<GO\>/,"b")
gsub(/,\s*CONSTRAINT[^b]+b/,")")
gsub(/b/,"GO"); gsub(/aB/,"b"); gsub(/aA/,"a")
print
}
$
$ gawk -f tst.awk file
CREATE TABLE Table1(
column1 double NOT NULL,
column2 varchar(60) NULL,
column3 varchar(60) NULL,
column4 double NOT NULL)
GO
Вышеизложенное изначально преобразует все "a" в "aA" и "b" в "aB", так что
- в записи больше нет "b" , а
- так как все оригинальные "а" теперь имеют после них "А", единственные вхождения
"aB" представляют, где "bs" изначально были расположены
а это значит, что теперь мы можем преобразовать все "GO" в "b" так же, как мы перевели их в "\ 034" в первом script выше. Затем мы делаем главный gsub(), а затем разворачиваем наш начальный gsub() s.
Эта идея gsub()
ing создавать символы, которые ранее не существовали, используя эти символы, а затем развертывание начального gsub()
- чрезвычайно полезная идиома, чтобы учиться и запоминать, например. см. fooobar.com/questions/8472/... для другого приложения.
Чтобы увидеть, как он работает шаг за шагом:
$ cat file
foo bar Hello World World able bodies
$ awk '{gsub(/a/,"aA")}1' file
foo baAr Hello World World aAble bodies
$ awk '{gsub(/a/,"aA"); gsub(/b/,"aB")}1' file
foo aBaAr Hello World World aAaBle aBodies
$ awk '{gsub(/a/,"aA"); gsub(/b/,"aB"); gsub(/World/,"b")}1' file
foo aBaAr Hello b b aAaBle aBodies
$ awk '{gsub(/a/,"aA"); gsub(/b/,"aB"); gsub(/World/,"b"); gsub(/Hello[^b]+b/,"We Are The")}1' file
foo aBaAr We Are The b aAaBle aBodies
$ awk '{gsub(/a/,"aA"); gsub(/b/,"aB"); gsub(/World/,"b"); gsub(/Hello[^b]+b/,"We Are The"); gsub(/b/,"World")}1' file
foo aBaAr We Are The World aAaBle aBodies
$ awk '{gsub(/a/,"aA"); gsub(/b/,"aB"); gsub(/World/,"b"); gsub(/Hello[^b]+b/,"We Are The"); gsub(/b/,"World"); gsub(/aB/,"b")}1' file
foo baAr We Are The World aAble bodies
$ awk '{gsub(/a/,"aA"); gsub(/b/,"aB"); gsub(/World/,"b"); gsub(/Hello[^b]+b/,"We Are The"); gsub(/b/,"World"); gsub(/aB/,"b"); ; gsub(/aA/,"a")}1' file
foo bar We Are The World able bodies
Ответ 3
Это может выглядеть страшно, но нетрудно понять немного:
SED_DELIM=$(echo -en "\001")
START=' CONSTRAINT Index1 PRIMARY KEY CLUSTERED'
END='GO'
sed -n $'\x5c'"${SED_DELIM}${START}${SED_DELIM},"$'\x5c'"${SED_DELIM}${END}${SED_DELIM}{s${SED_DELIM}GO${SED_DELIM})${SED_DELIM};t a;d;:a;};p" test2.txt
У sed есть следующая форма, с которой вы можете быть знакомы:
sed /regex1/,/regex2/{commands}
Сначала он использует SOH без печати в качестве разделителя \001
Устанавливает теги START и END для sed multiline match
Затем выполняет команду sed:
-n
не печатать по умолчанию
$'\x5c'
- это строковый литерал Bash, который соответствует обратному косую черту \
Обратные косые черты необходимы, чтобы избежать непечатаемого разделителя в многострочном диапазоне соответствия.
{s${SED_DELIM}GO${SED_DELIM})${SED_DELIM};t a;d;:a;};p
:
s${SED_DELIM}GO${SED_DELIM})${SED_DELIM}
замените строку, соответствующую GO, с помощью )
t a;
, если в предыдущем утверждении есть успешная замена, а затем перейдите к метке :a
d
, если нет подстановки, тогда удалите строку p
распечатать любой результат после команд
перейдите к
Я не видел их ответов до публикации этого вопроса - этот ответ такой же, как у FredPhil/hek2mgl - за исключением того, что у вас есть механизм, который будет более динамичным на LHS, поскольку вы можете изменить разделитель на символ, который гораздо меньше шансов появиться в наборе данных.