Sed: змеиные функции
Мне нужен скрипт sed для автоматического преобразования функций C в нижний регистр змеи.
То, что я имею до сих пор, - это следующее, которое будет отделять слова падежа верблюда подчеркиванием, но это не строчные буквы и это влияет на все.
sed -i -e 's/\([a-z0-9]\)\([A-Z]\)/\1_\L\2/g' `find source/ -type f`
Как сделать так, чтобы это применялось только к функциям? Т.е. только на строках с последующим символом '('.
Кроме того, что мне нужно, чтобы строки начинались с нижнего регистра?
Например, если у меня есть этот код:
void destroyPoolLender(PoolLender *lender)
{
while (!isListEmpty(&lender->pools)) {
MemoryPool *myPool = listPop(&this->pool);
if (pool->inUse) {
logError("%s memory pool still in use. Pool not released.", pool->lenderName);
} else {
free(pool);
}
}
listDestroy(&this->pool);
}
Это должно выглядеть так, как только будет преобразовано:
void destroy_pool_lender(PoolLender *lender)
{
while (!is_list_empty(&lender->pools)) {
MemoryPool *myPool = list_pop(&this->pool);
if (pool->inUse) {
log_error("%s memory pool still in use. Pool not released.", pool->lenderName);
} else {
free(pool);
}
}
list_destroy(&lender->pools);
}
Обратите внимание, что myPool не тронут, потому что это не имя функции.
2 ответа
Решение для Баш. Он использует информацию из объектных файлов nm
команда. Увидеть man nm
,
Для создания объектных файлов из источников вам нужно запустить gcc
с -c
опцию для каждого исходного файла (может быть, у вас уже есть, созданные make
команда. Затем вы можете пропустить этот шаг):
gcc -c one.c -o one.o
gcc -c two.c -o two.o
Использование: ./convert.sh one.o two.o
#!/bin/bash
# store original function names to the variable.
orig_func_names=$(
# get list symbols from all object files
nm -f sysv "$@" |
# picks the functions and removes all information except names.
sed -n '/FUNC/s/\s.*//p' |
# selects only functions, which contain the uppercase letter in the name.
sed -n '/[A-Z]/p'
);
# convert camel case names to snake case names and store new names to the variable.
new_func_names=$(sed 's/[A-Z]/_\l&/g' <<< "$orig_func_names")
# create file, containing substitute commands for 'sed'.
# Example of commands from this file:
# s/\boneTwo\b/one_two/g
# s/\boneTwoThree\b/one_two_three/g
# etc. One line to the each function name.
paste -d'/' <(printf 's/\\b%s\\b\n' ${orig_func_names}) <(printf '%s/g\n' ${new_func_names}) > command_file.txt
# do converting
# change object file extenstions '.o' to C source - '.c' file extensions.
# were this filenames: one.o two.o three.o
# now they are: one.c two.c three.c
# this 'sed' command creates backup for the each file and change the source files.
sed -i_backup -f command_file.txt "${@/.o/.c}"
Следует отметить, что время выполнения растет в геометрической прогрессии в этом решении. Например, если у нас 70000 строк и 1000 функций, то нужно выполнить 70 миллионов проверок (70 000 строк * 1000 функций). Было бы интересно узнать, сколько времени это займет.
тестирование
вход
файл one.c
#include <stdio.h>
int one();
int oneTwo();
int oneTwoThree();
int oneTwoThreeFour();
int one() {
puts("");
return 0;
}
int oneTwo() {
printf("%s", "hello");
one();
return 0;
}
int oneTwoThree() {
oneTwo();
return 0;
}
int oneTwoThreeFour() {
oneTwoThree();
return 0;
}
int main() {
return 0;
}
файл two.c
#include <stdio.h>
int two() {
return 0;
}
int twoThree() {
two();
return 0;
}
int twoThreeFour() {
twoThree();
return 0;
}
Выход
файл one.c
#include <stdio.h>
int one();
int one_two();
int one_two_three();
int one_two_three_four();
int one() {
puts("");
return 0;
}
int one_two() {
printf("%s", "hello");
one();
return 0;
}
int one_two_three() {
one_two();
return 0;
}
int one_two_three_four() {
one_two_three();
return 0;
}
int main() {
return 0;
}
файл two.c
#include <stdio.h>
int two() {
return 0;
}
int two_three() {
two();
return 0;
}
int two_three_four() {
two_three();
return 0;
}
Мы можем сделать это с помощью sed. Хитрость заключается в том, чтобы соответствовать всем, включая (
в качестве группы захвата 2, и использовать \l
скорее, чем \L
, в нижнем регистре только первый соответствующий символ:
s/\([a-z0-9]\)\([A-Z][A-Za-z0-9]*(\)/\1_\l\2/
Мы не можем просто использовать /g
модификатор, потому что последующие замены могут перекрываться, поэтому используйте его в цикле:
#!/bin/sed -rf
:loop
s/([a-z0-9])([A-Z][A-Za-z0-9]*\()/\1_\l\2/
tloop
(Я использовал -r
для GNU sed, чтобы уменьшить количество обратных косых черт, которые мне нужны).
Дальнейшее упрощение состоит в том, чтобы соответствовать границе без слов; это устраняет необходимость в двух группах захвата:
#!/bin/sed -rf
:loop
s/\B[A-Z]\w*\(/_\l&/
tloop
Демо-версия:
$ sed -r ':loop;s/\B[A-Z]\w*\(/_\l&/;tloop' \
<<<'SomeType *myFoo = callMyFunction(myBar, someOtherFunction());'
SomeType *myFoo = call_my_function(myBar, some_other_function());
Обратите внимание, что это только изменяет вызовы функций и определения - может быть трудно определить, какие имена являются функциями, если вы храните или передаете указатели на функции. Вы можете решить исправить это вручную (реагируя на ошибки компиляции), если у вас есть только 70 тыс. Строк для обработки. Если вы работаете с 1M+, вам может понадобиться подходящий инструмент рефакторинга.