มีบางสิ่งที่คล้ายกับ echo -n ใน heredoc (EOF) หรือไม่?


12

ฉันกำลังเขียนสคริปต์โรงงานสคริปต์ขนาดใหญ่ที่สร้างสคริปต์บำรุงรักษามากมายสำหรับเซิร์ฟเวอร์ของฉัน

จนถึงตอนนี้ฉันเขียนบางบรรทัดที่ต้องเขียนเป็นบรรทัดเดียว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ไม่มีจุดสิ้นสุดของบรรทัดสัญลักษณ์?


1
หากคุณต้องการsudoสคริปต์ที่คุณทำผิดให้รันสคริปต์ทั้งหมดเป็นรูทแทน!
ของหวาน

1
ไม่เพราะมันกำลังทำสิ่งอื่น ๆ ที่ฉันไม่ต้องการเรียกใช้ในฐานะ root
derHugo


ปัญหาในการทำสิ่งที่ฉันทำคืออะไร?
derHugo

4
แน่นอนว่ามีปัญหา: สคริปต์ที่sudoไม่มีชื่อผู้ใช้จะขอรหัสผ่านเว้นแต่ว่าคุณพิมพ์ไว้ว่ามีความล่าช้า มันยิ่งแย่กว่านี้หากคุณไม่ได้เรียกใช้สคริปต์ในเทอร์มินัลคุณจะไม่สามารถมองเห็นการสืบค้นรหัสผ่านและมันจะไม่ทำงาน sudo -uวิธีการไม่ได้มีปัญหาเหล่านี้
ของหวาน

คำตอบ:


9

ไม่ heredoc มักจะสิ้นสุดในการขึ้นบรรทัดใหม่ แต่คุณยังสามารถลบ newline สุดท้ายได้ตัวอย่างเช่นด้วย Perl:

cat << 'EOF' | perl -pe 'chomp if eof'
First line
Second line
EOF
  • -pอ่านบรรทัดอินพุตต่อบรรทัดเรียกใช้โค้ดที่ระบุใน-eและพิมพ์บรรทัด (อาจแก้ไข)
  • chompลบ newline สุดท้ายหากมีอยู่eofจะคืนค่าเป็นจริงเมื่อสิ้นสุดไฟล์

1
@Yaron ถ้าพบจุดสิ้นสุดของไฟล์ (ในกรณีนี้ปิดไปป์) มันจะส่งเสียงขึ้นบรรทัดใหม่จากบรรทัดสุดท้าย (ซึ่งเป็นสิ่งที่ heredoc และ herestring เพิ่มโดยอัตโนมัติ)
Sergiy Kolodyazhnyy

7

มีวิธีการที่ heredoc ทำงานคล้ายกับ echo -ne โดยไม่มีสัญลักษณ์สิ้นสุดบรรทัดหรือไม่?

