ฉันจะกระจายเอาต์พุตของสองคำสั่งได้อย่างไร


165

ฉันจินตนาการถึงวิธีที่ง่ายที่สุดในการเปรียบเทียบเนื้อหาของสองไดเรกทอรีที่คล้ายกันนั้นจะเป็นอย่างไร

diff `ls old` `ls new`

แต่ฉันเห็นว่าทำไมสิ่งนี้ไม่ทำงาน diffกำลังส่งรายชื่อไฟล์จำนวนมากในบรรทัดคำสั่งแทนที่จะเป็นสตรีมสองรายการอย่างที่ฉันหวังไว้ ฉันจะส่งผ่านเอาต์พุตทั้งสองไปยัง diff โดยตรงได้อย่างไร


คำตอบ:


246

การทดแทนคำสั่งจะแทนที่`…`เอาต์พุตของคำสั่งลงในบรรทัดคำสั่งดังนั้นdiffจะเห็นรายการไฟล์ในทั้งสองไดเรกทอรีว่าเป็นอาร์กิวเมนต์ สิ่งที่คุณต้องการใช้diffเพื่อดูชื่อไฟล์สองชื่อบนบรรทัดคำสั่งและมีเนื้อหาของไฟล์เหล่านี้เป็นรายชื่อไดเรกทอรี นั่นคือสิ่งที่กระบวนการทดแทนทำ

diff <(ls old) <(ls new)

ข้อโต้แย้งที่diffจะมีลักษณะ/dev/fd/3และ/dev/fd/4: พวกเขาเป็นตัวอธิบายไฟล์ที่สอดคล้องกับสองท่อที่สร้างขึ้นโดยทุบตี เมื่อdiffเปิดไฟล์เหล่านี้มันจะเชื่อมต่อกับด้านอ่านของแต่ละไพพ์ ด้านการเขียนของแต่ละไพพ์เชื่อมต่อกับlsคำสั่ง


49
echo <(echo) <(echo)ไม่เคยคิดว่าสิ่งนี้จะน่าสนใจ: D
Aquarius Power

3
กระบวนการทดแทนจะไม่ได้รับการสนับสนุนจากเปลือกหอยทั้งหมดแต่การเปลี่ยนเส้นทางท่อเป็นวิธีแก้ปัญหาเรียบร้อย
Irfan434

1
เพียงพูดถึงว่าไม่แนะนำให้ใช้การแยกวิเคราะห์ ls unix.stackexchange.com/questions/128985/why-not-parse-ls
Katu

@Katu ปัญหาที่เกิดขึ้นlsคือมัน mangles ชื่อไฟล์ การแยกวิเคราะห์ผลลัพธ์มีความเปราะบาง (ไม่ทำงานกับชื่อไฟล์“ แปลก”) สำหรับการเปรียบเทียบรายชื่อสารบบสองรายการมันก็โอเคตราบใดที่ผลลัพธ์ไม่มีความชัดเจน --quoting-style=escapeที่มีชื่อไฟล์โดยพลการนี้จะต้องเป็นตัวเลือกเช่น
Gilles

1
@ <(…)จะสร้างไปป์ ดูเหมือนว่าเมลล์จะไม่ทำงานกับท่อดังนั้นคุณจึงไม่สามารถใช้งาน<(…)ได้ ใน zsh คุณสามารถแทนที่<(…)ด้วย=(…)และมันจะทำงานเพราะ=(…)ทำให้เอาท์พุทกลางในไฟล์ชั่วคราว ในทุบตีฉันไม่คิดว่ามีไวยากรณ์ที่สะดวกสบายคุณต้องจัดการไฟล์ชั่วคราวด้วยตัวเอง
Gilles

3

สำหรับ zsh การใช้=(command)จะสร้างไฟล์ชั่วคราวโดยอัตโนมัติและแทนที่=(command)ด้วยพา ธ ของไฟล์เอง ด้วยการแทนที่คำสั่ง$(command)จะถูกแทนที่ด้วยเอาต์พุตของคำสั่ง

ดังนั้นจึงมีสามตัวเลือก:

  1. การทดแทนคำสั่ง: $(...)
  2. การทดแทนกระบวนการ: <(...)
  3. การทดแทนกระบวนการ zsh-Flavored: =(...)

กระบวนการย่อยของ zsh flavored process # 3 มีประโยชน์มากและสามารถใช้เพื่อเปรียบเทียบผลลัพธ์ของสองคำสั่งโดยใช้เครื่องมือ diff เช่น Beyond Beyond:

bcomp  =(ulimit -Sa | sort) =(ulimit -Ha | sort)

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

อ่านเพิ่มเติมได้ที่นี่: http://zsh.sourceforge.net/Intro/intro_7.html

โปรดสังเกตสิ่งนี้ด้วย:

โปรดทราบว่าเชลล์สร้างไฟล์ชั่วคราวและลบออกเมื่อคำสั่งเสร็จสิ้น

