ย้าย N บรรทัดแรกของเอาต์พุตไปยังจุดสิ้นสุดโดยไม่ต้องใช้ไฟล์ชั่วคราว


11

ลองนึกภาพผลลัพธ์ของคำสั่งเช่น

44444
55555
11111
22222
33333

ฉันจะดึงบรรทัด N แรกออกมาได้อย่างไร (สองบรรทัดแรกในตัวอย่างด้านบน) และต่อท้ายบรรทัด แต่ไม่ต้องใช้ไฟล์ temp ( โดยใช้ไพพ์เท่านั้น)

11111
22222
33333
44444
55555

บางสิ่งบางอย่างตามแนวของ| sed -n '3,5p;1,2p'(ซึ่งเห็นได้ชัดว่าใช้งานไม่ได้เพราะความใจร้อนไม่สนใจคำสั่งของคำสั่ง)


2
ทำไมเราไม่สามารถใช้ไฟล์ชั่วคราวได้
Braiam

คำตอบ:


13

เพียงแค่คัดลอกบรรทัดเหล่านั้นไปยังบัฟเฟอร์พัก (จากนั้นลบออก) และเมื่อในบรรทัดสุดท้ายผนวกเนื้อหาของบัฟเฟอร์ค้างไว้กับพื้นที่รูปแบบ:

some command | sed '1,NUMBER{           # in this range
H                                       # append line to hold space and
1h                                      # overwrite if it's the 1st line
d                                       # then delete the line
}
$G'                                     # on last line append hold buffer content

ด้วยgnu sedคุณสามารถเขียนมันเป็น

some command | sed '1,NUMBER{H;1h;d;};$G'

นี่เป็นอีกวิธีหนึ่งที่มี ol ' ed(มันจะrเอาท์พุทของการsome commandเข้าไปในบัฟเฟอร์ข้อความและจากนั้นmoves บรรทัด1,NUMBERหลังจาก la $t one):

ed -s <<IN
r ! some command
1,NUMBERm$
,p
q
IN

โปรดทราบว่า - ดังที่กล่าวไว้ - ทั้งคู่จะล้มเหลวหากเอาต์พุตมีน้อยกว่าNUMBER+1 บรรทัด แนวทางที่มั่นคงมากขึ้นจะเป็น ( gnu sedไวยากรณ์):

some command | sed '1,NUMBER{H;1h;$!d;${g;q;};};$G'

อันนี้ลบเฉพาะบรรทัดในช่วงนั้นตราบใดที่พวกเขาไม่ใช่บรรทัดสุดท้าย ( $!d) - มิฉะนั้นจะเขียนทับพื้นที่รูปแบบด้วยเนื้อหาบัฟเฟอร์ที่เก็บไว้ ( g) แล้วquits (หลังจากพิมพ์พื้นที่รูปแบบปัจจุบัน)


2
sed -e '1,NUMBER{H;1h;d;}' -e '$G'ยังใช้งานได้แบบพกพา (โปรดทราบว่าsedการใช้งานบางอย่างไม่สามารถเก็บได้มากกว่าสองสามกิโลไบต์ในพื้นที่พักดังนั้นNUMBERจะไม่ใหญ่เกินไป)
Stéphane Chazelas

@ StéphaneChazelas - ขอบคุณสำหรับอินพุต - ฉันมักจะไปกับหนึ่งคำสั่งต่อบรรทัดที่ฉันรู้ว่าเป็นแบบพกพา - ไวยากรณ์การแสดงออกหลายได้เสมอสับสนกับฉันเช่นเอกสาร posix พูดว่า"<ขวา - เครื่องหมาย> จะเป็น นำหน้าด้วย <newline>"ดังนั้นตามที่พวกเขาไม่ควรที่จะเป็นsed -e '1,NUMBER{H;1h;d' -e'}' -e '$G'?
don_crissti

4
โดยทั่วไปแล้วใหม่-eจะขึ้นบรรทัดใหม่ d;}ยังไม่ได้ POSIX แต่เป็นแบบพกพา ที่จะได้รับการแก้ไขในข้อกำหนด POSIX ถัดไป ดูaustingroupbugs.net/view.php?id=944#c2720
Stéphane Chazelas

