Есть ли что-то подобное echo -n в heredoc (EOF)?

Я пишу на огромном скрипте-сценарии, генерирующем множество сценариев обслуживания для моих серверов.

До сих пор я пишу некоторые строки, которые нужно записать в одной строке с помощью echo -ne, например

echo -n "if (( " | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null # Generate exitCode check for each Server IFS=" " COUNT=0 while read -r name ipAddr do if(($COUNT != 0)) then echo -n " || " | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null fi echo -n "(\$"$name"_E != 0 && \$"$name"_E != 1)" | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null COUNT=$((COUNT+1)) JOBSDONE=$((JOBSDONE+1)) updateProgress $JOBCOUNT $JOBSDONE done <<< "$(sudo cat /root/.virtualMachines)" echo " ))" | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null

это порождает меня (если в моем файле конфигурации есть, например, 2 сервера), например

if (( ($server1_E != 0 && $server1_E != 1) || ($server2_E != 0 && $server2_E != 1) ))

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

cat << EOF | sudo tee -a /usr/local/bin/upgradeAllServers &> /dev/null then # Print out ExitCode legend echo " ExitCode 42 - upgrade failed" echo " ExitCode 43 - Upgrade failed" echo " ExitCode 44 - Dist-Upgrade failed" echo " ExitCode 45 - Autoremove failed" echo "" echo "" fi EOF

Итак, конечный блок кода выглядит как

if (( ($server1_E != 0 && $server1_E != 1) || ($server2_E != 0 && $server2_E != 1) )) then # Print out ExitCode legend echo " ExitCode 42 - upgrade failed" echo " ExitCode 43 - Upgrade failed" echo " ExitCode 44 - Dist-Upgrade failed" echo " ExitCode 45 - Autoremove failed" echo "" echo "" fi

Мой вопрос: Есть ли способ, с помощью которого heredoc ведет себя подобно echo -ne без символа конца строки?

9
задан 30 November 2017 в 13:14

10 ответов

Нет, heredoc всегда заканчивается новой строкой. Но вы все равно можете удалить последнюю строку новой строки, например, с помощью Perl:

