удалить ведущие пробелы из всех строк в скрипте [duplicate]

Что такое «неопределенный ссылочный / неразрешенный внешний символ»

Я попытаюсь объяснить, что такое «неопределенный ссылочный / неразрешенный внешний символ».

note : я использую g ++ и Linux, и все примеры для него

Например, у нас есть некоторый код

// src1.cpp
void print();

static int local_var_name; // 'static' makes variable not visible for other modules
int global_var_name = 123;

int main()
{
    print();
    return 0;
}

и

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
//extern int local_var_name;

void print ()
{
    // printf("%d%d\n", global_var_name, local_var_name);
    printf("%d\n", global_var_name);
}

Создание объектных файлов

$ g++ -c src1.cpp -o src1.o
$ g++ -c src2.cpp -o src2.o

После фазы ассемблера у нас есть объектный файл, который содержит любые экспортируемые символы. Посмотрите на символы

$ readelf --symbols src1.o
  Num:    Value          Size Type    Bind   Vis      Ndx Name
     5: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 _ZL14local_var_name # [1]
     9: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_var_name     # [2]

Я отклонил некоторые строки из вывода, потому что они не имеют значения

Итак, мы видим следующие символы для экспорта.

[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable

src2.cpp ничего не экспортирует, и мы не видели его символов

Свяжите наши объектные файлы

$ g++ src1.o src2.o -o prog

и запустите его

$ ./prog
123

Linker видит экспортированные символы и связывает их. Теперь мы пытаемся раскомментировать строки в src2.cpp, как здесь

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
extern int local_var_name;

void print ()
{
    printf("%d%d\n", global_var_name, local_var_name);
}

, и перестроить объектный файл

$ g++ -c src2.cpp -o src2.o

OK (нет ошибок), потому что мы только строим объектный файл, связь еще не завершена. Попробуйте установить ссылку

$ g++ src1.o src2.o -o prog
src2.o: In function `print()':
src2.cpp:(.text+0x6): undefined reference to `local_var_name'
collect2: error: ld returned 1 exit status

Это произошло потому, что наше local_var_name статично, то есть оно не отображается для других модулей. Теперь глубже. Получить выход фазы перевода

$ g++ -S src1.cpp -o src1.s

// src1.s
look src1.s

    .file   "src1.cpp"
    .local  _ZL14local_var_name
    .comm   _ZL14local_var_name,4,4
    .globl  global_var_name
    .data
    .align 4
    .type   global_var_name, @object
    .size   global_var_name, 4
global_var_name:
    .long   123
    .text
    .globl  main
    .type   main, @function
main:
; assembler code, not interesting for us
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

Итак, мы видели, что для local_var_name нет метки, поэтому линкер не нашел его. Но мы хакеры :), и мы можем это исправить. Откройте src1.s в текстовом редакторе и измените

.local  _ZL14local_var_name
.comm   _ZL14local_var_name,4,4

на

    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789

i.e. вам должно быть как ниже

    .file   "src1.cpp"
    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789
    .globl  global_var_name
    .align 4
    .type   global_var_name, @object
    .size   global_var_name, 4
global_var_name:
    .long   123
    .text
    .globl  main
    .type   main, @function
main:
; ...

мы изменили видимость local_var_name и установили его значение в 456789. Попробуйте построить из него объектный файл

$ g++ -c src1.s -o src2.o

ok, см.

$ readelf --symbols src1.o
8: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 local_var_name

В настоящее время local_var_name имеет привязку GLOBAL (LOCAL)

link

$ g++ src1.o src2.o -o prog

и запускает ее

$ ./prog 
123456789

ok, мы взломаем его:)

Итак, в результате - «неопределенная ссылка / неразрешенная внешняя ошибка символа» происходит, когда компоновщик не может найти глобальные символы в объектных файлах.

23
задан Cengiz 9 November 2013 в 23:12
поделиться

5 ответов

Вы можете использовать .stripIndent(), чтобы удалить отступ в многострочных строках. Но вы должны иметь в виду, что если не указывается количество отступов, оно будет автоматически определяться из строки, содержащей наименьшее число ведущих пробелов .

Учитывая вашу строку это будет только одно пустое пространство перед This, которое будет удалено из каждой строки вашей многострочной строки.

def s = """ This 
            is
            multiline
"""

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

def s = """\
           This
           is
           multiline
"""
30
ответ дан sschuberth 25 August 2018 в 06:59
поделиться

Также используйте .stripMargin () (если это возможно).

def s = """ This 
            | is
            | multiline
        """
println s.stripMargin().stripIndent()
13
ответ дан Dónal 25 August 2018 в 06:59
поделиться

Для любого другого, у кого есть аналогичная проблема, решение stefanglase отлично, но дает мне исключение MultipleCompilationErrorsException в тесте Spock при включении многострочной строки в утверждение, которое не выполняется:

org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
Spec expression: 1: unexpected char: '\' @ line 1, column 16.
   myString == '''\ This Is Multiline '''.stripIndent()

Мое решение для этого is:

def myString = '''
  This
  Is
  Multiline
  '''.replaceFirst("\n","").stripIndent()

Теперь, когда утверждение терпит неудачу, вы увидите обычный diff, указывающий, что пошло не так, а не исключение компиляции.

3
ответ дан Ondra Žižka 25 August 2018 в 06:59
поделиться

Вы намеревались использовать == вместо =? Единственные ошибки, которые я получаю, - это использовать ваш пример. если я изменил его на = и использовал ваш пример без replaceFirst (), он отлично работает без ошибок.

Также вы не можете использовать a \nри выполнении одной строки. Я могу дублировать вашу точную проблему, если я использую '' '\ This Is Multiline' '' .stripIndent ()

1
ответ дан Protocol-X 25 August 2018 в 06:59
поделиться

У меня есть аналогичный случай использования, потому что я хотел бы отформатировать мой SQL-запрос в строке. Например, вложенный запрос:

String query = '''
    select ${CONTROL_ID} from ${TABLE_NAME} 
        where 
            location_id = ( 
                select id from location where code = ${locationCode} 
            )  
''';

Выглядит намного лучше в Groovy, чем версия Java с "..."+ TABLE_NAME +"...", поскольку я уверен, что вы можете согласиться.

Этот тип строки не работает с методом .stripIndent(). Вместо этого я сохранил новые строки в query (выше) для какой-либо цели. Обратите внимание на «\» в конце строки.

И, таким образом,

String query = """
    select ${CONTROL_ID} from ${TABLE_NAME} 
        where 
            location_id = ( 
                select id from location where code = '${locationCode}' 
            )  
""".replaceAll( /\n\s*/, " " );

println  "  Query= ${query};"; 

Дайте аккуратно отформатированный однострочный результат SQL-запроса:

  Query = select id from controls where  location_id = (  select id from location where code = '003');

Который я считаю весьма полезным и более простым для чтения. Я заменяю одно пространство, чтобы убедиться, что SQL остается дискретным. Важная часть состоит в том, чтобы использовать новую строку (\n) в качестве стартовой линии / якоря de facto . Отлично подходит для смешанных TAB-s и SPACE-s.

Конечно, это также работает и для оригинального вопроса.

1
ответ дан will 25 August 2018 в 06:59
поделиться
Другие вопросы по тегам:

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