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

9

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

До сих пор я пишу строки, которые должны быть записаны в одной строке с 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 без символа конца строки?

    
задан derHugo 30.11.2017 в 08:44
источник

5 ответов

9

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

cat << 'EOF' | perl -pe 'chomp if eof'
First line
Second line
EOF
  • -p читает ввод строки за строкой, запускает код, указанный в -e , и печатает (возможно, измененную) строку
  • chomp удаляет окончательную новую строку, если она существует, eof возвращает true в конце файла.
ответ дан choroba 30.11.2017 в 08:48
7
  

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

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

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

#!/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$
    
ответ дан Sergiy Kolodyazhnyy 30.11.2017 в 09:40
6

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

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

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

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

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

    
ответ дан kasperd 30.11.2017 в 12:40
1

Вы могли бы удалить новые строки любым способом, который, как вам нравится, может быть простейшим способом для соединения с 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 ) немного уродливы, и его, вероятно, следует заменить на associative array .     

ответ дан ilkkachu 30.11.2017 в 14:03
1

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

head -c -1 <<EOF | sudo tee file > /dev/null
my
heredoc
content
EOF
    
ответ дан David Foerster 21.12.2017 в 14:33