และต่อไปนี้ซึ่งเป็นข้อแตกต่างระหว่างการทดแทนกระบวนการสองประเภทที่สนับสนุนโดย zsh (เช่น # 2 และ # 3):

หากคุณอ่าน man page ของ zsh คุณอาจสังเกตเห็นว่า <(... ) เป็นอีกรูปแบบหนึ่งของการทดแทนกระบวนการซึ่งคล้ายกับ = (... ) มีความแตกต่างที่สำคัญระหว่างทั้งสอง ในกรณี <(... ), เชลล์สร้างไปป์ที่มีชื่อ (FIFO) แทนไฟล์ สิ่งนี้ดีกว่าเนื่องจากไม่ได้เติมระบบไฟล์ให้เต็ม แต่มันไม่ทำงานในทุกกรณี อันที่จริงแล้วถ้าเราแทนที่ = (... ) ด้วย <(... ) ในตัวอย่างด้านบนพวกเขาทั้งหมดจะหยุดทำงานยกเว้น fgrep -f <(... ) คุณไม่สามารถแก้ไขไปป์หรือเปิดเป็นโฟลเดอร์เมลได้ อย่างไรก็ตาม fgrep ไม่มีปัญหาในการอ่านรายการคำจากไปป์ คุณอาจสงสัยว่าเหตุใดแถบ diff <(foo) จึงไม่ทำงานเนื่องจาก foo | งานต่างบาร์ นี่เป็นเพราะ diff สร้างไฟล์ชั่วคราวหากพบว่าข้อโต้แย้งข้อใดข้อหนึ่งของมันคือ - จากนั้นคัดลอกอินพุตมาตรฐานไปยังไฟล์ชั่วคราว

การอ้างอิง: https://unix.stackexchange.com/questions/393349/difference-between-subshells-and-process-substitution


2
$(...)ไม่ใช่การทดแทนกระบวนการ แต่เป็นการทดแทนคำสั่ง <(...)เป็นการทดแทนกระบวนการ นั่นเป็นเหตุผลที่ข้อความที่ยกมาไม่ได้กล่าวถึง$(...)เลย
muru

2

ปลาเปลือกหอย

ในเปลือกปลาที่คุณต้องเข้าไปในท่อpsub นี่คือตัวอย่างของการเปรียบเทียบการกำหนดค่าของ heroku และ dokku กับBeyond Compare :

bcompare (ssh me@myapp.pl dokku config myapp | sort | psub) (heroku config -a myapp | sort | psub)

1
เครื่องมือดิจิตัล diff อื่นmeldคือโอเพ่นซอร์สและมีอยู่ในที่เก็บ Ubuntu และ EPEL meldmerge.org
phiphi

0

ฉันมักจะใช้เทคนิคที่อธิบายไว้ในคำตอบที่ได้รับการยอมรับ:

diff <(ls old) <(ls new)

แต่ฉันพบว่าฉันมักจะใช้มันพร้อมกับคำสั่งที่ซับซ้อนกว่าตัวอย่างด้านบน ในกรณีเช่นนี้อาจทำให้รำคาญคำสั่ง diff ฉันคิดวิธีแก้ปัญหาที่คนอื่นเห็นว่ามีประโยชน์

ฉันพบว่า 99% ของเวลาที่ฉันลองใช้คำสั่งที่เกี่ยวข้องก่อนที่จะทำงานต่างกัน ดังนั้นคำสั่งที่ฉันต้องการจะแตกต่างกันในประวัติศาสตร์ของฉัน ... ทำไมไม่ใช้มัน?

ฉันใช้ประโยชน์จากทุบตีแก้ไขคำสั่ง (fc) เพื่อรันคำสั่งสองครั้งสุดท้าย:

$ echo A
A
$ echo B
B
$ diff --color <( $(fc -ln -1 -1) ) <( $(fc -ln -2 -2 ) )
1c1
< B
---
> A

ธง fc คือ:

-n : ไม่มีตัวเลข มันไม่แสดงหมายเลขคำสั่งเมื่อแสดงรายการ

-l : รายการ: คำสั่งถูกแสดงรายการในเอาต์พุตมาตรฐาน

การ-1 -1อ้างถึงจุดเริ่มต้นและจุดสิ้นสุดในประวัติศาสตร์ในกรณีนี้คือจากคำสั่งสุดท้ายไปยังคำสั่งสุดท้ายซึ่งให้ผลลัพธ์เพียงคำสั่งสุดท้าย

สุดท้ายเราก็สรุปสิ่งนี้ไว้ใน$()เพื่อเรียกใช้งานคำสั่งในเชลล์ย่อย

เห็นได้ชัดว่านี่เป็นความเจ็บปวดเล็กน้อยที่จะพิมพ์เพื่อให้เราสามารถสร้างนามแฝง:

alias dl='diff --color <( $(fc -ln -1 -1) ) <( $(fc -ln -2 -2 ) )'

หรือเราสามารถสร้างฟังก์ชั่น:

dl() {
    if [[ -z "$1" ]]; then
        first="1"
    else
        first="$1"
    fi
    if [[ -z "$2" ]]; then
        last="2"
    else
        last="$2"
    fi
    # shellcheck disable=SC2091
    diff --color <( $(fc -ln "-$first" "-$first") ) <( $(fc -ln "-$last" "-$last") )
}

ซึ่งรองรับการระบุบรรทัดประวัติที่จะใช้ หลังจากใช้ทั้งฉันพบว่านามแฝงเป็นรุ่นที่ฉันต้องการ

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