ฉันจะรันคำสั่งใด ๆ ที่แก้ไขไฟล์ (อาร์กิวเมนต์)“ ในสถานที่” โดยใช้ bash ได้อย่างไร


110

ฉันมีไฟล์ temp.txt ที่ฉันต้องการจัดเรียงด้วยsortคำสั่งใน bash

ฉันต้องการให้ผลลัพธ์ที่เรียงลำดับแทนที่ไฟล์ต้นฉบับ

สิ่งนี้ใช้ไม่ได้เช่น (ฉันได้รับไฟล์เปล่า):

sortx temp.txt > temp.txt

สามารถทำได้ในบรรทัดเดียวโดยไม่ต้องคัดลอกไปยังไฟล์ชั่วคราวหรือไม่?


แก้ไข: -oตัวเลือกนี้ยอดเยี่ยมมากสำหรับsort. ฉันใช้sortในคำถามของฉันเป็นตัวอย่าง ฉันพบปัญหาเดียวกันกับคำสั่งอื่น ๆ :

uniq temp.txt > temp.txt.

มีวิธีแก้ปัญหาทั่วไปที่ดีกว่านี้หรือไม่?


โปรดดูที่serverfault.com/a/547331/313521
Wildcard

คำตอบ:


171
sort temp.txt -o temp.txt

3
นี่คือคำตอบ ฉันสงสัยจริงๆว่ามีวิธีแก้ปัญหาทั่วไปสำหรับปัญหานี้หรือไม่ ตัวอย่างเช่นถ้าฉันต้องการค้นหาบรรทัด UNIQ ทั้งหมดในไฟล์ "in place" ฉันจะทำ -o
jm ไม่ได้

ไม่ใช่แบบทั่วไป แต่คุณสามารถใช้ -u กับ GNU sort เพื่อค้นหาบรรทัดที่ไม่ซ้ำกัน
James

มีใครแก้ปัญหาเพื่อให้เช่นsort --inplace *.txt? มันจะเจ๋งมาก
sehe

@sehe ลองสิ่งนี้:find . -name \*.txt -exec sort {} -o {} \;
Keith Gaughan

29

sortต้องการที่จะเห็นการป้อนข้อมูลทั้งหมดก่อนที่จะสามารถเริ่มต้นการส่งออก ด้วยเหตุนี้sortโปรแกรมจึงสามารถเสนอตัวเลือกในการแก้ไขไฟล์แทน:

sort temp.txt -o temp.txt

โดยเฉพาะเอกสารของ GNUsortกล่าวว่า:

โดยปกติการจัดเรียงอ่านทุกท่านก่อนที่จะเปิดเอาท์พุทไฟล์เพื่อให้คุณสามารถจัดเรียงไฟล์ได้อย่างปลอดภัยในสถานที่โดยใช้คำสั่งเหมือนและsort -o F F cat F | sort -o Fอย่างไรก็ตามsortด้วย--merge( -m) สามารถเปิดไฟล์เอาต์พุตก่อนที่จะอ่านอินพุตทั้งหมดดังนั้นคำสั่ง like cat F | sort -m -o F - Gจึงไม่ปลอดภัยเนื่องจาก sort อาจเริ่มเขียนFก่อนที่catจะอ่านเสร็จ

ในขณะที่เอกสารของ BSD sortกล่าวว่า:

ถ้า [the] output-file เป็นหนึ่งในอินพุตไฟล์ให้เรียงสำเนาไปยังไฟล์ชั่วคราวก่อนที่จะเรียงลำดับและเขียนเอาต์พุตไปยังไฟล์เอาต์พุต [the]

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

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

uniq temp.txt | perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'

ที่นี่ส่วน perl จะอ่านผลลัพธ์ที่สมบูรณ์จากuniqในตัวแปร$_จากนั้นเขียนทับไฟล์ต้นฉบับด้วยข้อมูลนี้ คุณสามารถทำเช่นเดียวกันนี้ในภาษาสคริปต์ที่คุณเลือกหรือแม้กระทั่งใน Bash แต่โปรดทราบว่าจะต้องใช้หน่วยความจำเพียงพอที่จะจัดเก็บไฟล์ทั้งหมดจึงไม่แนะนำให้ใช้เมื่อทำงานกับไฟล์ขนาดใหญ่


19

นี่เป็นวิธีการทั่วไปที่ใช้ได้กับ uniq, sort และ whatnot

{ rm file && uniq > file; } < file

