มีคำสั่ง Unix เพื่อนำหน้าข้อมูลสตริงบางส่วนไปยังไฟล์ข้อความหรือไม่?
สิ่งที่ต้องการ:
prepend "to be prepended" text.txt
<<(echo "to be prepended") < text.txt | sponge text.txt
มีคำสั่ง Unix เพื่อนำหน้าข้อมูลสตริงบางส่วนไปยังไฟล์ข้อความหรือไม่?
สิ่งที่ต้องการ:
prepend "to be prepended" text.txt
<<(echo "to be prepended") < text.txt | sponge text.txt
คำตอบ:
sed -i.old '1s;^;to be prepended;' inFile
-i
เขียนการเปลี่ยนแปลงและทำการสำรองข้อมูลหากมีการให้ส่วนขยายใด ๆ (ในกรณีนี้.old
)1s;^;to be prepended;
แทนที่จุดเริ่มต้นของบรรทัดแรกด้วยสตริงการแทนที่ที่กำหนดโดยใช้;
เป็นตัวคั่นคำสั่ง\n
หลังคำนำหน้า; ขึ้นอยู่กับความต้องการของพวกเขา โซลูชันที่เรียบร้อย
inFile
รวมไดเรกทอรีในเส้นทางของมัน?
\n
ไฟล์. ควรอ่านความคิดเห็นก่อน ประณาม.
'1s;^;my-prepended-line\;\n;'
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt
git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';
และสิ่งนี้ยุ่งเหยิงเมื่อมันแปลง\t
และ\n
.
echo "to be prepended"$'\n'"$(cat text.txt)"
ฉันแปลกใจที่ไม่มีใครพูดถึงเรื่องนี้
cat <(echo "before") text.txt > newfile.txt
ซึ่งเป็นเนื้อหาที่เป็นธรรมชาติมากกว่าคำตอบที่ยอมรับ (การพิมพ์บางสิ่งบางอย่างและวางลงในคำสั่งการแทนที่เป็นคำศัพท์ที่ตอบโต้ได้ง่าย)
... และหักหลังสิ่งที่ไรอันกล่าวไว้ข้างต้นโดยที่sponge
คุณไม่จำเป็นต้องใช้ไฟล์ชั่วคราว:
sudo apt-get install moreutils
<<(echo "to be prepended") < text.txt | sponge text.txt
แก้ไข: ดูเหมือนว่าจะใช้ไม่ได้ใน Bourne Shell /bin/sh
ใช้สตริงที่นี่ - <<<
คุณสามารถทำได้:
<<< "to be prepended" < text.txt | sponge text.txt
<<(echo "to be prepended") < text.txt
และสิ่ง<<< "to be prepended" < text.txt
ก่อสร้างไม่ได้ผลในการทุบตี พวกเขาต้องการ zsh
นี่เป็นความเป็นไปได้อย่างหนึ่ง:
(echo "to be prepended"; cat text.txt) > newfile.txt
คุณอาจจะไม่สามารถเข้าถึงไฟล์ระดับกลางได้อย่างง่ายดาย
ทางเลือกอื่น (อาจยุ่งยากด้วยการหลบหนี):
sed -i '0,/^/s//to be prepended/' text.txt
cat <(echo "to be prepended") text.txt > newfile.txt
. ลองคิดดูฉันไม่แน่ใจว่าของฉันเกี่ยวข้องกันดังนั้นฉันจึงโพสต์คำตอบแยกต่างหาก
สิ่งนี้จะทำงานเพื่อสร้างผลลัพธ์ - หมายถึงอินพุตมาตรฐานซึ่งให้มาทางท่อจากเสียงสะท้อน
echo -e "to be prepended \n another line" | cat - text.txt
ในการเขียนไฟล์ใหม่จำเป็นต้องมีไฟล์ชั่วคราวเนื่องจากไม่สามารถไพพ์กลับเข้าไปในไฟล์อินพุตได้
echo "to be prepended" | cat - text.txt > text.txt.tmp
mv text.txt.tmp text.txt
text.txt
แม้ว่าจะเป็นไปตามที่ร้องขอ แต่จะแสดงบน stdout ผลลัพธ์อาจถูกแบ่งเป็นไฟล์อื่นหากใช้งานได้กับ OP
บันทึก:
การทำเช่นนั้นอาจมีผลข้างเคียงที่ไม่คาดคิดโดยเฉพาะอย่างยิ่งอาจแทนที่ symlink ด้วยไฟล์ปกติลงเอยด้วยสิทธิ์ที่แตกต่างกันในไฟล์และเปลี่ยนวันที่สร้าง (วันเกิด) ของไฟล์
sed -i
เช่นเดียวกับในคำตอบของเจ้าชายจอห์นเวสลีย์อย่างน้อยก็พยายามกู้คืนสิทธิ์เดิม แต่ก็มีข้อ จำกัด อื่น ๆ เช่นกัน
นี่เป็นทางเลือกง่ายๆที่ใช้ไฟล์ชั่วคราว (หลีกเลี่ยงการอ่านไฟล์อินพุตทั้งหมดลงในหน่วยความจำแบบที่โซลูชันของ shimeทำ):
{ printf 'to be prepended'; cat text.txt; } > tmp.txt && mv tmp.txt text.txt
การใช้กลุ่มคำสั่ง ( { ...; ...; }
) เล็กน้อยมีประสิทธิภาพมากขึ้นกว่าการใช้subshell ( (...; ...)
) ในขณะที่การแก้ปัญหาของ 0xC0000022L
ข้อดีคือ:
มันง่ายที่จะควบคุมว่าข้อความใหม่ควรจะใช้ได้โดยตรงกับบรรทัดแรกหรือว่ามันควรจะแทรกเป็นบรรทัดใหม่ (s) (เพียงผนวก\n
กับprintf
การโต้เถียง)
ไม่เหมือนกับsed
วิธีการแก้ปัญหาจะทำงานได้หากไฟล์อินพุตว่างเปล่า ( 0
ไบต์)
sed
วิธีการแก้ปัญหาได้ง่ายถ้าเจตนาคือการย่อหน้าหนึ่งหรือทั้งเส้นกับเนื้อหาที่มีอยู่ (สมมติว่าแฟ้มใส่เป็นไม่ว่างเปล่า):
sed
's i
แทรกฟังก์ชั่นสายทั้งหมด:
ด้วยGNU sed
:
# Prepends 'to be prepended' *followed by a newline*, i.e. inserts a new line.
# To prepend multiple lines, use '\n' as part of the text.
# -i.old creates a backup of the input file with extension '.old'
sed -i.old '1 i\to be prepended' inFile
ตัวแปรแบบพกพาที่ใช้งานได้กับ macOS / BSD sed
:
# Prepends 'to be prepended' *followed by a newline*
# To prepend multiple lines, escape the ends of intermediate
# lines with '\'
sed -i.old -e '1 i\
to be prepended' inFile
โปรดทราบว่าต้องขึ้นบรรทัดใหม่ตามตัวอักษรหลังจาก\
นั้น
การใช้ed
ยูทิลิตี้ POSIX ที่น่าเคารพ:
บันทึก:
ed
อ่านไฟล์อินพุตโดยรวมในหน่วยความจำอย่างสม่ำเสมอก่อนหากต้องการนำหน้าไปยังบรรทัดแรกโดยตรง (เช่นเดียวกับsed
จะใช้ไม่ได้หากไฟล์อินพุตว่างเปล่า ( 0
ไบต์)):
ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
-s
ed
ข้อความสถานะที่ถูกระงับed
เป็นเอกสารที่นี่หลายบรรทัด( <<EOF\n...\nEOF
) กล่าวคือผ่านทางstdin ; โดยการขยายสตริงเริ่มต้นจะดำเนินการในเอกสารดังกล่าว (ตัวแปรเชลล์ถูกสอดแทรก) อ้างตัวคั่นเปิดเพื่อระงับสิ่งนั้น (เช่น<<'EOF'
)1
ทำให้บรรทัดที่ 1 เป็นบรรทัดปัจจุบันs
ดำเนินการแทนที่สตริงตาม regex บนบรรทัดปัจจุบันเช่นเดียวกับในsed
; คุณอาจใส่บรรทัดใหม่ตามตัวอักษรในข้อความการแทนที่ได้ แต่ต้องเป็น\
-escapedw
เขียนผลลัพธ์กลับไปที่ไฟล์อินพุต (สำหรับการทดสอบแทนที่w
ด้วย,p
เพื่อพิมพ์ผลลัพธ์เท่านั้นโดยไม่ต้องแก้ไขไฟล์อินพุต)การย่อหน้าหนึ่งหรือทั้งเส้น :
เช่นเดียวกับsed
ที่i
ฟังก์ชั่นอย่างสม่ำเสมอเพิ่มขึ้นบรรทัดใหม่ต่อท้ายข้อความที่จะแทรก
ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
0 i
ทำให้0
(จุดเริ่มต้นของไฟล์) บรรทัดปัจจุบันและเริ่มโหมดแทรก ( i
); โปรดทราบว่าหมายเลขบรรทัดจะขึ้นอยู่กับ1
-.
บรรทัดของมันเองอาจไม่มีอะไรในตัว แต่คุณสามารถเขียนของคุณเองได้อย่างง่ายดายเช่นนี้:
#!/bin/bash
echo -n "$1" > /tmp/tmpfile.$$
cat "$2" >> /tmp/tmpfile.$$
mv /tmp/tmpfile.$$ "$2"
อะไรทำนองนั้นอย่างน้อย ...
$$
ไม่เพียงพอสำหรับรหัสการผลิตแม้ว่า (google symlink attack); แต่มันดีกว่าชื่อไฟล์แบบคงที่
mktemp
ในบางสถานการณ์ข้อความที่อยู่ข้างหน้าอาจใช้ได้เฉพาะจาก stdin จากนั้นชุดค่าผสมนี้จะทำงาน
echo "to be prepended" | cat - text.txt | tee text.txt
หากคุณต้องการที่จะละเว้นการส่งออกแล้วผนวกtee
> /dev/null
tee
ไม่ว่าจะไม่ยุ่งเหยิงtext.txt
ก่อนที่cat
จะอ่าน? ฉันคิดว่าไม่ - ซึ่งจะทำให้การแก้ปัญหานี้เป็นอันตรายต่อสุขภาพของไฟล์
lenA=1000000; yes a | head -c $lenA > a.txt; lenB=10000; b=$(yes b | head -c $lenB); echo "$b" | cat - a.txt | tee a.txt > /dev/null
. ถ้าlenA
เป็น 1000000 (ไฟล์ 1Mb) และlenB
เป็น 10,000 (ข้อความ 10Kb นำหน้า) ไฟล์ "a.txt" จะถูกเขียนทับด้วยตัวอักษร "b" 20Kb นี่พังโดยสิ้นเชิง ตอนนี้ถ้าคุณใช้ข้อความ 1Mb a.txt และ 1Mb เพื่อนำหน้าtee
เข้าไปในลูปที่สร้างไฟล์ 7Gb + ฉันต้องหยุดคำสั่ง ดังนั้นจึงเห็นได้ชัดว่าผลลัพธ์นั้นไม่สามารถคาดเดาได้สำหรับขนาดใหญ่ ฉันไม่มีข้อมูลว่าควรใช้กับขนาดเล็กหรือไม่
สารละลาย:
printf '%s\n%s' 'text to prepend' "$(cat file.txt)" > file.txt
โปรดทราบว่าสิ่งนี้ปลอดภัยสำหรับอินพุตทุกประเภทเนื่องจากไม่มีการขยาย ตัวอย่างเช่นหากคุณต้องการนำ!@#$%^&*()ugly text\n\t\n
หน้ามันจะทำงาน:
printf '%s\n%s' '!@#$%^&*()ugly text\n\t\n' "$(cat file.txt)" > file.txt
"$(cat file.txt)"
ส่วนสุดท้ายที่เหลืออยู่ในการพิจารณาคือการกำจัดช่องว่างในตอนท้ายของแฟ้มในระหว่างแทนคำสั่ง การแก้ไขปัญหาทั้งหมดนี้ค่อนข้างซับซ้อน หากคุณต้องการคงบรรทัดใหม่ไว้ที่ส่วนท้ายของ file.txt โปรดดูสิ่งนี้: https://stackoverflow.com/a/22607352/1091436
file.txt
ใหญ่กว่าที่สามารถใส่ลงในรายการอาร์กิวเมนต์printf
ได้
อีกวิธีหนึ่งโดยใช้sed
:
sed -i.old '1 {i to be prepended
}' inFile
หากบรรทัดที่จะนำหน้าเป็นแบบหลายเส้น:
sed -i.old '1 {i\
to be prepended\
multiline
}' inFile
sed -i '1ito be prepended' inFile
- หรืออนุญาตเฉพาะ GNU sed เท่านั้น?
sed
ที่i
คำสั่งตามด้วยเครื่องหมายและขึ้นบรรทัดใหม่และแต่ละบรรทัดของการป้อนข้อมูลยกเว้นปลายสุดท้ายด้วยเครื่องหมายเกินไป GNU sed
อนุญาตให้ shorthands ตามบรรทัดที่คุณถาม แต่เป็น GNU เท่านั้นsed
ที่ทำเช่นนั้น '
ตามที่ทดสอบใน Bash (ใน Ubuntu) หากเริ่มต้นด้วยไฟล์ทดสอบผ่าน;
echo "Original Line" > test_file.txt
คุณสามารถดำเนินการ;
echo "$(echo "New Line"; cat test_file.txt)" > test_file.txt
หรือถ้าเวอร์ชันของ bash เก่าเกินไปสำหรับ $ () คุณสามารถใช้ backticks;
echo "`echo "New Line"; cat test_file.txt`" > test_file.txt
และรับเนื้อหาต่อไปนี้ของ "test_file.txt";
New Line
Original Line
ไม่มีไฟล์ตัวกลางเพียง bash / echo
อีกวิธีหนึ่งที่ค่อนข้างตรงไปตรงมาคือ:
$ echo -e "string\n" $(cat file)
cat
การขยายตัวพารามิเตอร์ในราคาคู่เป็นเหตุผลที่ดีงามสำหรับdownvote echo
รหัสนี้จะแบ่งเนื้อหาของไฟล์เป็นคำพูดและการส่งผ่านแต่ละคำพูดเหล่านั้นเป็นอาร์กิวเมนต์ที่แยกต่างหากเพื่อ คุณสูญเสียขอบเขตอาร์กิวเมนต์เดิมคุณสูญเสียการขึ้นบรรทัดใหม่ / แท็บ / ฯลฯ และสตริงเช่น\t
และ\n
ในข้อความต้นฉบับจะถูกแทนที่ด้วยแท็บและบรรทัดใหม่แทนที่จะเก็บไว้เหมือนเดิม
ถ้าคุณชอบ vi / vim นี่อาจจะเป็นสไตล์ของคุณมากกว่า
printf '0i\n%s\n.\nwq\n' prepend-text | ed file
# create a file with content..
echo foo > /tmp/foo
# prepend a line containing "jim" to the file
sed -i "1s/^/jim\n/" /tmp/foo
# verify the content of the file has the new line prepened to it
cat /tmp/foo
ฉันขอแนะนำให้กำหนดฟังก์ชันจากนั้นนำเข้าและใช้ตามความจำเป็น
prepend_to_file() {
file=$1
text=$2
if ! [[ -f $file ]] then
touch $file
fi
echo "$text" | cat - $file > $file.new
mv -f $file.new $file
}
จากนั้นใช้มันดังนี้:
prepend_to_file test.txt "This is first"
prepend_to_file test.txt "This is second"
เนื้อหาไฟล์ของคุณจะเป็น:
This is second
This is first
ฉันกำลังจะใช้แนวทางนี้ในการติดตั้งตัวอัปเดตบันทึกการเปลี่ยนแปลง