วิธีต่อท้ายบรรทัดท้ายไฟล์ถ้ายังไม่มีเท่านั้น?


10

ฉันต้องการแก้ไขไฟล์ในสถานที่โดยต่อท้ายบรรทัดเฉพาะในกรณีที่ยังไม่มีหลักฐานของสคริปต์ของฉัน

ปกติฉันจะทำสิ่งที่ชอบ:

cat >> ~/.bashrc <<EOF
export PATH=~/.composer/vendor/bin:\$PATH
EOF

นอกจากนี้ยังเป็นไปได้ที่จะทำผ่าน ansible ( line+ insertafter=EOF+ regexp) แต่เป็นอีกเรื่องหนึ่ง

ใน vi / ex ฉันสามารถทำสิ่งที่ชอบ:

ex +'$s@$@\rexport PATH=\~/.composer/vendor/bin:$PATH@' -cwq ~/.bashrc

แต่แล้วฉันจะตรวจสอบได้อย่างไรว่ามีบรรทัดนั้นอยู่แล้ว (และไม่ทำอะไรเลย) โดยสมบูรณ์โดยไม่ต้องทำซ้ำบรรทัดเดิม?

หรืออาจจะมีวิธีที่ง่ายกว่าในการแก้ไขใน Ex?


คุณไม่สามารถให้บริการภายนอกหรือไม่ grep -Fq 'export PATH=~/.composer/vendor/bin:$PATH' ~/.bashrc || ex ...(หรือcatสำหรับเรื่องนั้น)?
muru

ฉันเชื่อว่านี่อาจเป็นสิ่งที่คล้ายกับวิธีการนี้แต่ตรงกันข้าม (เมื่อไม่มีสตริงให้ทำอะไร)
kenorb

ex ~/.bashrc -c "if search('export PATH=\~\/.composer\/vendor\/bin:\$PATH')>0 | norm quit | endif | norm Aexport PATH=~/.composer/vendor/bin:$PATH"
Alex Kroll

1
โปรดทราบว่านั่นexportคือคำสั่งดังนั้นส่วนที่เหลือของบรรทัดคือคำของเชลล์ไม่ใช่การกำหนด ดังนั้นจึงแตกต่างจากที่มีการกำหนดตัวแปร (ที่ไม่ได้ใช้export) ที่คุณต้องการคำพูดคู่หรือมันจะทำลายในช่องว่าง ยังเห็นวิธีการอย่างถูกต้องเพิ่มเส้นทางไปยังเส้นทาง
Wildcard

1
ฉันพบลิงค์จริงที่ฉันค้นหาเมื่อวานนี้ (แม้ว่าลิงก์ด้านบนก็ดีเช่นกัน) จำเป็นต้องใช้เครื่องหมายคำพูดสำหรับการกำหนดตัวแปรท้องถิ่นหรือไม่?
Wildcard

คำตอบ:


6

หากคุณต้องการระบบกันกระสุนคุณอาจต้องการบางสิ่งที่พกพาได้มากกว่าตัวเลือกที่ซับซ้อนด้านบน ฉันจะใช้คุณสมบัติ POSIX ของexและทำให้มันง่าย: เพียงลบบรรทัดถ้ามันมี (ไม่ว่าจะมีกี่ครั้ง) เพิ่มไปที่ท้ายไฟล์จากนั้นบันทึกและออก:

ex -sc 'g/^export PATH=\~\/\.composer\/vendor\/bin:\$PATH$/d
$a
export PATH=~/.composer/vendor/bin:$PATH
.
x' ~/.bashrc

ฉันทอดสมอ regex เพื่อความทนทานที่เพิ่มขึ้นแม้ว่าฉันคิดว่ามันเป็นไปไม่ได้เกือบที่จะจับคู่ regex นี้โดยไม่ได้ตั้งใจ (จุดยึดหมายถึงการใช้^และ$ดังนั้น regex จะจับคู่ก็ต่อเมื่อตรงกับทั้งบรรทัด)

โปรดทราบว่า+cmdPOSIX ไม่จำเป็นต้องใช้ไวยากรณ์และเป็นคุณสมบัติทั่วไปในการอนุญาตให้มี-cอาร์กิวเมนต์หลายตัว


