Регулярные выражения -Манипуляции с SQL

Кажется, вы оставили на один $слишком много. Замените $wholeна :

.
final=${whole#$prefix};
3
02.06.2021, 10:52
3 ответа

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

  • Вы используете расширенные регулярные выражения, но забыли указать параметр -Eдля sed.
  • Вы хотите повторно использовать идентификатор, но не включили его в()
  • Кажется, вы смешиваете группы ERE ()с буквальными. Вы, вероятно, имели в виду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'
1
28.07.2021, 11:28

Поскольку вы используете 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до того, как встретится с последней строкой, если первый тест прошел успешно.

3
28.07.2021, 11:28

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 является значением по умолчанию, поэтому нет реальной необходимости явно определять их как таковые.

1
28.07.2021, 11:28

Теги

Похожие вопросы