วิธีอ่านอินพุตของผู้ใช้จากไปป์?


9

สมมติว่าฉันมีไฟล์ชื่อที่confirmation.shมีเนื้อหาดังต่อไปนี้:

#!/bin/bash
echo -n "Are you sure [Y/n]? "
read line
case "$line" in
    n|N) echo "smth"
        ;;
    y|Y) echo "smth"
        ;;
esac

และฉันต้องการเรียกใช้สคริปต์นี้ด้วยวิธีต่อไปนี้:

cat confirmation.sh | sh

ฉันเห็นAre you sure [Y/n]?และสคริปต์ถูกขัดจังหวะ มีปัญหาอะไร?


2
คุณมี/bin/bashในสายปัง แต่คุณใช้ส่วนขยายและพยายามที่จะท่อสคริปต์เพื่อ.sh shไม่ใช่ปัญหาเนื่องจากรหัสที่คุณมีเข้ากันได้กับทั้งคู่ แต่ควรชี้ให้เห็น
แกรม

คำตอบ:


8

อย่างที่คนอื่น ๆ พูดนี่เป็นเพราะstdinของshถูกเปลี่ยนเส้นทางไปอ่านจากไปป์มันไม่ได้เชื่อมต่อกับเทอร์มินัลเหมือนปกติ สิ่งหนึ่งที่คุณสามารถทำได้เพื่อหลีกเลี่ยงปัญหานี้คือการใช้/dev/ttyเพื่อบังคับให้สคริปต์อ่านจากเทอร์มินัล เช่น:

#!/bin/sh

read -p "Are you sure [Y/n]?" line </dev/tty
case "$line" in
  y|Y) echo "confirmed"
    ;;
  *) echo "not confirmed"
    ;;
esac

โดยปกติคุณจะทำเช่นนี้เฉพาะกรณีที่คุณต้องการป้องกันไม่ให้คนอื่นเขียนสคริปต์อินพุตเช่น:

echo Y | sh confirmation.sh

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


2
sh 3<<CONFIRM /dev/fd/3
    $(cat ./confirmation.sh)
CONFIRM

sh 3<./confirmation.sh /dev/fd/3

หมายเหตุ:ขอบคุณ @Graeme สำหรับการแก้ไขฉันจากสองตัวอย่างข้างต้น ...

มันง่ายมากที่จะทำถ้าคุณstdinชัดเจน

2<./confirmation.sh . /dev/stderr

หรือเนื่องจากเทอร์มินัล 0 1 2 เป็นไฟล์เดียวกันทั้งหมดให้เพิ่ม:

read line <&2

และคุณ

cat ./confirmation.sh | sh

ทำงานได้ดี


ฉันไม่สามารถรับสิ่งเหล่านี้เพื่อทำงานนอกเหนือจากล่าสุด
แกรม

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

@ Graeme - ไม่แน่ใจว่าทำไมฉันถึงพลาดในครั้งแรก - ได้สาบานฉันทดสอบพวกเขาทั้งหมดในเวลาเดียวกัน - แต่สองคนแรกจำเป็นต้องมีการเปลี่ยนแปลงเพื่อให้ทำงานได้อย่างถูกต้อง ขอบคุณ.
mikeserv

หน้าตาดีก็ยังไม่ได้อ่านจากสถานีเมื่อฉันแหล่งที่มาจากstderrว่า ฉันควรจะเข้าใจฉันไม่เข้าใจว่าทำไม
แกรม

@ Graeme - แปลก - ฉันลองใช้ dash sh zsh และ bash ทำงานทั้งหมด
mikeserv

0

สิ่งนี้ได้ผลสำหรับการโต้แย้งเดียวที่ถูกไพพ์ไปยังสคริปต์ของฉัน:

if [ -p /dev/stdin ]; then set -- "$( cat )"; fi

จากนั้นฉันสามารถเข้าถึงข้อมูลไพพ์ในอาร์กิวเมนต์ตำแหน่ง ( $1) ซึ่งเข้ากันได้กับงานสคริปต์อื่นของฉัน


จากinfo test:

[ -p /dev/stdin ]: -p FILEเป็นจริงถ้าไฟล์มีอยู่และเป็นชื่อไปป์


-1

คำตอบสั้น ๆ คือคุณทำไม่ได้ ไพพ์เปลี่ยนเส้นทาง stdout เป็น stdin ดังนั้นคุณจึงไม่สามารถเรียกใช้สคริปต์แบบโต้ตอบได้เนื่องจากคุณได้เปลี่ยนทิศทางเอาต์พุตจากคำสั่งแรกเป็นอินพุตไปยังคำสั่งที่สองในคำสั่งไพพ์

คุณอาจต้องการทำสิ่งนี้:

cat confirmation.sh > ask.sh && sh ask.sh

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