Есть ли что-то похожее на 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 без символа конца строки?

11
задан 30 November 2017 в 12:14

5 ответов

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

cat << 'EOF' | perl -pe 'chomp if eof'
First line
Second line
EOF
  • -p читает строку ввода построчно, запускает код, указанный в -e, и печатает (возможно, изменено) line
  • chomp удаляет последний перевод строки, если он существует, eof возвращает true в конце файла.
9
ответ дан 30 November 2017 в 12:14
  • 1
    Ya, имеет смысл. Только, кажется, нет большого количества документации относительно интерфейсов в сети. Я пропустил его где-нибудь? – surgemcgee 22 May 2012 в 23:11
  • 2
    Ya, имеет смысл. Только, кажется, нет большого количества документации относительно интерфейсов в сети. Я пропустил его где-нибудь? – surgemcgee 22 May 2012 в 23:11
  • 3
    Ya, имеет смысл. Только, кажется, нет большого количества документации относительно интерфейсов в сети. Я пропустил его где-нибудь? – surgemcgee 22 May 2012 в 23:11
  • 4
    Ya, имеет смысл. Только, кажется, нет большого количества документации относительно интерфейсов в сети. Я пропустил его где-нибудь? – surgemcgee 22 May 2012 в 23:11
  • 5
    Ya, имеет смысл. Только, кажется, нет большого количества документации относительно интерфейсов в сети. Я пропустил его где-нибудь? – surgemcgee 22 May 2012 в 23:11
  • 6
    Ya, имеет смысл. Только, кажется, нет большого количества документации относительно интерфейсов в сети. Я пропустил его где-нибудь? – surgemcgee 22 May 2012 в 23:11

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

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

Однако, один из способов приблизиться к нему через bash-only, это прочитать то, что дает 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-ишем, поэтому оно должно работать также в ksh и dash.

$ dash ./strip_heredoc_newline.sh                                 
one
two$
7
ответ дан 30 November 2017 в 12:14

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

head -c -1 <<EOF | sudo tee file > /dev/null
my
heredoc
content
EOF
2
ответ дан 30 November 2017 в 12:14

Вы можете удалить переводы строк любым удобным для вас способом, возможно, проще всего подать трубку к 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
ответ дан 30 November 2017 в 12:14

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

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

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

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

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

6
ответ дан 30 November 2017 в 12:14

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

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