14
อีกวิธีที่ทั่วไปด้วยspongeจาก moreutils cat file |frobnicate |sponge fileนี้:
Tobu

3
@ โตบุ: ทำไมไม่ส่งเป็นคำตอบแยก?
Flimm

1
เป็นเรื่องดีที่จะทราบว่าสิ่งนี้ไม่จำเป็นต้องรักษาสิทธิ์ของไฟล์ umask ของคุณกำหนดว่าสิทธิ์ใหม่จะเป็นอย่างไร

1
ยุ่งยากอย่างหนึ่ง คุณช่วยอธิบายได้ไหมว่ามันทำงานอย่างไร?
patryk.beza

2
@ patryk.beza: ตามลำดับ: อินพุต FD ถูกเปิดจากไฟล์ต้นฉบับ รายการไดเร็กทอรีดั้งเดิมถูกลบ การเปลี่ยนเส้นทางได้รับการประมวลผลสร้างไฟล์ว่างใหม่ที่มีชื่อเดียวกับไฟล์เก่าที่เคยมี จากนั้นคำสั่งจะทำงาน
Charles Duffy

10

ความคิดเห็นของ Tobu เกี่ยวกับฟองน้ำรับประกันว่าเป็นคำตอบในตัวของมันเอง

หากต้องการอ้างอิงจากหน้าแรกของmoreutils :

เครื่องมือที่ใช้งานได้ทั่วไปมากที่สุดใน moreutils จนถึงตอนนี้คือฟองน้ำ (1) ซึ่งช่วยให้คุณทำสิ่งต่างๆดังนี้:

% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd

อย่างไรก็ตามspongeต้องทนทุกข์ทรมานจากปัญหาเดียวกันที่Steve Jessop แสดงความคิดเห็นที่นี่ หากคำสั่งใด ๆ ในไปป์ไลน์ก่อนที่จะspongeล้มเหลวไฟล์ต้นฉบับจะถูกเขียนทับ

$ mistyped_command my-important-file | sponge my-important-file
mistyped-command: command not found

อ๊ะmy-important-fileหายไปแล้ว


1
Sponge รู้ว่าจะถูกใช้เพื่อแทนที่ไฟล์อินพุตและในขั้นต้นจะสร้างไฟล์ชั่วคราวเพื่อหลีกเลี่ยงสภาวะการแข่งขัน เพื่อให้สามารถใช้งานได้ Sponge ต้องเป็นองค์ประกอบสุดท้ายในไปป์ไลน์และต้องได้รับอนุญาตให้สร้างไฟล์เอาต์พุตเอง (เมื่อเทียบกับการเปลี่ยนเส้นทางเอาต์พุตระดับเชลล์เป็นต้น) BTW: ดูเหมือนว่าการแก้ไขซอร์สโค้ดที่ง่ายสำหรับกรณี 'ล้มเหลว' คือการไม่เปลี่ยนชื่อไฟล์ temp ในกรณีของ pipefail (ไม่รู้ว่าทำไมฟองน้ำไม่มีตัวเลือกนั้น)
Brent Bradburn

ฉันคิดว่าถ้าคุณเพิ่มset -o pipefailที่จุดเริ่มต้นของสคริปต์ของคุณข้อผิดพลาดmistyped_command my-important-fileจะทำให้สคริปต์ออกทันทีก่อนที่จะดำเนินการspongeดังนั้นจึงรักษาไฟล์สำคัญไว้
Elouan Keryell-Even

6

จัดไปหนึ่งบรรทัด:

sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt

ในทางเทคนิคแล้วไม่มีการคัดลอกไปยังไฟล์ชั่วคราวและคำสั่ง 'mv' ควรเป็นแบบทันที


6
หืม ฉันยังคงเรียกว่า temp.txt.sort ไฟล์ชั่วคราว
JesperE

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

1
การขาดพื้นที่ดิสก์ซึ่งเป็นสาเหตุที่เป็นไปได้หรือสัญญาณ (ผู้ใช้กด CTRL-C)
Steve Jessop

5
หากคุณต้องการใช้สิ่งนี้ให้ใช้ && (ตรรกะและ) แทน; เพราะการใช้คำสั่งนั้นจะทำให้แน่ใจว่าหากคำสั่งล้มเหลวคำสั่งถัดไปจะไม่ถูกดำเนินการ ตัวอย่างเช่น: cp backup.tar /root/backup.tar && rm backup.tar หากคุณไม่มีสิทธิ์คัดลอกคุณจะปลอดภัยเนื่องจากไฟล์จะไม่ถูกลบ
daniels

