
Я пишу скрипт bash для автоматического создания некоторых других файлов, и мне нужно отформатировать некоторые строки определенным образом. В частности, последняя проблема, с которой я сталкиваюсь, — это форматирование строки, которая содержит отдельные заглавные буквы, и слова, которое начинается с заглавной буквы. Например:
O S D Settings
необходимо статьOSD Settings
У меня есть команда sed, которая удаляет первый пробел, но также удаляет "D" (т.е. O S D Settings
-> OS Settings
). Эта команда:
O S D Settings | sed 's/ \([A-Z]\)* \(A-Za-z]*\)/\1/g'
Кто-нибудь знает, как удалить пробелы между отдельными заглавными буквами, не потеряв ни одной буквы?
решение1
Это касается таких имен, как A B Chadwick
иA B C D'Souza
Текст, такой как A B cde
и A B CDE
не изменяется.
Он использует два временных нулевых символа \x00
для обозначения изменений (для каждого имени) по мере продвижения по строке, удаляя пробелы.
:N
и :S
естьфилиал-кметки (подойдет любое имя)
t
и b
являются инструкциями по ветвлению.
t
ветвления при успешной замене в предыдущей s/../../
команде.
b
ветвления без условий.
sed -r ":N # loop per name
/(\<[A-Z]\> )+[A-Z][a-z']/{ # line needs action
s/((\<[A-Z]\> )+)([A-Z][a-z'])/\x00\1\x00\3/ # add \x00 markers
:S # loop per space
s/(\x00[A-Z]+) (\<[A-Z]\>)/\1\2/ # delete a space
t S # any more spaces?
b N # any more names?
}; s/\x00//g" # remove \x00
решение2
Сложновато с sed
, но если perl
все в порядке, то можно сделать так
echo O S D Settings | perl -p -e 's/(\b[A-Z]) (?=.([^\w]|$))/$1/g'
Это сложно, sed
поскольку не поддерживает упреждающие утверждения.
Тесты:
echo O S D | perl -p -e 's/(\b[A-Z]) (?=.([^\w]|$))/$1/g'
echo O S D Settings | perl -p -e 's/(\b[A-Z]) (?=.([^\w]|$))/$1/g'
echo O S D. | perl -p -e 's/(\b[A-Z]) (?=.([^\w]|$))/$1/g'
echo One O DDE T. S Asdf Q R Tee | perl -p -e 's/(\b[A-Z]) (?=.([^\w]|$))/$1/g'
echo O S D\ | perl -p -e 's/([A-Z]) (?=.([^\w]|$))/$1/g'
Если вам нужно неаккуратное решение с sed
, попробуйте
echo O S D Settings | sed -e 's/ \([A-Z]\) \([A-Z] \)/\1\2/g'
Это работает для вашего образца, но не сработает в других случаях.
Тесты:
echo O S D | sed -e 's/ \([A-Z]\) \([A-Z] \)/\1\2/g'
echo O S D Settings | sed -e 's/ \([A-Z]\) \([A-Z] \)/\1\2/g'
echo O S D. | sed -e 's/ \([A-Z]\) \([A-Z] \)/\1\2/g'
echo One O DDE T. S Asdf Q R Tee | sed -e 's/ \([A-Z]\) \([A-Z] \)/\1\2/g'
echo O S D\ | sed -e 's/ \([A-Z]\) \([A-Z] \)/\1\2/g'
решение3
Это может вам подойти:
echo "O S D Settings and B T W and A B C D'Souza too F Y I" |
sed ':a;s/\(\<[[:upper:]]\>\) \(\<[[:upper:]]\>\([^'\'']\|$\)\)/\1\n\2/g;ta;s/\n//g'
OSD Settings and BTW and ABC D'Souza too FYI
Объяснение:
Используйте символ, которого нет в исходной строке, чтобы заменить пробелы, которые вы хотите удалить, затем удалите выбранный символ во всей строке. \n
— хороший кандидат, поскольку он не может существовать в обычном режиме, поскольку используется sed в качестве разделителя строк.
решение4
В итоге я просто использовал sed с каналами, чтобы получить выражение, которое мне легко понять:
echo O S D Settings | sed 's/\([A-Z][^ ]\)/_\1/g' | sed 's/ //g' | sed 's/_/ /g'
Все, что это делает, это заменяет ненужные мне пробелы на подчеркивание, а затем удаляет их. Спасибо за все ответы!