2
@don_crissti ขอบคุณ! มันจะเจ๋งถ้าคุณอาจรวมคำอธิบายสั้น ๆ ว่าทำไมมันถึงได้ผล (แน่นอนฉันจะไปดูมันขึ้น แต่มันจะทำให้คำตอบที่มีคุณค่ามากขึ้น.)
ปีเตอร์ Uhnak

ในหัวของฉันสิ่งที่พูด1,NUMBER{H;1h;d;}ไม่ได้เกี่ยวกับการไม่มีเซมิโคลอนทันทีหลังจากเปิดรั้ง นั่นอาจเป็นข้อผิดพลาดใน SunOS 4.1 sedซึ่งมีวิธีแก้ปัญหายังคงเชื่อมต่อกับนิ้วมือของฉันในอีก 20 ปีต่อมา
zwol

11

awkวิธีการ:

cmd | awk -v n=3 '
  NR <= n {head = head $0 "\n"; next}
  {print}
  END {printf "%s", head}'

ข้อดีอย่างหนึ่งของsedวิธีการ@ don_crissti ก็คือมันยังคงใช้งานได้ (เอาต์พุตของบรรทัด) หากเอาต์พุตมีnบรรทัดหรือน้อยกว่า


คุณอาจต้องการแทนที่ hardcoded \nด้วย ORS เพื่อให้สามารถทำงานร่วมกับตัวแยกเร็กคอร์ดอื่น ๆ (บอกว่าคุณต้องการใช้ย่อหน้า ฯลฯ )
fedorqui

6

ฉันมีxclipและด้วยสิ่งนี้สามารถทำได้ในลักษณะนี้:

./a_command | xclip -in && xclip -o | tail -n +3 && xclip -o | head -n 2

นี่คือคำอธิบาย:

xclip - command line interface to X selections (clipboard)

NAME
       xclip - command line interface to X selections (clipboard)

SYNOPSIS
       xclip [OPTION] [FILE]...

DESCRIPTION
       Reads from standard in, or from one or more files, and makes the data available as an X selection for pasting into X applications. Prints current X selection to standard out.

       -i, -in
              read text into X selection from standard input or files (default)

       -o, -out
              prints the selection to standard out (generally for piping to a file or program)

3
+1 สำหรับโฆษณา (mis-) ใช้ xclip คำตอบนั้นต้องการ X server ที่สามารถเข้าถึงได้ / กำลังรันอยู่
jofel

3

วิธี Perl:

perl -ne '$.<3?($f.=$_):print;}{print $f'

หรือสิ่งเดียวกันที่เขียนน้อยเข้ารหัส:

perl -ne 'if($.<3){ $f.=$_ } else{ print } END{print $f}'

ตัวอย่างเช่น:

$ cat file
44444
55555
11111
22222
33333

$ cat file | perl -ne '$.<3?($f.=$_):print;}{print $f'
11111
22222
33333
44444
55555

