การต่อท้ายบรรทัดกับไฟล์ต่อเมื่อไม่มีอยู่


157

ฉันต้องเพิ่มบรรทัดต่อไปนี้ที่ส่วนท้ายของไฟล์ปรับแต่ง:

include "/configs/projectname.conf"

ไปยังไฟล์ที่เรียกว่า lighttpd.conf

ฉันกำลังมองหาที่sedจะใช้ในการทำเช่นนี้ แต่ฉันไม่สามารถหาวิธีได้

ฉันจะแทรกได้อย่างไรถ้าบรรทัดนั้นไม่มีอยู่?


หากคุณพยายามแก้ไขไฟล์ ini เครื่องมือcrudiniอาจเป็นตัวเลือกที่ดี (แต่ไม่ใช่สำหรับ lighthttpd)
rubo77

คำตอบ:


292

เพียงทำให้มันง่าย :)

grep + echoควรเพียงพอ:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar
  • -q เงียบสงบ
  • -x ตรงกับทั้งบรรทัด
  • -F pattern เป็นสตริงธรรมดา
  • https://linux.die.net/man/1/grep

แก้ไข: รวมคำแนะนำ @cerin และ @ thijs-woutersไว้


2
ฉันจะเพิ่มสวิตช์ -q เพื่อการส่งออก grep ปราบ: grep -vq ...
เดฟเคอร์บี้

1
FYI การใช้ -v และ && ดูเหมือนจะไม่หลอกลวงในขณะที่ || ไม่มี v-works
bPizzi

3
ใช้งานได้กับบรรทัดที่ง่ายมากเท่านั้น มิฉะนั้น grep ตีความอักขระที่ไม่ใช่ตัวอักษรส่วนใหญ่ในบรรทัดของคุณเป็นรูปแบบทำให้ไม่ตรวจพบบรรทัดในไฟล์
Cerin

2
ทางออกที่สวยงาม นอกจากนี้ยังใช้สำหรับกระตุ้นการแสดงออกที่ซับซ้อนมากขึ้นแน่นอน Mine ใช้echoเพื่อเรียกcatheredoc หลายอันลงในไฟล์ปรับแต่ง
Eric L.

2
เพิ่ม-sเพื่อละเว้นข้อผิดพลาดเมื่อไม่มีไฟล์สร้างไฟล์ใหม่ที่มีเพียงบรรทัดนั้น
แฟรงค์

78

นี่จะเป็นโซลูชันที่สะอาดอ่านได้และใช้ซ้ำได้โดยใช้grepและechoเพื่อเพิ่มบรรทัดลงในไฟล์ต่อเมื่อไม่มีอยู่:

LINE='include "/configs/projectname.conf"'
FILE='lighttpd.conf'
grep -qF -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"

หากคุณต้องการให้ตรงกับการใช้งานทั้งบรรทัด grep -xqF

เพิ่ม-sเพื่อละเว้นข้อผิดพลาดเมื่อไม่มีไฟล์สร้างไฟล์ใหม่ที่มีเพียงบรรทัดนั้น


5
ถ้าเป็นไฟล์ที่คุณต้องการสิทธิ์รูท:sudo grep -qF "$LINE" "$FILE" || echo "$LINE" | sudo tee -a "$FILE"
nebffa

-นี้ที่จริงไม่ทำงานเมื่อเส้นเริ่มต้นด้วย
ไฮเปอร์โมน

@zsero: จุดดี! ฉันเพิ่ม--ในคำสั่ง grep ดังนั้นมันจะไม่ตีความ $ LINE ที่เริ่มต้นด้วยเส้นประเป็นตัวเลือกอีกต่อไป
rubo77

13

นี่คือsedรุ่น:

sed -e '\|include "/configs/projectname.conf"|h; ${x;s/incl//;{g;t};a\' -e 'include "/configs/projectname.conf"' -e '}' file

หากสตริงของคุณอยู่ในตัวแปร:

string='include "/configs/projectname.conf"'
sed -e "\|$string|h; \${x;s|$string||;{g;t};a\\" -e "$string" -e "}" file