1
เปลี่ยนคำตอบของฉันเพื่อพิจารณาข้อเสนอแนะของคุณขอบคุณ
davr

4

ฉันชอบsort file -o fileคำตอบ แต่ไม่ต้องการพิมพ์ชื่อไฟล์ซ้ำสองครั้ง

ใช้ BASH ขยายประวัติ :

$ sort file -o !#^

enterคว้าหาเรื่องแรกบรรทัดปัจจุบันเมื่อคุณกด

การจัดเรียงที่ไม่ซ้ำกันในสถานที่:

$ sort -u -o file !#$

คว้าอาร์กิวเมนต์สุดท้ายในบรรทัดปัจจุบัน


3

หลายคนได้กล่าวถึงตัวเลือก-o นี่คือส่วนของหน้าคน

จากหน้าคน:

   -o output-file
          Write output to output-file instead of to the  standard  output.
          If  output-file  is  one of the input files, sort copies it to a
          temporary file before sorting and writing the output to  output-
          file.

3

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

uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt

ฉันคิดว่ามันเป็นไปได้>ตัดทอนแฟ้มก่อนที่คำสั่ง (uniqในกรณีนี้) อ่านมัน
Martin

3

ทางเลือกอื่นนอกเหนือspongeจากที่พบบ่อยsed:

sed -ni r<(command file) file

มันทำงานสำหรับคำสั่งใด ๆ ( sort, uniq, tac, ... ) และการใช้งานที่รู้จักกันเป็นอย่างดีsedของ-iตัวเลือก (แก้ไขไฟล์ที่อยู่ในสถานที่)

คำเตือน:ลองใช้command fileก่อนเนื่องจากการแก้ไขไฟล์ในสถานที่นั้นไม่ปลอดภัยโดยธรรมชาติ


คำอธิบาย

ประการแรกคุณบอกsedไม่ได้ในการพิมพ์ (เดิม) สาย ( -nตัวเลือก ) และด้วยความช่วยเหลือของsedของrคำสั่งและbash 's ชดเชยกระบวนการเนื้อหาที่สร้างขึ้นโดย<(command file)จะมีการส่งออกที่บันทึกไว้ในสถานที่


ทำให้สิ่งต่างๆง่ายยิ่งขึ้น

คุณสามารถรวมโซลูชันนี้ไว้ในฟังก์ชัน:

ip_cmd() { # in place command
    CMD=${1:?You must specify a command}
    FILE=${2:?You must specify a file}
    sed -ni r<("$CMD" "$FILE") "$FILE"
}

ตัวอย่าง

$ cat file
d
b
c
b
a

$ ip_cmd sort file
$ cat file
a
b
b
c
d

$ ip_cmd uniq file
$ cat file
a
b
c
d

$ ip_cmd tac file
$ cat file
d
c
b
a

$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file



1

อ่านเกี่ยวกับตัวแก้ไขแบบไม่โต้ตอบ, ex.


หึ - นั่นเป็นความคิดที่ชั่วร้ายโดยสิ้นเชิง ฉันชอบมัน.
David Mackintosh

0

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

แก้ไข: อัปยศกับฉัน sort temp.txt -o temp.txtทำงานได้ดีเยี่ยม


ฉันอ่าน Q เช่นกันว่าเป็น "ในสถานที่" แต่การอ่านครั้งที่สองทำให้ฉันเชื่อว่าเขาไม่ได้ขอมันจริงๆ
epatel

0

วิธีแก้ปัญหาอื่น:

uniq file 1<> file

ควรสังเกตว่า<>เคล็ดลับใช้ได้เฉพาะในกรณีนี้เนื่องจากuniqมีความพิเศษในการคัดลอกบรรทัดอินพุตไปยังบรรทัดเอาต์พุตเท่านั้นโดยทิ้งบางส่วนไว้ระหว่างทาง ถ้าคำสั่งอื่น ๆ (เช่นsed) ถูกนำมาใช้ซึ่งจะเปลี่ยนการรับสัญญาณ (เช่นจะเปลี่ยนทุกaเข้าไปในaa) จากนั้นจะสามารถแทนที่fileในรูปแบบที่ไม่ทำให้รู้สึกใด ๆ และแม้กระทั่งวงอนันต์ให้การว่าการป้อนข้อมูลที่มีขนาดใหญ่พอ (มากกว่าหนึ่ง บัฟเฟอร์การอ่านเดี่ยว)
เดวิด
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.