มีวิธีง่ายๆ มากมายในการทำเช่นนี้ ตัวอย่างเช่นเพิ่มบรรทัดไปยังจุดสิ้นสุดของไฟล์จากนั้นรันสองบรรทัดสุดท้ายของไฟล์ผ่านตัวกรอง UNIX ภายนอกuniq:

ex -sc '$a
export PATH=~/.composer/vendor/bin:$PATH
.
$-,$!uniq
x' input

มันสะอาดและเรียบง่ายมากและยังรองรับ POSIX ได้อย่างสมบูรณ์ มันใช้คุณสมบัติ Escape ของexสำหรับการกรองข้อความภายนอกและยูทิลิตี้เชลล์ที่ระบุ POSIXuniq

บรรทัดล่างคือexถูกออกแบบมาสำหรับการแก้ไขไฟล์ในสถานที่และเป็นวิธีที่พกพาได้มากที่สุดเพื่อให้บรรลุในแบบไม่โต้ตอบ มันมีมาก่อนต้นฉบับviจริง ๆ แล้ว - เป็นชื่อviสำหรับ "โปรแกรมแก้ไขภาพ" และโปรแกรมแก้ไขที่ไม่ได้เป็นภาพที่สร้างขึ้นคือ ex )


เพื่อความเรียบง่ายยิ่งขึ้น (และเพื่อลดให้เหลือเพียงบรรทัดเดียว) ใช้printfเพื่อส่งคำสั่งไปที่ex:

printf %s\\n '$a' 'export PATH=~/.composer/vendor/bin:$PATH' . '$-,$!uniq' x | ex input


2

ทางออกที่เป็นไปได้คือการสร้างฟังก์ชั่นให้ทำ:

function! AddLine()

    let l:res = 0
    g/export PATH=\~\/.composer\/vendor\/bin:\\\$PATH/let l:res = l:res+1

    if (l:res == 0)
        normal G
        normal oexport PATH=~/.composer/vendor/bin:\$PATH
    endif

endfunction

ครั้งแรกที่เราสร้างตัวแปรท้องถิ่นl:resซึ่งจะใช้ในการนับจำนวนที่เกิดขึ้นของสาย

เราใช้globalคำสั่ง: ทุกครั้งที่มีการจับคู่บรรทัดl:resจะเพิ่มขึ้น

ในที่สุดถ้าl:resยังคงเป็น 0 หมายความว่าบรรทัดไม่ปรากฏในไฟล์ดังนั้นเราจึงไปที่บรรทัดสุดท้ายด้วยGและเราสร้างบรรทัดใหม่oที่เราเขียนบรรทัดเพื่อเพิ่ม

หมายเหตุ 1วิธีที่ฉันใช้ในการตั้งค่าของl:resหนักเล็กน้อยและสามารถถูกแทนที่ด้วย @Alex ที่แนะนำในคำตอบของเขาด้วยสิ่งที่ชอบ:

let l:res = search('export PATH=\~\/.composer\/vendor\/bin:\\\$PATH')

หมายเหตุ 2นอกจากนี้เพื่อให้ฟังก์ชันยืดหยุ่นมากขึ้นคุณสามารถใช้อาร์กิวเมนต์:

function! AddLine(line)

    let l:line =  escape(a:line, "/~$" )

    let l:res = 0
    exe "g/" . l:line . "/let l:res = l:res+1"

    if (l:res == 0)
        normal G
        exe "normal o" . a:line
    endif

endfunction
  • จดบันทึกเคล็ดลับด้วยescape()เพื่อเพิ่ม\อักขระด้านหน้าซึ่งอาจทำให้เกิดปัญหาในการค้นหา
  • โปรดทราบว่าคุณยังต้องหลบหนี\ด้วยตัวเองเมื่อเรียกใช้ฟังก์ชัน (ฉันไม่ได้จัดการ\โดยอัตโนมัติเพื่อหลบหนี):

    call AddLine("export PATH=~/.composer/vendor/bin:\\$PATH")
    

2

ลอง:

ex ~/.bashrc -c "if search('export PATH=\~\/.composer\/vendor\/bin:\$PATH')>0 | norm quit | endif | norm Aexport PATH=~/.composer/vendor/bin:$PATH"

โปรดทราบว่า:appendจะไม่ทำงานภายในif endif-block ด้วยโหมด Ex (ดูVimDoc ) ดังนั้นฉันต้องออกไปnorm A...ข้างนอก



1

เพื่อที่จะลดความซับซ้อนและหลีกเลี่ยงการปรากฏตัวของหลายสายที่จะเพิ่มจะเป็น\export PATH=mybin:PATH

กลยุทธ์หลัก: แทนที่mybinข้อความ " non- " (ทุกไฟล์) ด้วย text + mybin-path-definition:

 vim  +'s#\v%^((.|\n)(mybin:)@!)*%$#&\rexport PATH=mybin:PATH#e' -cx bashrc

หรือเยื้องการสอนมากขึ้น :

s#                                    substitute
   \v                                  very magical to redude \

   %^                                  file begin
   ((.|\n)(mybin:)@!)*                 multi-line str without "mybin:"
                                         (any char not followed by "mybin")*
   %$                                  end of file
 #                                    by

หากเรามีการแข่งขัน:

  1. &มีข้อความเต็มbashrcและ
  2. & ไม่มี /mybin:/

ดังนั้น:

 #                                    by ...
   &                                   all file
   export PATH=mybin:PATH              and the new path
 #e                                   don't complain if patt not found

-cx                                   save and leave 

ปรับปรุง : ในที่สุดเราสามารถสร้างสคริปต์เป็นกลุ่ม:

$ cat bashrcfix

#!/usr/bin/vim -s
:e ~/fakebashrc
:s#\v%^((.|\n)(mybin:)@!)*%$#&\rexport PATH=mybin:PATH#e
:x

chmod 755 และติดตั้ง การใช้งาน:bashrcfix


ระวัง: ใน\vโหมด=หมายถึงตัวเลือก


1

คำตอบส่วนใหญ่ดูเหมือนจะซับซ้อนว่าคำสั่งเชลล์เก่าดีเพียงใด:

grep composer/vender ~/.bashrc || cat >> ~/.bashrc <<EOF
export PATH=~/.composer/vendor/bin:\$PATH
EOF

นี่อาจเป็นฟังก์ชัน Bash ได้อย่างง่ายดาย:

addBashrcPath() {
  if ! grep "$1" ~/.bashrc >/dev/null 2>&1; then
    printf 'export PATH=%s:$PATH' "$1" >> ~/.bashrc
  fi
}

addBashrcPath "~/.composer/vendor/bin"

แก้ไข : รหัสทางออกของ grep เป็นสิ่งสำคัญและในกรณีนี้จะต้องมีการใช้ตรงกันข้าม! grep; grep -vจะไม่ออกตามที่คาดไว้ในสถานการณ์นี้ ขอบคุณ @joeytwiddle สำหรับคำแนะนำ


ฉันไม่คิดว่าgrep -vจะให้รหัสทางออกที่คุณต้องการ แต่! grepควรทำอย่างไร
joeytwiddle

หากเป็นกรณีนี้แล้วคุณไม่จำเป็นต้องเป็นเพียงแค่-v !
Sukima

เผง คุณสามารถแก้ไขคำตอบเพื่อทำการแก้ไขได้
joeytwiddle

1
1. คุณควรจะใช้-Fเพื่อให้grepdosn't ตีความตัวละครเป็นนิพจน์ปกติและ 2. ใช้แทนการเปลี่ยนเส้นทางไปยัง-q /dev/nullฉันในการแสดงความคิดเห็นก่อนหน้านี้เกี่ยวกับคำถาม
muru

จุดที่ดี อยากรู้ว่าอุปกรณ์พกพา-qและ-Fตัวเลือกต่าง ๆ เป็นอย่างไร? ฉันคิดว่า>/dev/nullมันเป็นสากลมากขึ้นในแพลตฟอร์มที่แตกต่างกัน (sun vs hp กับ Mac OS X เทียบกับ Ubuntu เทียบกับ FreeBSD เทียบกับ…)
Sukima
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.