จัดเตรียมบรรทัดสุดท้ายของ stdin ไปยัง stdin ทั้งหมด


9

พิจารณาสคริปต์นี้:

tmpfile=$(mktemp)

cat <<EOS > "$tmpfile"
line 1
line 2
line 3
EOS

cat <(tail -1 "$tmpfile") "$tmpfile"

งานและผลลัพธ์:

line 3
line 1
line 2
line 3

สมมติว่าแหล่งอินพุตของเราแทนที่จะเป็นไฟล์จริงแทน stdin:

cat <<EOS | # what goes here now?
line 1
line 2
line 3
EOS

เราจะแก้ไขคำสั่งได้อย่างไร:

cat <(tail -1 "$tmpfile") "$tmpfile"

เพื่อที่จะยังคงสร้างผลลัพธ์เดียวกันในบริบทที่แตกต่างกันนี้

หมายเหตุ: Heredoc ที่เฉพาะเจาะจงที่ฉันกำลังพูดถึงรวมถึงการใช้ Heredoc นั้นเป็นเพียงตัวอย่างเท่านั้น คำตอบใด ๆ ที่ได้รับการยอมรับควรคิดว่ามันจะได้รับข้อมูลผ่านทางพล stdin


1
stdin เป็น"ไฟล์จริง" เสมอ (fifo / socket / etc เป็นไฟล์ด้วยเช่นกันไม่ใช่ทุกไฟล์ที่ค้นหาได้) คำตอบสำหรับคำถามของคุณอาจเป็น "ใช้ไฟล์ชั่วคราว" เล็กน้อยหรือสยองขวัญซึ่งจะโหลดไฟล์ทั้งหมดในหน่วยความจำ "ฉันจะดึงข้อมูลเก่าจากกระแสได้โดยไม่ต้องเก็บไว้ที่ไหน " ไม่สามารถมีคำตอบที่ดี
mosvy

1
@mosvy นั่นเป็นคำตอบที่ยอมรับได้อย่างสมบูรณ์หากคุณต้องการที่จะเพิ่ม
โยนาห์

2
@mosvy ดังที่โจนาห์ได้กล่าวไว้คำตอบควรโพสต์ในกล่องคำตอบ ฉันรู้ว่ามันเป็นเรื่องยากที่จะอ่านเว็บไซต์ใด ๆ ในขณะนี้ แต่โปรดเพิกเฉยสีแดงที่ค่อยๆหยดลงไปตามวิสัยทัศน์ของคุณและใช้ textarea ที่ต่ำกว่า
wizzwizz4

คำตอบ:


7

ลอง:

awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'

ตัวอย่าง

กำหนดตัวแปรด้วยอินพุตของเรา:

$ input="line 1
> line 2
> line 3"

ใช้คำสั่งของเรา:

$ echo "$input" | awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'
line 3
line 1
line 2
line 3

แน่นอนว่าเราสามารถใช้ here-doc:

$ cat <<EOS | awk '{x=x $0 ORS}; END{printf "%s", $0 ORS x}'
line 1
line 2
line 3
EOS
line 3
line 1
line 2
line 3

มันทำงานอย่างไร

  • x=x $0 ORS

    xผนวกนี้แต่ละบรรทัดของการป้อนข้อมูลให้กับตัวแปร

    ใน awk, ORSเป็นตัวคั่นบันทึกเอาท์พุท โดยค่าเริ่มต้นมันเป็นตัวอักษรขึ้นบรรทัดใหม่

  • END{printf "%s", $0 ORS x}

    หลังจากที่เราได้อ่านในไฟล์ทั้งหมดนี้พิมพ์บรรทัดสุดท้าย, ตามด้วยเนื้อหาของแฟ้มทั้ง$0x

เนื่องจากเป็นการอ่านอินพุตทั้งหมดในหน่วยความจำจึงไม่เหมาะสำหรับอินพุตขนาดใหญ่ ( เช่นกิกะไบต์)


ขอบคุณจอห์น ดังนั้นเป็นไปไม่ได้ที่จะทำสิ่งนี้ในลักษณะที่คล้ายคลึงกับตัวอย่างไฟล์ที่มีชื่อของฉันใน OP หรือไม่? ฉันจินตนาการว่า stdin ถูกทำซ้ำอย่างใด ... เรียงลำดับวิธีteeแต่ stdin และไฟล์เราจะวาง stdin เดียวกันลงในการแทนที่กระบวนการสองแบบที่แตกต่างกัน หรืออะไรก็ตามที่จะเทียบเท่ากับสิ่งนั้น
Jonah

5

