Piszę na ogromnym skrypcie fabrycznym, generującym wiele skryptów konserwacyjnych dla moich serwerów.
Do tej pory piszę kilka wierszy, które należy zapisać w jednym wierszu echo -ne
np
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
generuje to (jeśli w moim pliku konfiguracyjnym są np. 2 serwery) kod podobny do
if (( ($server1_E != 0 && $server1_E != 1) || ($server2_E != 0 && $server2_E != 1) ))
Wszystkie inne bloki kodu, które nie wymagają tego wbudowanego pisania kodu, tworzę z heredocami, ponieważ uważam, że są one o wiele lepsze do pisania i zarządzania. Np. Po górnym kodzie mam resztę wygenerowaną jak
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
Tak wygląda ostatni blok kodu
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
Moje pytanie
Czy istnieje sposób, aby heredok zachowywał się podobnie jak echo -ne
bez symbolu końca linii?
sudo
skryptu, robisz to źle: Zamiast tego uruchom cały skrypt jako root!sudo -u USERNAME
na który zamiast zobacz Jak uruchomić polecenie „sudo” wewnątrz skryptu? .sudo
bez nazwy użytkownika prosi o hasło, chyba że je wpiszesz. Gorzej, jeśli nie uruchomisz skryptu w terminalu, wtedy nawet nie zobaczysz zapytania o hasło i po prostu nie zadziała.sudo -u
Podejście nie ma żadnego z tych problemów.Odpowiedzi:
Nie, heredoc zawsze kończy się nową linią. Ale nadal możesz usunąć ostatnią nową linię, na przykład za pomocą Perla:
-p
odczytuje wejściowy wiersz po wierszu, uruchamia kod określony w-e
i drukuje (ewentualnie zmodyfikowaną) linięźródło
Krótka odpowiedź jest taka, że heredoc i tustrings są właśnie budowane w ten sposób, więc nie - tutaj-doc i tustring będą zawsze dodawać końcowy znak nowej linii i nie ma żadnej opcji ani natywnego sposobu, aby go wyłączyć (być może będą w przyszłych wydaniach bash?).
Jednak jednym ze sposobów, aby podejść do tego tylko za pomocą bash, byłoby odczytanie tego, co heredoc daje standardową
while IFS= read -r
metodą, ale dodanie opóźnienia i wydrukowanie ostatniej liniiprintf
bez końca nowej linii. Oto, co można zrobić z bash-only:To, co tu widzisz, jest zwykłą pętlą while z
IFS= read -r line
podejściem, jednak każda linia jest przechowywana w zmiennej, a zatem opóźniona (więc pomijamy drukowanie pierwszej linii, przechowujemy ją i rozpoczynamy drukowanie dopiero po przeczytaniu drugiej linii). W ten sposób możemy przechwycić ostatnią linię, a kiedy przy następnej iteracjiread
zwraca status wyjścia 1, wtedy drukujemy ostatnią linię bez nowej linii przezprintf
. Pełne? tak. Ale to działa:Zauważ, że
$
znak zachęty jest przesuwany do przodu, ponieważ nie ma nowego wiersza. Jako bonus, to rozwiązanie jest przenośny i POSIX-owski, więc powinien działać naksh
idash
tak dobrze.źródło
Polecam spróbować innego podejścia. Zamiast składać wygenerowany skrypt z wielu instrukcji echa i heredoków. Sugeruję, aby spróbować użyć jednego heredoc.
Możesz użyć rozszerzania zmiennych i podstawiania poleceń w heredoc. Jak w tym przykładzie:
Uważam, że często prowadzi to do znacznie bardziej czytelnych rezultatów końcowych niż wytwarzanie wyników kawałek po kawałku.
Wygląda na to, że zapomniałeś
#!
linii na początku swoich skryptów.#!
Linia jest obowiązkowe w każdym poprawnie sformatowanych skryptów. Próba uruchomienia skryptu bez#!
wiersza będzie działać tylko wtedy, gdy wywołasz go z powłoki, która działa wokół źle sformatowanych skryptów, a nawet w takim przypadku może zostać zinterpretowany przez inną powłokę niż zamierzałeś.źródło
#!
ale mój blok kodu to tylko jeden fragment skryptu z 2000 wierszy;) Nie rozumiem teraz, w jaki sposób twoja sugestia rozwiązuje mój problem z generowaniem jednego wiersza kodu w pętli while ...$( ...command...)
wewnątrz heredoc, aby wstawić wynik tych poleceń inline w tym punkcie heredoc; (3) Bash ma zmienne tablicowe, które można rozwinąć w linii i użyć podstawień, aby dodać rzeczy na początku / końcu, jeśli to konieczne. Razem sprawiają, że sugestia kasperda jest o wiele silniejsza (a twój skrypt potencjalnie o wiele bardziej czytelny).Heredoki zawsze kończą się znakami nowej linii, ale możesz to po prostu usunąć. Najłatwiejszy sposób, jaki znam, to
head
program:źródło
-c
także przyjmuje wartości ujemne! Tak to nawet bardziej niżpearl
rozwiązanieMożesz usunąć nowe wiersze w dowolny sposób, przesyłanie potoków do
tr
byłoby prawdopodobnie najprostsze. Oczywiście usunęłoby to wszystkie nowe wiersze.Ale budowana instrukcja if nie wymaga tego, wyrażenie arytmetyczne
(( .. ))
powinno działać dobrze, nawet jeśli zawiera znaki nowej linii.Więc możesz zrobić
produkować
Jednak budowanie go w pętli wciąż jest brzydkie.
|| 0
Tam jest oczywiście tak, że nie trzeba specjalnego przypadku pierwszej lub ostatniej linii.Masz to również
| sudo tee ...
na każdym wyjściu. Myślę, że moglibyśmy się tego pozbyć, otwierając przekierowanie tylko raz (z podstawieniem procesu) i używając go do późniejszego drukowania:I szczerze mówiąc, nadal uważam, że budowanie nazw zmiennych ze zmiennych w zewnętrznym skrypcie (jak ten
\$${name}_E
) jest nieco brzydkie i prawdopodobnie powinno zostać zastąpione tablicą asocjacyjną .źródło