วิธีเลียนแบบการทดแทนกระบวนการใน Dash ได้อย่างไร


18

ในbashฉันสามารถใช้การทดแทนกระบวนการและปฏิบัติต่อผลลัพธ์ของกระบวนการราวกับว่ามันเป็นไฟล์ที่บันทึกไว้ในดิสก์:

$ echo <(ls)
/dev/fd/63

$ ls -lAhF <(ls)
lr-x------ 1 root root 64 Sep 17 12:55 /dev/fd/63 -> pipe:[1652825]

dashน่าเสียดายที่กระบวนการชดเชยไม่ได้รับการสนับสนุนใน

อะไรจะเป็นวิธีที่ดีที่สุดที่จะเลียนแบบได้อย่างรวดเร็วProcess Substitution?

ฉันไม่ต้องการบันทึกผลลัพธ์เป็นไฟล์ชั่วคราวที่ไหนสักแห่ง ( /tmp/) แล้วต้องลบมันออก มีวิธีอื่นไหม?


ขึ้นอยู่กับสิ่งที่คุณพยายามจะทำคุณอาจใช้ไพพ์ได้
Eric Renouf

จริงๆแล้วถ้าการพกพาไม่ใช่ประเด็นหลักของคุณที่นี่จะไม่ง่ายกว่าที่จะติดตั้งbashบนอุปกรณ์ของคุณ?
Arkadiusz Drabczyk

1
ตัวอย่างที่คุณให้ไว้ในการแจ้งเตือนค่าหัวนั้นเป็นหัวข้อของคำตอบที่เชื่อมโยงนี้ ดังที่แสดงไว้ที่นี่คำตอบของ Gilles ฉบับง่าย (สมมติว่ามี/dev/fdและใช้xz -cd <file>แทนcat <file> | xz -d) อาจเป็นxz -cd "$1" | { xz -cd "$2" | { diff /dev/fd/3 /dev/fd/4; } 3<&0; } 4<&0ได้
fra-san

1
@ fra-san - นั่นคือสิ่งที่ฉันต้องการ คุณสามารถทำให้คำตอบถ้าคุณต้องการ ขอบคุณ
Martin Vegter

คำตอบ:


4

คำถามในหนังสือแจ้งการรับรางวัลปัจจุบัน:

ตัวอย่างทั่วไปนั้นซับซ้อนเกินไป ใครบางคนช่วยอธิบายวิธีการใช้ตัวอย่างต่อไปนี้ได้ไหม?diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)

ดูเหมือนว่าจะมีคำตอบที่นี่

ดังที่แสดงในคำตอบของ Gillesแนวคิดทั่วไปคือการส่งเอาต์พุตของคำสั่ง "producer" ไปยังไฟล์อุปกรณ์ใหม่1ในระยะต่าง ๆ ของไปป์ไลน์ทำให้พวกเขาพร้อมใช้งานกับคำสั่ง "consumer" ซึ่งอาจใช้ชื่อไฟล์เป็นอาร์กิวเมนต์ ( สมมติว่าระบบของคุณอนุญาตให้คุณเข้าถึง file descriptors as /dev/fd/X)

วิธีที่ง่ายที่สุดในการบรรลุสิ่งที่คุณค้นหาน่าจะเป็น:

xz -cd file1.xz | { xz -cd file2.xz | diff /dev/fd/3 -; } 3<&0

(ใช้file1.xzแทน"$1"การอ่านได้และxz -cdแทนที่จะใช้cat ... | xz -dเพราะคำสั่งเดียวก็เพียงพอแล้ว)

เอาต์พุตของคำสั่ง "producer" แรกxz -cd file1.xzจะถูกไพพ์ไปยังคำสั่งผสม ( {...}); แต่แทนที่จะถูกบริโภคทันทีที่เข้ามาตรฐานของคำสั่งต่อไปก็คือการทำซ้ำเพื่ออธิบายไฟล์และทำให้สามารถเข้าถึงได้ทุกอย่างภายในคำสั่งผสมเป็นดังนั้น3 /dev/fd/3การส่งออกของคำสั่งที่สอง "ผู้ผลิต" ที่xz -cd file2.xzซึ่งไม่กินไม่เข้ามาตรฐานของมันมิได้อะไรจากไฟล์อธิบาย3เป็นแล้วประปาคำสั่ง "ผู้บริโภค" ซึ่งอ่านจากอินพุตมาตรฐานและจากdiff/dev/fd/3

การทำสำเนา Piping และ File Descriptor อาจถูกเพิ่มเพื่อให้ไฟล์อุปกรณ์สำหรับคำสั่ง "producer" ได้มากเท่าที่ต้องการเช่น:

xz -cd file1.xz | { xz -cd file2.xz | { diff /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0

แม้ว่ามันอาจจะไม่เกี่ยวข้องกับบริบทของคำถามเฉพาะของคุณ แต่ก็เป็นที่น่าสังเกตว่า:

  1. cmd1 <(cmd2) <(cmd3), cmd2 | { cmd3 | { cmd1 /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0และ( cmd2 | ( cmd3 | ( cmd1 /dev/fd/3 /dev/fd/4 ) 4<&0 ) 3<&0 )มีผลกระทบที่อาจเกิดขึ้นแตกต่างกันในสภาพแวดล้อมการดำเนินการเริ่มต้น

  2. ขัดกับสิ่งที่เกิดขึ้นในcmd1 <(cmd2) <(cmd3), cmd3และcmd1ในcmd2 | { cmd3 | { cmd1 /dev/fd/3 /dev/fd/4; } 4<&0; } 3<&0จะไม่สามารถที่จะอ่านป้อนข้อมูลใด ๆ จากผู้ใช้ ที่จะต้องมีตัวอธิบายไฟล์เพิ่มเติม ตัวอย่างเช่นเพื่อให้ตรงกับ

    diff <(echo foo) <(read var; echo "$var")
    

    คุณจะต้องการอะไรเช่น

    { echo foo | { read var 0<&9; echo "$var" | diff /dev/fd/3 -; } 3<&0; } 9<&0
    

1 เพิ่มเติมเกี่ยวกับพวกเขาสามารถพบได้บน U & L เช่นในการทำความเข้าใจ / dev และ subdirs และไฟล์


12

คุณสามารถทำซ้ำสิ่งที่เปลือกทำภายใต้ประทุนโดยทำท่อประปาด้วยตนเอง หากระบบของคุณมีรายการคุณสามารถใช้การสลับไฟล์อธิบาย: คุณสามารถแปล/dev/fd/NNN

main_command <(produce_arg1) <(produce_arg2) >(consume_arg3) >(consume_arg4)

ถึง

{ produce_arg1 |
  { produce_arg2 |
    { main_command /dev/fd5 /dev/fd6 /dev/fd3 /dev/fd4 </dev/fd/8 >/dev/fd/9; } 5<&0 3>&1 |
    consume_arg3; } 6<&0 4>&1; |
  consume_arg4; } 8<&0 9>&1

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

main_command <(produce_arg1)
produce_arg1 | main_command /dev/stdin

โดยไม่ต้องคุณจะต้องใช้ไปป์ที่มีชื่อ ไปป์ที่มีชื่อเป็นรายการไดเรกทอรีดังนั้นคุณต้องสร้างไฟล์ชั่วคราวบางแห่ง แต่ไฟล์นั้นเป็นเพียงชื่อมันไม่มีข้อมูลใด ๆ/dev/fd/NNN

tmp=$(mktemp -d)
mkfifo "$tmp/f1" "$tmp/f2" "$tmp/f3" "$tmp/f4"
produce_arg1 >"$tmp/f1" &
produce_arg2 >"$tmp/f2" &
consume_arg3 <"$tmp/f3" &
consume_arg4 <"$tmp/f4" &
main_command "$tmp/f1" "$tmp/f2" "$tmp/f3" "$tmp/f4"
rm -r "$tmp"

2
</dev/fd/8 >/dev/fd/9ไม่เทียบเท่ากับ<&8 >&9บน Linux และควรหลีกเลี่ยง
Stéphane Chazelas

@Gilles - ฉันสับสนกับตัวอย่างที่ซับซ้อน คุณช่วยกรุณาแสดงวิธีเลียนแบบได้diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)ไหม:
Martin Vegter

3

ใครบางคนช่วยอธิบายวิธีการใช้ตัวอย่างต่อไปนี้ได้ไหม?
diff <(cat "$2" | xz -d) <(cat "$1" | xz -d)

ฉันคิดว่าชื่อ pipes นั้นง่ายต่อการติดตามมากกว่าการเปลี่ยนเส้นทางดังนั้นในแง่ที่ง่ายที่สุด:

mkfifo p1 p2               # create pipes
cat "$2" | xz -d > p1 &    # start the commands in the background
cat "$1" | xz -d > p2 &    #    with output to the pipes
diff p1 p2                 # run 'diff', reading from the pipes
rm p1 p2                   # remove them at the end

p1และp2เป็นชื่อไพพ์ชั่วคราวพวกเขาสามารถตั้งชื่ออะไรก็ได้

มันจะฉลาดกว่าในการสร้างไปป์ใน/tmpเช่นในไดเรกทอรีเมื่อคำตอบของ Gilles แสดงและทราบว่าคุณไม่ต้องการcatที่นี่ดังนั้น:

tmpdir=$(mktemp -d)
mkfifo "$tmpdir/p1" "$tmpdir/p2"
xz -d < "$2" > "$tmpdir/p1" &
xz -d < "$1" > "$tmpdir/p2" &
diff "$tmpdir/p1" "$tmpdir/p2"
rm -r "$tmpdir"

(คุณอาจหนีไปได้โดยไม่ต้องใส่เครื่องหมายคำพูดด้วยเนื่องจากmktempมีแนวโน้มว่าจะสร้างชื่อไฟล์ที่ "ดี")

โซลูชันท่อมีข้อแม้ว่าถ้าคำสั่งหลัก ( diff) ตายก่อนที่จะอ่านอินพุตทั้งหมดกระบวนการพื้นหลังสามารถปล่อยให้แขวนอยู่


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