в Кш, почему последняя (пустая) строка моей многострочной строки исчезает при сохранении ее в переменной?

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

Эта строка «пустая», но, тем не менее, я не могу потерять содержащийся в ней возврат каретки (потому что я объединяю блоки кода, сохраненные в БД и содержащие символ «\n», в удобочитаемую строку... Если я потеряю некоторые из "\n", я потеряю часть отступа моего кода)

Вот код, иллюстрирующий мою проблему:

test="A

B
";
test2=`echo "$test"`;
echo "||$test2||";

Это возвращает

||A

B||

пока я ждал:

||A

B
||

--> последняя (пустая) строка исчезла... и, таким образом, в моем удобочитаемом коде отсутствует возврат каретки.

Эта проблема возникает только тогда, когда последняя строка моей многострочной строки пуста...

Ты знаешь

  • Почему эта последняя строка исчезает?
  • Как я могу гарантировать, что моя последняя пустая строка будет сохранена в моей многострочной строковой переменной?

Обратите внимание, что я, конечно, не могу использовать самое простое решение

test2="$test";

потому что полный процесс скорее:

test="^A\n\nB\n^"
test2="`echo "$test" | sed -e 's/\^//g'`";

но я постарался максимально упростить вопрос.


person Seba991    schedule 07.09.2017    source источник
comment
Подстановки команд всегда обрезают завершающие символы новой строки — это соответствует дизайну и спецификации. В противном случае foo=$(echo foo) не содержало бы строки foo, но имело бы завершающую новую строку, что могло бы сбить людей с толку.   -  person Charles Duffy    schedule 07.09.2017
comment
Кстати, какая это конкретная версия ksh? Существует множество различных несовместимых реализаций.   -  person Charles Duffy    schedule 07.09.2017
comment
@CharlesDuffy Ни одна из команд, которые я пытался найти, какую версию ksh я использую, не сработала ... Я пробовал ksh --version, echo ${.sh.version} и echo $KSH_VERSION. Если вы знаете другие возможности идентифицировать мою версию ksh, мне очень интересно ;-)   -  person Seba991    schedule 07.09.2017
comment
Какая операционная система? Если у него есть менеджер пакетов, мы можем спросить у него, какой у вас ksh.   -  person Charles Duffy    schedule 07.09.2017
comment
@CharlesDuffy: IBM AIX 7.1.0.0   -  person Seba991    schedule 07.09.2017
comment
Хм. В некоторых версиях AIX используется rpm (что позволяет использовать семейство подкоманд rpm -q для проверки сведений об установленных пакетах), но я не помню, какие именно.   -  person Charles Duffy    schedule 08.09.2017
comment
@CharlesDuffy Думаю, я наконец нашел версию, выполнив Escape CTRL+V. Результат: версия M-11/16/88f.   -  person Seba991    schedule 08.09.2017


Ответы (2)


Подстановки команд всегда обрезают завершающие символы новой строки — это соответствует дизайну и спецификации. Если вы этого не хотите, вы можете добавить к выходным данным фиксированный символ сигилы и обрезать его так, чтобы новые строки, которые вы хотите сохранить, находились перед сигилом:

test="A

B
"
test_wip=$(printf '%sEND' "$test")
test2=${test_wip%END}
person Charles Duffy    schedule 07.09.2017
comment
Действительно, с этой идеей я могу получить желаемый результат test_wip=$(printf '%sENDOFLINE' $test); test_wip2=echo "$test_wip" | sed -e 's/\^//g'; test2=${test_wip2%ENDOFLINE}; Будем надеяться, что никто не будет использовать ENDOFLINE в коде, который я пытаюсь интерпретировать. - person Seba991; 07.09.2017
comment
@ Seba991 Seba991: вы можете немного сократить это с помощью test2="$(echo "${test}"ENDOFLINE | sed -e 's/\^//g')" ; test2="${test2%ENDOFLINE}" - person markp-fuso; 07.09.2017
comment
@ Seba991 Seba991, ну, мы удаляем только один ENDOFLINE (тот, что в самом конце), поэтому не имеет значения, есть лишний литерал в данных. - person Charles Duffy; 07.09.2017
comment
@markp, современные версии реального ksh (которых, похоже, нет у OP) оптимизируют var=$(printf ...), чтобы по эффективности они были эквивалентны printf -v var ... bash без подоболочки; использование sed будет значительно дороже. - person Charles Duffy; 07.09.2017
comment
@CharlesDuffy: эммм, не уверен, что понимаю, как printf может заменить sed, чтобы убрать ^ ... или вы говорите, что sed все еще требуется, но некоторые накладные расходы можно устранить, используя $(printf..) вместо $(echo..) - person markp-fuso; 07.09.2017
comment
Ааа - пропустил контекст (ОП пытается выполнить отдельную операцию и не имеет PE в любом нечетном ksh, который у них есть локально). - person Charles Duffy; 07.09.2017
comment
@CharlesDuffy: я понимаю ваши комментарии о том, что $(printf...) более эффективен, и спасибо за внимание; но OP расширил проблему до необходимости удалить фактическую каретку (^), но поскольку ${...//^} не вариант ... sed на «спасение»; да, мы говорим мимо друг друга :-) - person markp-fuso; 07.09.2017

Вместо того, чтобы пытаться обойти проблемы, возникающие при назначении вывода из echo переменной (например, удаление конечных \n), рассмотрите возможность использования встроенной обработки строк ksh в этом случае, например:

$ test="^A\n\nB\n^"
$ test2="${test//^}"
$ echo "||${test2}||"
||A

B
||
  • //^ : удалить все символы ^
person markp-fuso    schedule 07.09.2017
comment
Когда я пытаюсь это сделать, я получаю сообщение об ошибке: ksh: test2=${test//^}: 0403-011 Указанная замена недействительна для этой команды. - person Seba991; 07.09.2017
comment
хммм, что возвращает ksh --version? - person markp-fuso; 07.09.2017
comment
@ Seba991, эта подстановка команд действительна в ksh 93u+; если вы используете старую версию (или не совсем совместимый сторонний клон, такой как mksh), вы должны указать это в вопросе. - person Charles Duffy; 07.09.2017
comment
@markp ksh --version возвращает --version: 0403-010 Указанный флаг недействителен для этой команды. - person Seba991; 07.09.2017
comment
@Seba991 Seba991: да, вероятно, более старая (или несовместимая) версия ksh - person markp-fuso; 07.09.2017