สำหรับคำสั่งที่แทนที่สตริงบางส่วนด้วยรายการไฟล์กำหนดค่าที่สมบูรณ์ / อัพเดทหรือต่อท้ายบรรทัดตามต้องการ:sed -i -e '\|session.*pam_mkhomedir.so|h; ${x;s/mkhomedir//;{g;tF};a\' -e 'session\trequired\tpam_mkhomedir.so umask=0022' -e '};:F;s/.*mkhomedir.*/session\trequired\tpam_mkhomedir.so umask=0022/g;' /etc/pam.d/common-session
bgStack15

เยี่ยมมากมันช่วยฉันได้มาก +1 โปรดอธิบายการแสดงออกที่เย่อหยิ่งของคุณได้ไหม
ราหุล

ฉันชอบที่จะเห็นคำอธิบายประกอบของการแก้ปัญหานี้ ฉันเคยใช้sedหลายปี แต่ส่วนใหญ่เป็นเพียงsคำสั่ง holdและpatternแลกเปลี่ยนพื้นที่อยู่นอกเหนือฉัน
nortally

11

ถ้าเขียนไปยังแฟ้มที่มีการป้องกันและ @drAlberT @ rubo77 's คำตอบอาจจะไม่ทำงานสำหรับคุณตั้งแต่หนึ่งไม่สามารถ >>sudo วิธีง่ายๆในทำนองเดียวกันแล้วจะใช้tee --append(หรือบน MacOS, tee -a):

LINE='include "/configs/projectname.conf"'
FILE=lighttpd.conf
grep -qF "$LINE" "$FILE"  || echo "$LINE" | sudo tee --append "$FILE"

6

ถ้าวันหนึ่งมีคนอื่นต้องจัดการกับรหัสนี้เป็น "รหัสดั้งเดิม" ดังนั้นบุคคลนั้นจะรู้สึกขอบคุณถ้าคุณเขียนรหัสที่น้อยกว่าเช่น

grep -q -F 'include "/configs/projectname.conf"' lighttpd.conf
if [ $? -ne 0 ]; then
  echo 'include "/configs/projectname.conf"' >> lighttpd.conf
fi

1
ขออภัยถ้าคุณไม่รู้จักเชลล์ให้ไปและจัดการ Windows โซลูชันที่ใช้ && และ || เป็นไวยากรณ์เชลล์ปกติและผู้ดูแลระบบควรเข้าใจ ไม่มีอะไรลึกลับเลย
Graham Nicholls

3
ขอบคุณสำหรับความคิดเห็นของคุณเกรแฮม! ใช่มันเป็นไวยากรณ์ของเชลล์ปกติ และใช่ผู้ดูแลระบบที่มีอำนาจควรเข้าใจ อย่างไรก็ตามมันทำงานได้โดยขึ้นอยู่กับผลข้างเคียงของอัลกอริทึมการประเมินผลนิพจน์ภายในเชลล์ ดังนั้นคุณสามารถเรียกมันได้ทุกอย่างที่คุณต้องการยกเว้นตรงไปตรงมา - ซึ่งเป็นจุดเริ่มต้นของฉัน
Marcelo Ventura


6

โซลูชัน sed อื่นคือการผนวกท้ายบรรทัดสุดท้ายและลบโซลูชันที่มีอยู่ก่อนเสมอ

sed -e '$a\' -e '<your-entry>' -e "/<your-entry-properly-escaped>/d"

"escaped อย่างถูกต้อง" หมายถึงการใส่ regex ที่ตรงกับรายการของคุณนั่นคือหนีการควบคุม regex ทั้งหมดจากรายการจริงของคุณเช่นใส่แบ็กสแลชต่อหน้า ^ $ / *? + ()

สิ่งนี้อาจล้มเหลวในบรรทัดสุดท้ายของไฟล์ของคุณหรือหากไม่มีการขึ้นบรรทัดใหม่ฉันไม่แน่ใจ แต่สามารถจัดการได้โดยการแยกสาขาที่ดี ...


