awk sed или regex вставить подстроку и изменить регистр - PullRequest
0 голосов
/ 26 апреля 2011

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

VI.d5.5
VII.b2.1
VII.b2.2
VII.b2.3
VII.c1

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

VI.D.d5.5
VII.B.b2.1
VII.B.b2.2
VII.B.b2.3
VII.C.c1

Я знаю о флаге \U в sed, но не знаю, как применить его только один раз.Например, следующие заглавные буквы и вставленная буква, и оригинальная строчная буква: (нежелательно)

echo 'VII.b1.1' | sed -e 's/\([a-h]\)/\U\1.\1/'
VII.B.B1.1

Я бы приветствовал любую оболочку (sed, awk, perl и т. Д.) Или решение vim, которое быпозвольте мне изменить этот столбец на месте в файле с разделителями табуляции.

Ответы [ 6 ]

2 голосов
/ 26 апреля 2011

Вы не можете сделать это в стандартном sed (1) , потому что там нет такой вещи, как \u или \U. Действительно, на всех моих системах (кроме одной) она выходит из строя - и молча, увы! Я попробовал версию sed как на своем ноутбуке Mac, так и на рабочем столе Mac, а затем попробовал ее на нашем сервере Solaris и на нашем сервере OpenBSD. Я тоже попробовал это на одиночном AIX-боксе, и, конечно, он там не работал. (

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

% cat sample
VI.d5.5                                                                           
VII.b2.1
VII.b2.2
VII.b2.3
VII.c1

% perl -wpe 's/([^.]+)\.(.)/$1.\u$2.$2/' /tmp/sample 
VI.D.d5.5
VII.B.b2.1
VII.B.b2.2
VII.B.b2.3
VII.C.c1

Мало того, что он более портативный, он намного проще.

Это должно работать на любой версии Perl, выпущенной за последние 20 лет, включая perl4. Однако, если вы живете на переднем крае и у вас установлено не менее 5.10, вы можете сделать это следующим образом:

% perl -M5.10.0 -wpe 's/[^.]+\.\K(?=(.))/\u$1./' /tmp/sample
VI.D.d5.5
VII.B.b2.1
VII.B.b2.2
VII.B.b2.3
VII.C.c1

То, что ‑M5.10.0 просто для того, чтобы убедиться, что у вас действительно есть доступный и загруженный набор функций 5.10.

А как насчет Юникода?

Теперь предположим, что в ваших данных образца есть Unicode:

% cat /tmp/sample.utf8
Ⅵ.ð5.5
Ⅷ.ß2.3
Ⅺ.ç1

% uniquote /tmp/sample.utf8 
\N{U+2165}.\N{U+F0}5.5
\N{U+2167}.\N{U+DF}2.3
\N{U+216A}.\N{U+E7}1

% uniquote -v /tmp/sample.utf8
\N{ROMAN NUMERAL SIX}.\N{LATIN SMALL LETTER ETH}5.5
\N{ROMAN NUMERAL EIGHT}.\N{LATIN SMALL LETTER SHARP S}2.3
\N{ROMAN NUMERAL ELEVEN}.\N{LATIN SMALL LETTER C WITH CEDILLA}1

Я могу гарантировать вам, что вы не найдете версию sed, которая правильно работает с этими данными. Это испортит. Я подошел к нашему жертвенному Linux-боксу, и хотя ɢɴᴜsed, который они там используют, работает с вашими примерами данных, он отказался от отображения одного из этих символов в моем более красивом наборе данных Unicode, даже когда у меня все настройки были настроены правильно. Но версия perl все же сделала правильно.

Но с perl, просто добавьте параметры командной строки ‑CSD, чтобы сообщить perl, что файлы данных и std {in, out, err} находятся в UTF ‑ 8, затем выполните те же команды, и вы увидите что-то, что действительно Qᴜɪᴛᴇ Iɴᴛᴇʀᴇsᴛɪɴɢ :

% perl -CSD -wpe 's/([^.]+)\.(.)/$1.\u$2.$2/' /tmp/sample.utf8
Ⅵ.Ð.ð5.5
Ⅷ.Ss.ß2.3
Ⅺ.Ç.ç1

% perl -CSD -wpe 's/[^.]+\.\K(?=(.))/\u$1./' /tmp/sample.utf8
Ⅵ.Ð.ð5.5
Ⅷ.Ss.ß2.3
Ⅺ.Ç.ç1

% perl -CSD -wpe 's/[^.]+\.\K(?=(.))/\U$1./' /tmp/sample.utf8
Ⅵ.Ð.ð5.5
Ⅷ.SS.ß2.3
Ⅺ.Ç.ç1

Как видите, есть разница между заголовком , который \u делает и верхним регистром , который \U делает. Это потому, что строчная буква «ß» - это «Ss» в заглавном, а «SS» в верхнем. Странно, но верно! По общему признанию, подобное происходит гораздо чаще с греческими буквами, чем с латинскими, как мы используем, но вы все равно хотите сделать это правильно.

Вот это all uniquote d, чтобы вы могли видеть, о каких именно кодах мы говорим:

% perl -CSD -wpe 's/[^.]+\.\K(?=(.))/\u$1./' /tmp/sample.utf8 | uniquote
\N{U+2165}.\N{U+D0}.\N{U+F0}5.5
\N{U+2167}.Ss.\N{U+DF}2.3
\N{U+216A}.\N{U+C7}.\N{U+E7}1

% perl -CSD -wpe 's/[^.]+\.\K(?=(.))/\u$1./' /tmp/sample.utf8 | uniquote -v
\N{ROMAN NUMERAL SIX}.\N{LATIN CAPITAL LETTER ETH}.\N{LATIN SMALL LETTER ETH}5.5
\N{ROMAN NUMERAL EIGHT}.Ss.\N{LATIN SMALL LETTER SHARP S}2.3
\N{ROMAN NUMERAL ELEVEN}.\N{LATIN CAPITAL LETTER C WITH CEDILLA}.\N{LATIN SMALL LETTER C WITH CEDILLA}1

Разве это не круто?

2 голосов
/ 26 апреля 2011
sed -e 's/\.[a-z]/\U&\E&/'

Perl тоже хорошо работает:

perl -pe 's/\.[a-z]/uc($&) . $&/e'
2 голосов
/ 26 апреля 2011

вы пробовали \u вместо \U?По данным информационной страницы sed (info sed):

`\U'
     Turn the replacement to uppercase until a `\L' or `\E' is found,

`\u'
     Turn the next character to uppercase,
1 голос
/ 27 апреля 2011

Вот решение awk. Никаких грязных регулярных выражений не требуется. Основная идея: разделить на точки, получить первый символ 2-го поля. Затем измените его регистр, используя функцию toupper (). Наконец, заменить обратно на 2-е поле.

awk -F"." '{
    ch = toupper(substr($2,1,1))
    $2=ch"."$2
}1' OFS="." file
1 голос
/ 26 апреля 2011

Попробуйте использовать \ u вместо \ U, что переводит следующий символ в верхний регистр. Но если вы хотите использовать \ U, вы должны остановить верхний регистр с помощью \ E или \ L сделать как

's/\([a-h]\)/\U\1\E.\1/'

1 голос
/ 26 апреля 2011
sed -e 's/\([^.]\+\)\.\(.\)/\1.\u\2\.\2/'

как это:

$ sed -e 's/\([^.]\+\)\.\(.\)/\1.\u\2\.\2/' <<<'VI.d5.5'
VI.D.d5.5
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...