เปลี่ยนเส้นทางเอาต์พุตจากคำสั่งทั้งหมดไปยังไฟล์ไม่ใช่เพียงครั้งสุดท้าย


11

ฉันต้องการเพิ่มส่วนหัวในไฟล์ที่มี echo จากนั้นใช้คำสั่งเพื่อสร้างไฟล์ที่เหลือ หมายความว่าฉันจะใช้สองคำสั่งแยกกัน

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

ฉันพยายามแล้ว

echo "header line" | cut -c 1-5 input_file > output_file

echo "header line"; cut -c 1-5 input_file > output_file

นี่จะเปลี่ยนเส้นทางเอาต์พุตจากคำสั่ง cut เท่านั้น

คำสั่งต่อไปนี้ใช้งานได้ แต่รู้สึกเงอะงะ:

echo "header line" > output_file; cut -c 1-5 input_file >> output_file

วิธีที่ชาญฉลาดในการแก้ปัญหาของฉันคืออะไร?

คำตอบ:


11

ปัญหาที่นี่ไม่มีปัญหากับการเปลี่ยนเส้นทางของ Linux ค่อนข้างเป็นความเข้าใจผิดขั้นพื้นฐานของวิธีการทำงานของไปป์ไลน์ การเปลี่ยนเส้นทางที่นี่ไม่ได้ทำงานเพราะเพียงตัดเป็นจริงการพิมพ์ที่ stdout stdout สำหรับคำสั่ง echo ถูกไพพ์ไปยัง stdin ของ cut (ซึ่งไม่ได้ใช้ในกรณีนี้เนื่องจากไฟล์ถูกระบุ)

echo "header line" > output_file && cut -c 1-5 input_file >> output_file

คือสิ่งที่คุณต้องการและไม่ไม่เกี่ยวข้องเลย (ฉันแทนที่;ด้วย&&ดังนั้นคำสั่ง cut จะดำเนินการเฉพาะเมื่อส่วนหัวถูกเขียนสำเร็จวิธีนี้จะไม่ทำงานหากคุณไม่มีสิทธิ์ในการสร้างหรือเขียนไปยัง output_file )

คุณสามารถทำได้ทุกอย่างใน subshell เช่น

(echo "header line"; cut -c 1-5 input_file) > output_file

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

หากคุณต้องการให้ cut เพื่อส่ง stdin ไปยัง stdout คุณสามารถลอง:

echo "header line" | cut -c 1-5 - input_file

(เส้นประเป็นทางลัดทั่วไปสำหรับ stdin)

อย่างไรก็ตามสิ่งนี้จะทำการตัดบน stdin (ทำให้บรรทัดส่วนหัวของ "header") เป็นการยากที่จะบอกว่านี่คือสิ่งที่คุณต้องการหรือไม่จากคำถาม


13

สิ่งที่คุณมองหาคือ:

{ echo "header line"; cut -c 1-5 input_file; } > output_file
  • ไวยากรณ์นี้ไม่มีผลข้างเคียงเนื่องจากคำสั่งถูกเรียกใช้ในเชลล์ปัจจุบันไม่ใช่เชลล์ย่อย
  • มีการคั่นอย่างชัดเจนของคำสั่งที่ไปที่ output_file
  • มันปรับขนาดได้ตามที่คุณสามารถเขียนใหม่ด้วยวิธีนั้น:

{
  echo "header line"
  cut -c 1-5 input_file
  ... # insert new commands here
} > output_file

หากคุณต้องการให้เอาต์พุตข้อผิดพลาดไปที่ไฟล์เดียวกันคุณสามารถแก้ไขบรรทัดสุดท้ายด้วยวิธีนี้:

} > output_file 2>&1

ขอบคุณ Olivier Dulac ที่เตือนเคล็ดลับ

หากคุณต้องการให้ผลลัพธ์บางอย่างภายในบล็อกไปที่หน้าจอของคุณคุณสามารถใช้ไวยากรณ์นั้นได้:

{
  echo "header line"
  echo "this line doesn't go to output_file" > /dev/tty
  cut -c 1-5 input_file
} > output_file

+1: ชัดเจนและระบุสิ่งที่ถูกเปลี่ยนเส้นทาง เมื่อต้องการเปลี่ยนเส้นทางทุกอย่าง (รวมถึงข้อผิดพลาด msg): { .... } > some_file 2>&1( "จะข่มขี่" some_file ไม่ข่มขี่ แต่ผนวกกับมันแทนเพียงแค่เปลี่ยน. >เป็น>>: { ... } >> some_file 2>&1)
โอลิเวีย Dulac

4

เพียงแค่ปัดเศษคำตอบที่มี exec

exec > output_file
echo "header line"
cut -c 1-5 input_file

+1: ในสคริปต์เป็นเรื่องธรรมดามากที่จะใช้สิ่งนี้ (น้อยกว่าบรรทัดคำสั่งเนื่องจากสิ่งต่าง ๆ เริ่มสับสน) ^^
Olivier Dulac

ใครบางคนสามารถอธิบายรายละเอียดเกี่ยวกับการแก้ปัญหานี้ ฉันเห็นว่ามันกำลังทำอะไร แต่ฉันไม่เข้าใจ 'ผู้บริหาร' ในบริบทนี้ นอกจากนี้ดูเหมือนว่าคุณต้องปิดอีกครั้งหากสคริปต์ที่เหลือของคุณต้องการ stdout ปกติ
โจ

1
เมื่อคุณใช้ exec ในสคริปต์โดยไม่มีคำสั่งคุณสามารถทำการเปลี่ยนเส้นทาง I / O บน exec ซึ่งมีผลต่อเชลล์ปัจจุบันและลูกในอนาคตทั้งหมด
hildred

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