1
ดีหนึ่งซับสั้นและง่ายต่อการอ่าน ใช้งานได้ดีสำหรับการอัพเดท etc / hosts:sed -i.bak -e '$a\' -e "$NEW_IP\t\t$HOST.domain\t$HOST" -e "/.*$HOST/d" /etc/hosts
Noam Manos

รุ่นที่เล็กกว่า:sed -i -e '/<entry>/d; $a <entry>'
Jérôme Pouiller

@Jezz ฉันคิดว่าสิ่งนี้จะล้มเหลวหากรายการอยู่ที่ท้ายไฟล์เนื่องจากdคำสั่งจะรีสตาร์ทโปรแกรม sed ดังนั้นจึงข้ามappend หากการลบเกิดขึ้นที่บรรทัดสุดท้าย
Robin479

@ Robin479 ไอ้append จะต้องมีการดำเนินการก่อนหนึ่งคำ:d sed -i -e '$a<entry>' -e '/<entry>/d'มันเกือบจะเหมือนกันกับคำตอบเดิมของคุณ
Jérôme Pouiller

3

ใช้ awk

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file file

มันเป็น 'ไฟล์ไฟล์' หรือเพียงแค่ 'ไฟล์'?
webdevguy

เป็นfile fileเพราะมันทำสองผ่านผ่านไฟล์เดียวกัน NR==FNRเป็นจริงในการผ่านครั้งแรก แต่ไม่ใช่ครั้งที่สอง นี่เป็นสำนวน Awk ทั่วไป
tripleee

1

คำตอบที่ใช้ grep นั้นผิด คุณต้องเพิ่มตัวเลือก -x เพื่อจับคู่ทั้งบรรทัดมิฉะนั้นบรรทัดที่ต้องการ#text to addจะยังคงตรงกันเมื่อต้องการเพิ่มtext to addจะยังคงตรงกับเมื่อมองหาเพื่อเพิ่มว่า

ดังนั้นวิธีแก้ไขปัญหาที่ถูกต้องคือ:

grep -qxF 'include "/configs/projectname.conf"' foo.bar || echo 'include "/configs/projectname.conf"' >> foo.bar

1

การใช้ sed:มันจะแทรกที่ท้ายบรรทัด คุณสามารถส่งผ่านตัวแปรได้ตามปกติ

grep -qxF "port=9033" $light.conf
if [ $? -ne 0 ]; then
  sed -i "$ a port=9033" $light.conf
else
    echo "port=9033 already added"
fi

ใช้ oneliner sed

grep -qxF "port=9033" $lightconf || sed -i "$ a port=9033" $lightconf

ใช้เสียงสะท้อนอาจไม่ทำงานภายใต้รูท แต่จะทำงานเช่นนี้ แต่จะไม่ยอมให้คุณทำสิ่งต่าง ๆ โดยอัตโนมัติหากคุณต้องการทำเพราะอาจขอรหัสผ่าน

ฉันมีปัญหาเมื่อฉันพยายามแก้ไขจากรูทสำหรับผู้ใช้เฉพาะ การเพิ่ม$usernameก่อนหน้าเป็นการแก้ไขสำหรับฉัน

grep -qxF "port=9033" light.conf
if [ $? -ne 0 ]; then
  sudo -u $user_name echo "port=9033" >> light.conf
else
    echo "already there"    
fi

ยินดีต้อนรับสู่ stackoverflow! โปรดอ่านคำถามอย่างรอบคอบก่อนโพสต์คำตอบ - OP ถามวิธีแทรกโดยใช้ sed
Markoorn

-1

ฉันต้องการที่จะแก้ไขไฟล์ที่มีสิทธิ์ในการเขียนที่ถูก จำกัด sudoจำเป็นเพื่อ ทำงานจากคำตอบของ ghostdog74 และใช้ไฟล์ temp:

awk 'FNR==NR && /configs.*projectname\.conf/{f=1;next}f==0;END{ if(!f) { print "your line"}} ' file > /tmp/file
sudo mv /tmp/file file

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