คำอธิบาย

  • -ne: อ่านไฟล์อินพุต / สตรีมทีละบรรทัดและใช้สคริปต์ที่กำหนดโดย-eกับแต่ละบรรทัด
  • $.<3: $.เป็นหมายเลขบรรทัดปัจจุบันดังนั้นเปลี่ยน3เป็นจำนวนบรรทัดที่คุณต้องการเปลี่ยน
  • $.<3?($f.=$_):print;: นี่คือโอเปอเรเตอร์ที่มีเงื่อนไขรูปแบบทั่วไปคือcondition ? case1 : case2มันจะทำงานcase1หากconditionเป็นจริงและcase2หากเป็นเท็จ ที่นี่หากหมายเลขบรรทัดปัจจุบันน้อยกว่า 3 จะต่อท้ายบรรทัดปัจจุบัน ( $_) กับตัวแปร$fและหากหมายเลขบรรทัดมากกว่า 3 จะพิมพ์ออกมา
  • }{ print $fคือ}{เป็นชวเลข Perl END{}สำหรับ มันจะทำงานหลังจากประมวลผลบรรทัดอินพุตทั้งหมดแล้ว ณ $fจุดนี้เราจะมีการเก็บรวบรวมทุกสายที่เราต้องการที่จะเปลี่ยนและจะมีการพิมพ์ทุกคนที่เราต้องการที่จะออกเพียงอย่างเดียวดังนั้นพิมพ์เส้นที่บันทึกเป็น

1
เกี่ยวกับรุ่นที่ตีกอล์ฟของคุณตัวละครบางตัวสามารถลบได้perl -ne '$.<3?$f.=$_:print}{print $f
123

1

ใช้ exPOSIX ใช่มันมีไว้สำหรับการแก้ไขไฟล์ แต่มันจะทำงานในขั้นตอน

printf %s\\n 111 222 333 444 555 | ex -sc '1,2m$|%p|q!' /dev/stdin

นี้สามารถมีคำสั่งโดยพลการใด ๆ ที่เพิ่มที่จุดเริ่มต้นหรือจุดสิ้นสุดของไปป์ไลน์และจะทำงานเหมือนกัน ยังดีกว่าเมื่อมีสถานะของ/dev/stdinมันเป็นไปตาม POSIX

(ฉันไม่รู้ว่า/dev/stdinมีการระบุใน POSIX หรือไม่ แต่ฉันเห็นว่ามีทั้งใน Linux และ Mac OS X)

สิ่งนี้มีความได้เปรียบในการอ่านมากกว่าการใช้sedพื้นที่พัก - คุณเพียงแค่บอกว่าex"ย้ายเส้นเหล่านี้ไปยังจุดสิ้นสุด" และทำเช่นนั้น (คำสั่งที่เหลือหมายถึง "พิมพ์บัฟเฟอร์" และ "exit" ซึ่งสามารถอ่านได้เช่นกัน)

หมายเหตุ: exคำสั่งที่ระบุด้านบนจะล้มเหลวหากได้รับน้อยกว่า 2 บรรทัดเป็นอินพุต

อ่านเพิ่มเติม:


0

pythonตัวอย่างสั้น ๆ:

#!/usr/bin/env python3
import sys
file_ = sys.argv[1]
lines = int(sys.argv[2])
with open(file_) as f:
    f = f.readlines()
    out = f[lines:] + f[:lines]
    print(''.join(out), end='')

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

ตัวอย่าง:

$ cat input.txt
44444
55555
11111
22222
33333

$ ./sw_lines.py input.txt 2
11111
22222
33333
44444
55555

$ ./sw_lines.py input.txt 4
33333
44444
55555
11111
22222


0

นี่เป็นอีกตัวเลือกหนึ่งที่ต้องใช้ GNU sed:

(x=$(gsed -u 3q);cat;printf %s\\n "$x")

-uทำให้ GNU ไม่มีข้อผิดพลาดsedเพื่อให้sedคำสั่งไม่ใช้มากกว่า 3 บรรทัดของ STDIN การทดแทนคำสั่งลบบรรทัดว่างดังนั้นคำสั่งจะไม่รวมบรรทัดว่างที่ท้ายของเอาต์พุตหากบรรทัดที่สาม, สามและสองหรือสาม, สองและสามและบรรทัดแรกว่างเปล่า

คุณสามารถทำสิ่งนี้:

tee >(sponge /dev/stdout|sed -u 3q)|sed 1,3d

หากไม่มีsponge /dev/stdout|คำสั่งจะล้มเหลวด้วยอินพุตที่ยาว sponge /dev/stdoutสามารถถูกแทนที่ด้วยtac|tacแม้ว่าจะพิมพ์ตัวอย่างเช่นa\ncbหากอินพุตอยู่a\nb\ncที่ไหน\nเป็น linefeed หรือด้วย(x=$(cat);printf %s\\n "$x")แม้ว่าจะลบบรรทัดว่างออกจากจุดสิ้นสุดของอินพุต คำสั่งด้านบนจะลบบรรทัดแรกเมื่อจำนวนบรรทัดของอินพุตเป็นหนึ่งหรือสอง

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