#c #regex #linux #sed
#c #регулярное выражение #linux #sed
Вопрос:
Итак, у меня есть гигантская база кода, к которой необходимо добавить пространство имен. Вместо того, чтобы делать это вручную, я подумал об использовании grep, xargs amp; sed для добавления пространства имен ко всем исходным файлам…
Но мои навыки не на должном уровне. В идеале
namespace foo
{
Будет добавлено после всех включений, а ‘}’ добавлено после #endif в файлах .h.
Для файлов .cpp достаточно добавить ‘using namespace foo’ после всех включений.
Я возился с sed, но не продвинулся далеко.
Любая помощь будет оценена. Спасибо!
Комментарии:
1. Я рекомендую вам использовать язык сценариев, такой как Ruby, с регулярными выражениями, для выполнения этой работы. С вашими требованиями и некоторыми примерами Ruby в Google, вы можете выполнить этот скрипт за 30 минут-1 час. Вы можете использовать rubular.com чтобы проверить ваше регулярное выражение. Черт возьми, я только что написал скрипт Ruby для поиска зависимостей пространства имен / модуля между всеми моими файлами .hpp и .cpp — потребовалось около часа, чтобы написать с использованием regex.
2. sed — одна из первых программ, которая использовала регулярные выражения в качестве основного инструмента для работы с текстом. Удачи всем.
Ответ №1:
Это непростая задача. Вещи, которые делают это проблематичным, включают
- Материал, который вы не хотите размещать внутри вашего нового пространства имен, например
- Такие конструкции, как
language "C" { C-stuff }
- Пересылать объявления классов, которые не являются вашими. Это становится в n раз сложнее, если вы используете какой-либо внешний пакет, который также не использует пространства имен.
- Такие конструкции, как
- Даже найти, куда поместить
namespace Foo {
и закрывающий ‘}’, может быть сложно.- Вы хотите вложить объявления forward, кроме тех, которые вам не принадлежат.
- Обычно вы хотите заключить в область действия файла все, что включает пару фигурных скобок, за исключением таких вещей, как
language C
. - Программисты-разработчики (если ваш код существует достаточно долго) будут делать с вашим кодом всевозможные плохие вещи. Они будут отправлять объявления внутри классов или функций, между классами и функциями, где угодно, кроме верхнего, что упрощает работу скрипта.
- Вы не хотите делать двойные вложения. Это удивительно легко сделать.
- По разным причинам некоторые люди помещают в конец заголовка то, что кажется посторонним. Это может быть особенно неприятной проблемой.
- Если вы решите попытаться заключить каждый класс, структуру и перечисление, вам придется выяснить, где на самом деле начинается объявление. Вы хотите обернуть
template
объявление, если оно есть, и, конечно, комментарии doxygen и тому подобное. - Поиск конца класса может быть просто неприятным. У людей есть неприятная тенденция вставлять несоответствующие фигурные скобки в комментарии и строки.
Удачи! Написание скрипта займет чуть больше получаса. Это займет гораздо меньше времени, чем переписывание всего вашего кода. Я предлагаю вам использовать что-то более мощное, чем sed. Perl, python и ruby — три хороших варианта.
Ответ №2:
Это заставило меня на полпути:
#!/bin/sh
NAMESPACE=my_namespace
for x in $(find . -name "*.h"); do
sed -i "$(grep -n "^#" $x | tail -2 | head -1 | sed 's/:.*//')a\nnamespace $NAMESPACE {n" $x
sed -i "$(($(grep -n "^#" $x | tail -1 | sed 's/:.*//')-1))a} // namespace $NAMESPACEn" $x
done
for x in $(find . -name "*.cpp"); do
sed -i "$(grep -n "#include" $x | tail -1 | sed 's/:.*//')a\nnamespace $NAMESPACE {n" $x
echo >> $x
echo "} // namespace $NAMESPACE" >> $x
done
В основном он находит предпоследнюю директиву препроцессора в файле h и добавляет туда пространство имен. он закрывает пространство имен прямо перед последней директивой препроцессора (я предполагаю, что у меня есть #endif
в конце моих файлов h). Если это не так, сценарий можно соответствующим образом адаптировать.
В случае cpp я ищу последнее включение, чтобы открыть пространство имен и закрыть его в конце файла.
Это далеко от совершенства, но, столкнувшись с более чем 100 файлами, это значительно упростило работу.
Ответ №3:
Обратите внимание, это не учитывает закрытие защитных #endif
строк, но их обертывание в a namespace {};
, похоже, никому не повредит.
Удалите строки комментариев в соответствии с вашими предпочтениями.
#!/usr/bin/env bash
namespace="namespace m {"
pad=" "
function processFile {
# dos2unix "$1"
local fn=$1
local tmp="$1.tmp"
echo "Processing '$fn'"
rm -f "$tmp"
local -i skipping=1
local regSkip="^(#include.*|#pragma.*|//.*|)"'$'
local line
while IFS= read -r line
do
if (( skipping )); then
if [[ $line =~ $regSkip ]]; then :
else skipping=0; echo "$namespace" >> "$tmp"
fi
echo "$line" >> "$tmp"
else
echo "$pad$line" >> "$tmp"
fi
done < "$1"
echo "$line" >> "$tmp"
echo "};" >> "$tmp"
mv -f "$fn" "$fn.ori"
# optionally reformat
astyle --options='e:gitsfink.ini' < "$tmp" > "$fn" amp;amp; rm "$tmp"
# else just move
# mv -f "$tmp" "$fn"
}
# for fn in $( find . -name '*.h' ); do processFile "$fn"; done
# for fn in $( find . -name '*.cpp' ); do processFile "$fn"; done
for fn in "$@"; do processFile "$fn"; done
n.b. файлы должны быть в формате перевода строки unix