cat << 'EOF' | perl -pe 'chomp if eof' First line Second line EOF -p читает ввод строки за строкой, запускает код, указанный в -e, и печатает (возможно, line chomp удаляет окончательную новую строку, если она существует, eof возвращает true в конце файла.
9
ответ дан 18 July 2018 в 02:19

Heredocs всегда заканчивается символами новой строки, но вы можете просто удалить это. Самый простой способ, который я знаю, - это программа head:

head -c -1 <<EOF | sudo tee file > /dev/null my heredoc content EOF
1
ответ дан 18 July 2018 в 02:19

Вы могли бы удалить новые строки любым способом, который вам понравился бы, например, переход на tr был бы самым простым. Конечно, это приведет к удалению всех строк новой строки.

$ tr -d '\n' <<EOF if (( EOF

Но утверждение if, которое вы строите, не требует этого, арифметическое выражение (( .. )) должно работать нормально даже если он содержит символы новой строки.

Итак, вы можете сделать

cat <<EOF if (( EOF for name in x y; do cat << EOF ( \$${name}_E != 0 && \$${name}_E != 1 ) || EOF done cat <<EOF 0 )) ; then echo do something fi EOF

, производя

if (( ( $x_E != 0 && $x_E != 1 ) || ( $y_E != 0 && $y_E != 1 ) || 0 )) ; then echo do something fi

Построение его в цикле все же делает уродливым. Конечно, || 0, поэтому нам не нужен специальный случай для первой или последней строки.

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

exec 3> >(sudo tee outputfile &>/dev/null) echo output to the pipe like this >&3 echo etc. >&3 exec 3>&- # close it in the end

И, честно говоря, я все еще подумайте, что создание имен переменных из переменных во внешнем скрипте (например, \$${name}_E) является немного уродливым, и его, вероятно, следует заменить на ассоциативный массив.

1
ответ дан 18 July 2018 в 02:19

Я рекомендую вам попробовать другой подход. Вместо того, чтобы скомпилировать созданный скрипт из нескольких выражений эха и heredocs. Я предлагаю вам попробовать использовать один heredoc.

Вы можете использовать расширение переменных и подстановку команд внутри heredoc. Как и в этом примере:

#!/bin/bash cat <<EOF $USER $(uname) EOF

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

Также похоже, что вы забыли #! в начале ваших скриптов. Строка #! обязательна в любых правильно отформатированных сценариях. Попытка запустить скрипт без строки #! будет работать, только если вы вызовете его из оболочки, которая работает с плохо отформатированными сценариями, и даже в этом случае она может получить интерпретацию другой оболочки, чем вы предполагали.

6
ответ дан 18 July 2018 в 02:19
Есть ли способ, с помощью которого heredoc ведет себя подобно echo -ne без символа конца строки?

Короткий ответ заключается в том, что heredoc и herestrings просто построены таким образом, поэтому no - here-doc и herestring будут добавлять конечную новую строку всегда, и нет возможности или собственный способ ее отключить (возможно, будет в будущих релизах bash?).

Однако один из способов приблизиться к нему с помощью только bash - это прочитать, что heredoc дает с помощью стандартного метода while IFS= read -r, но добавить задержку и распечатать последнюю строку через printf без завершающей новой строки , Вот что можно сделать только с bash-only:

#!/usr/bin/env bash while true do IFS= read -r line || { printf "%s" "$delayed";break;} if [ -n "$delayed" ]; then printf "%s\n" "$delayed" fi delayed=$line done <<EOF one two EOF

То, что вы видите здесь, это обычный цикл while с подходом IFS= read -r line, однако каждая строка сохраняется в переменной и, таким образом, задерживается (поэтому мы пропускаем распечатать первую строку, сохранить ее и начать печатать только после чтения второй строки). Таким образом, мы можем захватить последнюю строку, и когда на следующей итерации read возвращается статус выхода 1, это когда мы печатаем последнюю строку без новой строки через printf. Подробный ? да. Но он работает:

$ ./strip_heredoc_newline.sh one two$

Обратите внимание, что символ приглашения $ выдвинут вперед, так как нет новой строки. В качестве бонуса это решение является портативным и POSIX-ish, поэтому оно должно работать и в ksh и dash.

$ dash ./strip_heredoc_newline.sh one two$
7
ответ дан 18 July 2018 в 02:19

Нет, heredoc всегда заканчивается новой строкой. Но вы все равно можете удалить последнюю строку новой строки, например, с помощью Perl:

cat << 'EOF' | perl -pe 'chomp if eof' First line Second line EOF -p читает ввод строки за строкой, запускает код, указанный в -e, и печатает (возможно, line chomp удаляет окончательную новую строку, если она существует, eof возвращает true в конце файла.
9
ответ дан 24 July 2018 в 17:31
  • 1
    @Yaron, если каждый обнаруженный конец файла (в этом случае закрытый канал), он будет chomp newline char из последней строки (это то, что heredoc и herestring добавляются автоматически) – Sergiy Kolodyazhnyy 30 November 2017 в 13:22

Heredocs всегда заканчивается символами новой строки, но вы можете просто удалить это. Самый простой способ, который я знаю, - это программа head:

head -c -1 <<EOF | sudo tee file > /dev/null my heredoc content EOF
1
ответ дан 24 July 2018 в 17:31
  • 1
    Прохладный, я не знал, что -c также принимает отрицательные значения! Подобно этому даже больше, чем решение pearl – derHugo 21 December 2017 в 19:10

Вы могли бы удалить новые строки любым способом, который вам понравился бы, например, переход на tr был бы самым простым. Конечно, это приведет к удалению всех строк новой строки.

$ tr -d '\n' <<EOF if (( EOF

Но утверждение if, которое вы строите, не требует этого, арифметическое выражение (( .. )) должно работать нормально даже если он содержит символы новой строки.

Итак, вы можете сделать

cat <<EOF if (( EOF for name in x y; do cat << EOF ( \$${name}_E != 0 && \$${name}_E != 1 ) || EOF done cat <<EOF 0 )) ; then echo do something fi EOF

, производя

if (( ( $x_E != 0 && $x_E != 1 ) || ( $y_E != 0 && $y_E != 1 ) || 0 )) ; then echo do something fi

Построение его в цикле все же делает уродливым. Конечно, || 0, поэтому нам не нужен специальный случай для первой или последней строки.

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

exec 3> >(sudo tee outputfile &>/dev/null) echo output to the pipe like this >&3 echo etc. >&3 exec 3>&- # close it in the end

И, честно говоря, я все еще подумайте, что создание имен переменных из переменных во внешнем скрипте (например, \$${name}_E) является немного уродливым, и его, вероятно, следует заменить на ассоциативный массив.

1
ответ дан 24 July 2018 в 17:31

Я рекомендую вам попробовать другой подход. Вместо того, чтобы скомпилировать созданный скрипт из нескольких выражений эха и heredocs. Я предлагаю вам попробовать использовать один heredoc.

Вы можете использовать расширение переменных и подстановку команд внутри heredoc. Как и в этом примере:

#!/bin/bash cat <<EOF $USER $(uname) EOF

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

Также похоже, что вы забыли #! в начале ваших скриптов. Строка #! обязательна в любых правильно отформатированных сценариях. Попытка запустить скрипт без строки #! будет работать, только если вы вызовете его из оболочки, которая работает с плохо отформатированными сценариями, и даже в этом случае она может получить интерпретацию другой оболочки, чем вы предполагали.

6
ответ дан 24 July 2018 в 17:31
  • 1
    havent забыли #!, но мой блок кода - всего лишь один фрагмент из сценария 2000 строк;) Я не вижу прямо сейчас, как ваше предложение решает мою проблему, генерируя одну строку кода в цикле while ... – derHugo 30 November 2017 в 17:59
  • 2
    @derHugo Подстановка команды для части строки, где вам нужен цикл, является одним из способов сделать это. Другой способ - построить эту часть в переменной перед heredoc. Какой из двух наиболее читаемых зависит от длины кода, необходимого в цикле, а также от количества строк heredoc, которые у вас есть перед соответствующей строкой. – kasperd 30 November 2017 в 18:10
  • 3
    Подстановка @derHugo Command, вызывающая функцию оболочки, определенную ранее в скрипте, является третьим подходом, который может быть более читаемым, если вам требуется значительный объем кода для создания этой одной строки. – kasperd 30 November 2017 в 18:12
  • 4
    @derHugo: Обратите внимание, что: (1) Вы можете иметь цикл, который помещает все необходимые команды в переменную; (2) Вы можете использовать $( ...command...) внутри heredoc, чтобы вставить вывод этих команд в строку в этой точке heredoc; (3) У Bash есть переменные массива, которые вы можете расширить внутри, и использовать замены, чтобы добавить вещи в начале / конце, если это необходимо. Вместе они делают предложение kasperd более мощным (и ваш сценарий потенциально более читабельным). – psmears 30 November 2017 в 19:56
  • 5
    Я использую heredocs из-за своего поведения шаблонов (wysiwyg). Создание всего шаблона в другом месте и, по моему мнению, передача его в heredoc на самом деле не облегчает чтение / поддержку. – derHugo 21 December 2017 в 19:06
Есть ли способ, с помощью которого heredoc ведет себя подобно echo -ne без символа конца строки?

Короткий ответ заключается в том, что heredoc и herestrings просто построены таким образом, поэтому no - here-doc и herestring будут добавлять конечную новую строку всегда, и нет возможности или собственный способ ее отключить (возможно, будет в будущих релизах bash?).

Однако один из способов приблизиться к нему с помощью только bash - это прочитать, что heredoc дает с помощью стандартного метода while IFS= read -r, но добавить задержку и распечатать последнюю строку через printf без завершающей новой строки , Вот что можно сделать только с bash-only:

#!/usr/bin/env bash while true do IFS= read -r line || { printf "%s" "$delayed";break;} if [ -n "$delayed" ]; then printf "%s\n" "$delayed" fi delayed=$line done <<EOF one two EOF

То, что вы видите здесь, это обычный цикл while с подходом IFS= read -r line, однако каждая строка сохраняется в переменной и, таким образом, задерживается (поэтому мы пропускаем распечатать первую строку, сохранить ее и начать печатать только после чтения второй строки). Таким образом, мы можем захватить последнюю строку, и когда на следующей итерации read возвращается статус выхода 1, это когда мы печатаем последнюю строку без новой строки через printf. Подробный ? да. Но он работает:

$ ./strip_heredoc_newline.sh one two$

Обратите внимание, что символ приглашения $ выдвинут вперед, так как нет новой строки. В качестве бонуса это решение является портативным и POSIX-ish, поэтому оно должно работать и в ksh и dash.

$ dash ./strip_heredoc_newline.sh one two$
7
ответ дан 24 July 2018 в 17:31

Другие вопросы по тегам:

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