คำตอบสั้น ๆ คือ heredoc และ herestrings เพิ่งถูกสร้างขึ้นมาดังนั้นไม่ใช่ - here-doc และ herestring จะเพิ่มบรรทัดใหม่ต่อท้ายเสมอและไม่มีตัวเลือกหรือวิธีดั้งเดิมในการปิดการใช้งาน (อาจจะมีใน 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

สิ่งที่คุณเห็นที่นี่เป็นเรื่องปกติในขณะที่วนรอบด้วยIFS= read -r lineวิธีอย่างไรก็ตามแต่ละบรรทัดจะถูกเก็บไว้ในตัวแปรและทำให้ล่าช้า (ดังนั้นเราจึงข้ามการพิมพ์บรรทัดแรกเก็บไว้และเริ่มพิมพ์เมื่อเราอ่านบรรทัดที่สองเท่านั้น) วิธีการที่เราสามารถจับบรรทัดสุดท้ายและเมื่อทวนต่อไปreadผลตอบแทนออกจากสถานะ 1 printfที่เมื่อเราพิมพ์บรรทัดสุดท้ายโดยไม่ต้องขึ้นบรรทัดใหม่ผ่านทาง verbose ใช่. แต่มันใช้งานได้:

$ ./strip_heredoc_newline.sh                                      
one
two$ 

สังเกตุว่า$ตัวละครนั้นถูกผลักไปข้างหน้าเนื่องจากไม่มีบรรทัดใหม่ เป็นโบนัสโซลูชั่นนี้เป็นแบบพกพาและ POSIX-ish ดังนั้นจึงควรทำงานในkshและdashเช่นกัน

$ dash ./strip_heredoc_newline.sh                                 
one
two$

6

ฉันแนะนำให้คุณลองใช้วิธีอื่น แทนที่จะรวมสคริปต์ที่คุณสร้างจากคำสั่ง echo และ heredocs เข้าด้วยกัน ฉันแนะนำให้คุณลองใช้ heredoc ตัวเดียว

คุณสามารถใช้การขยายตัวแปรและการทดแทนคำสั่งภายใน heredoc เหมือนในตัวอย่างนี้:

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

ฉันพบว่าสิ่งนี้มักจะนำไปสู่ผลลัพธ์ที่อ่านได้มากกว่าการผลิตชิ้นงานออกเป็นชิ้น ๆ

ดูเหมือนว่าคุณลืม#!บรรทัดในตอนต้นสคริปต์ของคุณ #!บรรทัดมีผลบังคับใช้ในสคริปต์ใด ๆ รูปแบบถูกต้อง ความพยายามในการเรียกใช้สคริปต์โดยไม่มี#!บรรทัดจะใช้งานได้ก็ต่อเมื่อคุณเรียกใช้จากเชลล์ที่ทำงานกับสคริปต์ที่จัดรูปแบบไม่ดีและในกรณีนี้อาจสิ้นสุดการตีความโดยเชลล์ที่แตกต่างจากที่คุณตั้งใจไว้


havent ลืม#!แต่การป้องกันรหัสของฉันเป็นเพียงหนึ่งตัวอย่างจากสคริปต์ 2000 สาย;) ผมไม่เห็นในขณะนี้ว่าคำแนะนำของคุณแก้ที่ก่อให้เกิดปัญหาของฉันหนึ่งบรรทัดของรหัสในขณะที่วง ...
derHugo

การทดแทนคำสั่ง @derHugo สำหรับส่วนของบรรทัดที่คุณต้องการวนซ้ำเป็นวิธีหนึ่งในการทำเช่นนั้น อีกวิธีคือสร้างส่วนนั้นในตัวแปรก่อน heredoc ข้อใดที่ทั้งสองสามารถอ่านได้มากที่สุดขึ้นอยู่กับความยาวของรหัสที่จำเป็นในการวนซ้ำรวมถึงจำนวน heredoc ที่คุณมีก่อนหน้าบรรทัดที่มีปัญหา
kasperd

การทดแทนคำสั่ง @derHugo ที่เรียกใช้ฟังก์ชั่นเชลล์ที่กำหนดไว้ก่อนหน้าในสคริปต์เป็นวิธีที่สามซึ่งอาจอ่านได้มากขึ้นถ้าคุณต้องการรหัสจำนวนมากเพื่อสร้างหนึ่งบรรทัด
kasperd

@derHugo: โปรดทราบว่า: (1) คุณสามารถมีลูปที่ใส่คำสั่งที่จำเป็นทั้งหมดลงในตัวแปร (2) คุณสามารถใช้$( ...command...)ภายใน heredoc เพื่อแทรกผลลัพธ์ของคำสั่งเหล่านั้นแบบอินไลน์ที่จุดนั้นใน heredoc; (3) Bash มีตัวแปรอาเรย์ซึ่งคุณสามารถขยายอินไลน์และใช้การแทนที่เพื่อเพิ่มสิ่งต่าง ๆ เมื่อเริ่มต้น / สิ้นสุดหากจำเป็น สิ่งเหล่านี้ทำให้การแนะนำของ kasperd มีประสิทธิภาพยิ่งขึ้น (และสคริปต์ของคุณอาจอ่านได้มากขึ้น)
psmears

ฉันกำลังใช้ heredocs อยู่เพราะพฤติกรรมแม่แบบ (wysiwyg) การผลิตเทมเพลตทั้งหมดในที่อื่นและส่งผ่านไปยัง heredoc ในความคิดของฉันไม่ได้ทำให้ง่ายต่อการอ่าน / ดูแล
derHugo

2

Heredocs ลงท้ายด้วยอักขระขึ้นบรรทัดใหม่เสมอ แต่คุณสามารถลบได้ วิธีที่ง่ายที่สุดที่ฉันรู้ก็คือheadโปรแกรม:

head -c -1 <<EOF | sudo tee file > /dev/null
my
heredoc
content
EOF

เยี่ยมฉันไม่ทราบว่า-cยังใช้ค่าลบ! เช่นนั้นยิ่งกว่าpearlวิธีแก้ปัญหา
derHugo

1

คุณสามารถลบบรรทัดใหม่ในรูปแบบใดก็ได้ตามต้องการท่อไปที่trอาจจะง่ายที่สุด ซึ่งจะเป็นการลบบรรทัดใหม่ทั้งหมดแน่นอน

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

แต่ถ้าคำสั่งที่คุณสร้างไม่จำเป็นต้องใช้นิพจน์ทางคณิตศาสตร์(( .. ))ควรทำงานได้ดีแม้ว่าจะมีบรรทัดใหม่

ดังนั้นคุณสามารถทำได้

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) เป็นบิตที่น่าเกลียดและมันอาจจะถูกแทนที่ด้วยอาเรย์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.