Кажется, вы оставили на один $
слишком много. Замените $whole
на :
final=${whole#$prefix};
У вас уже есть ответы, но я хочу добавить, что пошло не так в вашем собственном подходе, чтобы вы могли учиться на нем, а не просто копировать какое-то решение:
-E
для sed
. ()
()
с буквальными. Вы, вероятно, имели в видуsed -E 's/^ ([a-z]+)[ ]+NVARCHAR\(([0-9]+)\) NOT NULL/TEXT NOT NULL CHECK \(LENGTH\((\1) <= (\2)\)/g'
sed -E 's/^( ([a-z]+)[ ]+)NVARCHAR\(([0-9]+)\) NOT NULL/\1TEXT NOT NULL CHECK \(LENGTH\((\2) <= (\3)\)/g'
[ ]+
совпадает с +
. Не ошибка, но делает чтение более запутанным. g
является лишней. С привязкой типа ^
или $
в шаблоне множественные замены невозможны.NOT
необязательным:`sed -E 's/^ (([a -z]+ )+ )NVARCHAR (([0 -9]+))(НЕ )?NULL/\1TEXT \4NULL CHECK (LENGTH ((\2 )<= (\3 ))/' s/^( [a-z]+ +)NVARCHAR\(([0-9]+)\) NULL/\1TEXT NULL/
s/N\'\'/g
отсутствует разделитель между шаблоном поиска и заменой:s/N\'/\'/g
Таким образом, вы получите
sed -E 's/^( ([a-z]+) +)NVARCHAR\(([0-9]+)\) NOT NULL/\1TEXT NOT NULL CHECK \(LENGTH\((\2) <= (\3)\)/
s/^( [a-z]+ +)NVARCHAR\(([0-9]+)\) NULL/\1TEXT NULL/
s/N\'/\'/g'
Поскольку вы используете fedora
, у вас есть GNU sed
, и это должно работать:
s=" shipname NVARCHAR(40) NOT NULL,"
echo "$s" | sed -E '/NOT/{s/^ ([[:lower:]]+)\s*NVARCHAR\(([[:digit:]]+)\) NOT NULL,$/\1 TEXT NOT NULL CHECK \(LENGTH\(\1\) <= \2\),/;q0} ; s/^ ([[:lower:]]+)/\1 TEXT NULL,/'
Это эмулирует поддельное if.
if
:
aNOT
(/NOT/
)находится внутри структуры db, затем выполняется первая команда sed, затем выходим(q0
)без выполнения второго оператора.
else
:
ключевое слово NOT
не найдено, и выполняется второй экземпляр.
Для вторых требований:
sed "s/N'/'/g"
Глобальный поиск N'
и замена только на '
. Я нашел полезным поменять местами '
с "
для разделителя командной строки sed
и сделать его более чистым без большого количества экранирования.
Поместить первый sed
внутрь файла:
#!/bin/sed -Ef
# If a NOT is found execute this:
# capture the column name and the value of this
/NOT/ {
s/^ ([[:lower:]]+)\s*NVARCHAR\(([[:digit:]]+)\) NOT NULL,$/\1 TEXT NOT NULL CHECK \(LENGTH\(\1\) <= \2\),/
# Quit without execute the other statement
q0
}
# Else: If we are here then the database
# structure does not contains a length for the column;
# so it should be NULL
s/^ ([[:lower:]]+)/\1 TEXT NULL,/
Команда {
используется для группировки большего количества команд sed
.
q
— это команда quit
, она используется для выхода sed
. Здесь я использовал его для принудительного выхода sed
до того, как встретится с последней строкой, если первый тест прошел успешно.
sed
отлично подходит для некоторых задач, но для некоторых других задач требовался -полнофункциональный язык, например awk
или perl
, с условными выражениями, printf и т. д. И желательно язык, который не читается как какой-то отвратительный гибрид регулярного выражения и калькулятора RPN :-).
#!/usr/bin/perl
use strict;
while(<>) {
# print verbatim any lines that don't define an identifier
unless (m/^\s+\S/) { print; next };
# print a blank line before certain identifiers
print "\n" if m/birthdate|address|phone/;
# various regex transformations for IDENTITY and VARCHAR fields
s/\s+NOT NULL IDENTITY/ GENERATED BY DEFAULT AS IDENTITY/;
s/([[:lower:]]+)\s+NVARCHAR\((\d+)\) NOT NULL/$1 TEXT NOT NULL CHECK (LENGTH($1) <= $2)/;
s/\s+NVARCHAR\((\d+)\)\s+NULL/ TEXT NULL/;
# remove length checks from NULL definitions
s/\s+CHECK.*/,/ if /(?<!NOT) NULL/;
# add a comma at the end of the mgrid line if it's not there
s/\s*$/,/ if /mgrid/ && ! /,\s*$/;
# hacky crap to nicely format "TYPE (NOT )?NULL" output.
my @F = split;
my $identifier = shift @F;
my $type = shift @F;
$type.= " ". shift @F if ($F[0] =~ /NOT/);
$type = sprintf "%-8s", $type;
$type.= " ". shift @F if ($F[0] =~ /NULL/);
printf " %-15s %-13s%s\n", $identifier, $type, join(" ",'',@F);
# print the test_field definition after mgrid
if ($identifier eq 'mgrid') {
print " test_field TEXT NULL CHECK (LENGTH(test_field) <= 25)\n";
};
}
это довольно грубый -метод преобразования вашего ввода в (примерно )желаемый результат. несколько преобразований регулярных выражений и некоторый код для красивого выравнивания «полей». и несколько дополнительных операторов печати для добавления пустых строк и поля теста _в соответствующих местах. Таким образом, это не очень полезно, но при необходимости может быть адаптировано для других преобразований SQL.
сценарий реализует описание в вашем вопросе, а не то, что отображается в «желаемом выводе» (, поэтому, например, и region
, и postalcode
не имеют проверки длины, поскольку они являются полями NULL ). ].
Выход:
CREATE TABLE employee
(
empid INT GENERATED BY DEFAULT AS IDENTITY,
lastname TEXT NOT NULL CHECK (LENGTH(lastname) <= 20),
firstname TEXT NOT NULL CHECK (LENGTH(firstname) <= 10),
title TEXT NULL,
titleofcourtesy TEXT NULL,
birthdate DATE NOT NULL,
hiredate DATE NOT NULL,
address TEXT NOT NULL CHECK (LENGTH(address) <= 60),
city TEXT NOT NULL CHECK (LENGTH(city) <= 15),
region TEXT NULL,
postalcode TEXT NULL,
country TEXT NOT NULL CHECK (LENGTH(country) <= 15),
phone TEXT NOT NULL CHECK (LENGTH(phone) <= 24),
mgrid INT NULL,
test_field TEXT NULL CHECK (LENGTH(test_field) <= 25)
);
Вот разница между выводом сценария и желаемым выводом (после очистки для удаления комментариев и некоторых посторонних символов пробела):
- region TEXT NULL CHECK (LENGTH(region) <= 15),
- postalcode TEXT NULL CHECK (LENGTH(postalcode) <= 10),
+ region TEXT NULL,
+ postalcode TEXT NULL,
Вы, вероятно, хотите PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY
дляempid
postgresql имеет тип данных VARCHAR (n ), который, вероятно, более подходит, чем TEXT, и гораздо проще преобразовывать:s/NVARCHAR/VARCHAR/
. VARCHAR имеют фиксированную длину, поэтому для )не нужны проверки ограничения длины,и b )быстрее индексируются и ищутся.
Разрешение поля быть NULL является значением по умолчанию, поэтому нет реальной необходимости явно определять их как таковые.