หาก stdin ชี้ไปที่ไฟล์ที่ค้นหาได้ (เช่นในกรณีของ bash's (แต่ไม่ใช่เชลล์อื่นทั้งหมด) ที่นี่ซึ่งมีการใช้งานกับไฟล์ temp) ที่นี่คุณสามารถรับหางแล้วค่อยย้อนกลับไปอ่านเนื้อหาทั้งหมดก่อน:

ขอผู้ประกอบการที่มีอยู่ในzshหรือksh93เปลือกหอยหรือภาษาสคริปต์เช่น TCL / Perl / หลาม bashแต่ไม่ได้อยู่ใน แต่คุณสามารถโทรหาผู้ล่ามที่สูงขึ้นจากถ้าคุณมีการใช้bashbash

ksh93 -c 'tail -n1; cat <#((0))' <<...

หรือ

zsh -c 'zmodload zsh/system; tail -n1; sysseek 0; cat' <<...

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

โซลูชันบางอย่างสำหรับการจัดเก็บในหน่วยความจำได้รับการกำหนดแล้ว

ด้วย tempfile zshคุณสามารถทำได้ด้วย:

seq 10 | zsh -c '{ cat =(sed \$w/dev/fd/3); } 3>&1'

หากบน Linux ด้วยbashหรือzshหรือเชลล์ใด ๆ ที่ใช้ไฟล์ temp สำหรับ here-documents คุณสามารถใช้ไฟล์ temp ที่สร้างขึ้นโดย here-document เพื่อเก็บเอาท์พุท:

seq 10 | {
  chmod u+w /dev/fd/3 # only needed in bash5+
  cat > /dev/fd/3
  tail -n1 /dev/fd/3
  cat <&3
} 3<<EOF
EOF

4
cat <<EOS | sed -ne '1{h;d;}' -e 'H;${G;p;}'
line 1
line 2
line 3
EOS

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

  1. ให้เนื้อหาเต็มของเอกสารถึง tailให้เนื้อหาทั้งหมดของเอกสารที่จะ
  2. ให้มันอีกครั้งเพื่อcatเพื่อ
  3. เพื่อให้.

บิตที่ยุ่งยากนั้นไม่ได้เป็นการทำซ้ำเนื้อหาของเอกสาร ( teeทำเช่นนั้น) แต่เพื่อให้ได้ผลลัพธ์ที่tailจะเกิดขึ้นก่อนที่จะเอาท์พุทที่เหลือของเอกสารโดยไม่ต้องใช้ไฟล์ชั่วคราวระดับกลาง

ใช้sed(หรือawkเป็น John1024 ทำ ) กำจัดการแยกวิเคราะห์ข้อมูลและปัญหาการสั่งซื้อโดยการเก็บข้อมูลไว้ในหน่วยความจำ

sedแก้ปัญหาที่ผมเสนอคือการ

  1. 1{h;d;}จัดเก็บบรรทัดแรกในพื้นที่พักตามที่เป็นอยู่และข้ามไปที่บรรทัดถัดไป
  2. Hต่อท้ายแต่ละบรรทัดไปที่พื้นที่พักด้วยการขึ้นบรรทัดใหม่
  3. ${G;p;}ต่อท้ายพื้นที่พักไว้ที่บรรทัดสุดท้ายด้วยการขึ้นบรรทัดใหม่และพิมพ์ข้อมูลผลลัพธ์

นี่เป็นการแปลตามตัวอักษรอย่างแท้จริงของวิธีแก้ปัญหาของ John1024 sedโดยข้อแม้ที่ว่ามาตรฐาน POSIX รับประกันได้ว่าพื้นที่พักค้างนั้นจะไม่เกิน 8192 ไบต์ (8 KiB แต่แนะนำว่าบัฟเฟอร์นี้จะถูกจัดสรรแบบไดนามิกและขยายตามความจำเป็นซึ่ง GNU ทั้งสองsedและ BSD sedกำลังทำอยู่)


หากคุณอนุญาตให้คุณใช้ไพพ์ที่มีชื่อ:

mkfifo mypipe
cat <<EOS | tee mypipe | cat <( tail -n 1 mypipe ) -
line 1
line 2
line 3
EOS
rm -f mypipe

ใช้สิ่งนี้teeในการส่งข้อมูลลงและในเวลาเดียวกันmypipe ยูทิลิตี้ครั้งแรกที่จะอ่านเอาท์พุทจาก(ซึ่งอ่านจากที่มีการเขียนถึง) แล้วผนวกสำเนาของเอกสารมาโดยตรงจากcatcattailmypipeteetee

มีข้อบกพร่องร้ายแรงในเรื่องนี้แม้ว่าในกรณีที่เอกสารมีขนาดใหญ่เกินไป (ใหญ่กว่าขนาดบัฟเฟอร์ของไปป์) teeการเขียนmypipeและcatจะบล็อกในขณะที่รอให้ท่อ (ไม่มีชื่อ) ว่างเปล่า มันจะไม่ถูกทำให้ว่างจนกว่าจะcatอ่านจากมัน catจะไม่อ่านจนกว่าtailจะเสร็จ และtailจะไม่เสร็จจนกว่าteeจะเสร็จสิ้น นี่คือสถานการณ์การหยุดชะงักแบบคลาสสิก

ความแปรปรวน

tee >( tail -n 1 >mypipe ) | cat mypipe -

มีปัญหาเดียวกัน


2
sedหนึ่งไม่ทำงานถ้าใส่มีเพียงหนึ่งบรรทัด (อาจจะsed '1h;1!H;$!d;G') นอกจากนี้โปรดทราบว่าsedการใช้งานหลายอย่างมีขีด จำกัด ต่ำกับขนาดของรูปแบบและพื้นที่ว่าง
Stéphane Chazelas

การแก้ปัญหาท่อที่มีชื่อเป็นสิ่งที่ฉันกำลังมองหา ข้อ จำกัด คือความอัปยศ ฉันเข้าใจคำอธิบายของคุณยกเว้น“ และหางจะไม่เสร็จจนกว่าทีจะเสร็จ” - คุณช่วยอธิบายได้ไหมว่าทำไมถึงเป็นเช่นนั้น?
โยนาห์

2

มีเครื่องมือที่มีชื่อpeeอยู่ในชุดของโปรแกรมอรรถประโยชน์บรรทัดคำสั่งมักจะบรรจุด้วยชื่อ "moreutils" (หรือเรียกคืนได้จากเว็บไซต์บ้านของมัน)

หากคุณมีมันไว้ในระบบของคุณสิ่งที่เทียบเท่ากับตัวอย่างของคุณจะเป็นเช่น:

cat <<EOS | pee 'tail -1' cat 
line 1
line 2
line 3
EOS

การสั่งซื้อคำสั่งที่ทำงานผ่านpeeมีความสำคัญเนื่องจากจะถูกดำเนินการตามลำดับที่ให้ไว้


1

ลอง:

cat <<EOS # | what goes here now? Nothing!
line 3
line 1
line 2
line 3
EOS

เนื่องจากข้อมูลทั้งหมดเป็นข้อมูลตัวอักษร ("นี่คือเอกสาร") และความแตกต่างระหว่างมันกับผลลัพธ์ที่ต้องการนั้นไม่สำคัญเพียงแค่นวดข้อมูลตามตัวอักษรตรงนั้นเพื่อให้ตรงกับผลลัพธ์

ตอนนี้สมมติว่าline 3มาจากที่อื่นและถูกเก็บไว้ในตัวแปรที่เรียกว่าlastline:

cat <<EOS # | what goes here now? Nothing!
$lastline
line 1
line 2
$lastline
EOS

ในเอกสารที่นี่เราสามารถสร้างข้อความโดยการแทนที่ตัวแปร ไม่เพียงแค่นั้น แต่เราสามารถคำนวณข้อความโดยใช้การทดแทนคำสั่ง:

cat <<EOS
this is template text
here we have a hex conversion: $(printf "%x" 42)
EOS

เราสามารถแก้ไขหลายบรรทัด:

cat <<EOS
multi line
preamble
$(for x in 3 1 2 3; do echo line $x ; done)
epilog
EOS

โดยทั่วไปให้หลีกเลี่ยงการประมวลผลข้อความเทมเพลต doc ที่นี่ พยายามที่จะสร้างมันโดยใช้รหัสการแก้ไข


1
ฉันไม่สามารถบอกได้ว่านี่เป็นเรื่องตลกหรือไม่ cat <<EOS...ใน OP เป็นเพียง Standin ตัวอย่างสำหรับ "catting ไฟล์โดยพลการ" เพื่อให้เฉพาะโพสต์และคำถามที่ชัดเจน นั่นไม่ชัดเจนกับคุณจริง ๆ หรือคุณแค่คิดว่ามันจะฉลาดกว่าที่จะตีความคำถามอย่างแท้จริง?
โยนาห์

@Jonah คำถามชัดเจนว่า "[l] และพูดว่าแหล่งอินพุตของเราแทนที่จะเป็นไฟล์จริงแทนที่จะเป็น stdin:" ไม่มีอะไรเกี่ยวกับ "ไฟล์โดยพลการ"; เอกสารเกี่ยวกับที่นี่ เอกสารที่นี่ไม่ได้โดยพลการ มันไม่ใช่อินพุตของโปรแกรมของคุณ แต่เป็นส่วนของไวยากรณ์ที่โปรแกรมเมอร์เลือก
Kaz

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

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

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

0

ถ้าคุณไม่สนใจเกี่ยวกับการสั่งซื้อ จากนั้นจะใช้งานcat lines | tee >(tail -1)ได้ อย่างที่คนอื่นพูด คุณต้องอ่านไฟล์สองครั้งหรือบัฟเฟอร์ไฟล์ทั้งหมดเพื่อทำตามลำดับที่คุณขอ

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