среда, 11 декабря 2013 г.

Как изящно разбить на несколько строк длинный строковый литерал в bash

Под изяществом я здесь понимаю простую возможность контролировать отступы от левого края перенесенных частей литерала. Возьмем, к примеру, следующий алиас g4conf, который я хочу поместить в сценарий $HOME/.bashrc.
alias g4conf='cmake -DCMAKE_INSTALL_PREFIX:PATH=$G4WORKDIR -DGeant4_DIR=$G4CONFIGDIR'
Этот алиас позволяет не писать в терминале длинные команды каждый раз, когда нужно сконфигурировать билд приложения с использованием библиотеки Geant4 (см. мой предыдущий пост). Кстати, обратите внимание на то, что значение алиаса взято в одинарные кавычки: это запрещает интерпретацию переменных среды в момент чтения интерпретатором сценария .bashrc и переносит ее на момент выполнения команды.

К сожалению, эта строка не умещается в 80 символов, чего я всегда стараюсь придерживаться в любом коде. Возникает вопрос о ее переносе. Удобнее всего перенести на новую строку последний аргумент cmake -DGeant4_DIR=$G4CONFIGDIR. Очевидное решение - использовать обратный слэш.
alias g4conf='cmake -DCMAKE_INSTALL_PREFIX:PATH=$G4WORKDIR \
                    -DGeant4_DIR=$G4CONFIGDIR'
Это сработает, хотя и выглядит некрасиво. Так, если ввести в терминале команду alias g4conf, то она выведет вот такую кривулю:
alias g4conf='cmake -DCMAKE_INSTALL_PREFIX:PATH=$G4WORKDIR \
                    -DGeant4_DIR=$G4CONFIGDIR'
Но самое главное - это то, что добавление дополнительных пробелов во многих случаях недопустимо или нежелательно, например при объявлении переменных среды PS1 и PS2. Другой вариант - сформировать две отдельных строки и соединить их обратным слэшем - тоже сработает неправильно.
alias g4conf='cmake -DCMAKE_INSTALL_PREFIX:PATH=$G4WORKDIR '\
                   '-DGeant4_DIR=$G4CONFIGDIR'
На этот раз алиас будет просто неверным.
$ alias g4conf
alias g4conf='cmake -DCMAKE_INSTALL_PREFIX:PATH=$G4WORKDIR '
И это неудивительно. Такой перенос строки вместо конкатенации двух ее частей формирует два аргумента для alias, при этом перенесенный второй аргумент просто игнорируется, что мы и видим в выводе команды alias g4conf.

Очень классное решение я подсмотрел здесь. Оно заключается в нецелевом использовании обратных кавычек.
alias g4conf='cmake -DCMAKE_INSTALL_PREFIX:PATH=$G4WORKDIR '`
                  `'-DGeant4_DIR=$G4CONFIGDIR'
На этот раз вывод команды alias g4conf будет таким, каким мы ожидали его увидеть:
alias g4conf='cmake -DCMAKE_INSTALL_PREFIX:PATH=$G4WORKDIR -DGeant4_DIR=$G4CONFIGDIR'
Как это работает. Интерпретатор во время выполнения сценария .bashrc находит первую обратную кавычку, затем пропускает все символы (включая возможные переносы строк) до второй обратной кавычки, и то, что находится между ними пытается эвалюировать как внешнюю команду. В нашем случае между первой и второй обратными кавычками находятся символ переноса строки и несколько пробелов: это эвалюируется в пустую строку. Строки, склеенные вместе без всяких пробелов bash интерпретирует как одну непрерывную строку, этот эффект и позволяет склеивать перенесенные части строк в одну. Обратите внимание на то, что между обратными кавычками и кавычками, ограничивающими части строк, не должно быть пробелов. Иначе произойдет не склейка нескольких строк в одну, а разбиение большой строки, которую мы пытаемся склеить, на несколько отдельных аргументов, как это было в предыдущем варианте.

В строках с двойными кавычками или без кавычек вовсе этот подход работает еще лучше. Поскольку обратные кавычки интерпретируются внутри двойных кавычек, то нет нужды расщеплять длинную строку вставкой промежуточных двойных кавычек, cклеенных перемычкой из обратных кавычек: достаточно просто вставить обратные кавычки в нужном месте. Например:
$ echo "My shell is `
>      `$SHELL"
My shell is /bin/bash
То же самое при отсутствии двойных кавычек:
$ echo My shell is `
>      `$SHELL
My shell is /bin/bash

1 комментарий: