Lệnh Unix để thêm văn bản vào một tệp


124

Có một lệnh Unix để thêm một số dữ liệu chuỗi vào một tệp văn bản không?

Cái gì đó như:

prepend "to be prepended" text.txt

Bạn đang tìm kiếm một lệnh duy nhất, hoặc một lệnh script / bí danh nhỏ có ổn không?
Levon

2
Nếu bạn kết hợp tất cả các câu trả lời, đây có lẽ là cách nhỏ gọn nhất:<<(echo "to be prepended") < text.txt | sponge text.txt
Sridhar Sarnobat

Câu trả lời:


110
sed -i.old '1s;^;to be prepended;' inFile
  • -ighi thay đổi tại chỗ và sao lưu nếu có bất kỳ phần mở rộng nào được đưa ra. (Trong trường hợp này, .old)
  • 1s;^;to be prepended;thay thế đầu dòng đầu tiên bằng chuỗi thay thế đã cho, sử dụng ;làm dấu phân cách lệnh.

19
Nếu bạn có thể thêm một số lời giải thích với lệnh này, nó sẽ hữu ích cho những người khác không quá quen thuộc với hoạt động của sed. OP có thể muốn chèn vào \nsau khi trả trước; tùy thuộc vào nhu cầu của họ. Giải pháp gọn gàng.
Levon

Vâng, một lời giải thích sẽ là tuyệt vời. Các inFilethư mục bao gồm trong đường dẫn của nó?
zakdances

3
@Levon Tôi hoàn toàn muốn chèn một \n. Nên đọc bình luận trước. Chỉ trích.
chishaku

Trong trường hợp của tôi, tôi muốn có một dấu chấm phẩy ở cuối dòng được chuẩn bị trước, vì vậy tôi đã đi với'1s;^;my-prepended-line\;\n;'
SamGamgee

5
Lưu ý rằng '\ n' chỉ hoạt động cho GNU sed, không phải BSD (như OSX, FreeBSD, v.v.). Để dễ di chuyển hơn với những thứ đó, hãy xem: stackoverflow.com/questions/1421478/ cấp
jwd

162
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt

2
Trong một dòng và trong tệp văn bản gốc! Đây là câu trả lời đơn giản chính xác. Tôi đề nghị dòng mới bổ sung \ n echo -e "được thêm trước \ n $ (cat text.txt)"> text.txt
VincentPerrin.com 7/2/2016

32
Giải pháp này có vấn đề ... nó chuyển đổi các lần xuất hiện của "\ n" thành các dòng mới thực sự ... có vấn đề nếu bạn đang chuẩn bị một tệp mã có chuỗi chứa "\ n".
Paul Go

1
Tôi đã có điều này trong tập tin của tôi git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';và điều này gây rối khi nó chuyển đổi \t\n.
hIpPy

8
Điều này (1) tải toàn bộ tệp vào bộ nhớ và (2) dán toàn bộ tệp vào một đối số dòng lệnh. Tốt cho những thứ đơn giản, nhưng hãy cẩn thận những hạn chế.
JWD

2
Lần cuối tôi thử, tôi tin rằng điều này sẽ phá vỡ các tệp thực sự lớn, trong đó con mèo không đọc được toàn bộ nội dung của text.txt trước khi nó bị ghi đè. Nếu tùy chọn -e trên echo là một vấn đề, bạn có thể trích dẫn một dòng mới trong bash hoặc doecho "to be prepended"$'\n'"$(cat text.txt)"
jbo5112

41

Quy trình thay thế

Tôi ngạc nhiên không ai đề cập đến điều này.

cat <(echo "before") text.txt > newfile.txt

được cho là tự nhiên hơn câu trả lời được chấp nhận (in một cái gì đó và đưa nó vào một lệnh thay thế là phản trực quan từ vựng).

... và đánh cắp những gì ryan đã nói ở trên, với spongebạn không cần một tập tin tạm thời:

sudo apt-get install moreutils
<<(echo "to be prepended") < text.txt | sponge text.txt

EDIT: Có vẻ như điều này không hoạt động trong Bourne Shell /bin/sh


Ở đây Chuỗi (chỉ zsh)

Sử dụng chuỗi ở đây - <<<, bạn có thể làm:

<<< "to be prepended" < text.txt | sponge text.txt

3
Câu trả lời này là người chiến thắng cho tôi. Đơn giản và có thể đạt được chỉ với nội dung. Làm tốt lắm!
PiersyP

1
@PiersyP Tôi đồng ý! Điều này đã giúp rất nhiều, cảm ơn bạn Sridhar-Sarnobat.

Vấn đề là có 1 tệp chứ không phải 2 tệp. Vì vậy, dòng đầu tiên của câu trả lời không thực sự hoạt động. Dòng cuối cùng có thể hoạt động (nhưng nó đòi hỏi bọt biển).
VasiliNovikov

1
Bọt biển làm gì?
Hemanta

1
Của bạn <<(echo "to be prepended") < text.txt<<< "to be prepended" < text.txtcông trình không hoạt động trong bash; họ yêu cầu zsh.
Anders Kaseorg

31

Đây là một khả năng:

(echo "to be prepended"; cat text.txt) > newfile.txt

có lẽ bạn sẽ không dễ dàng có được một tập tin trung gian.

Các lựa chọn thay thế (có thể cồng kềnh với thoát vỏ):

sed -i '0,/^/s//to be prepended/' text.txt

Chiến lược 1) là trực quan nhất của bất kỳ câu trả lời ở đây, tôi cảm thấy. Và một biến thể nhỏ : cat <(echo "to be prepended") text.txt > newfile.txt . Nghĩ về nó, tôi không chắc là của tôi có liên quan, vì vậy tôi đăng một câu trả lời riêng.
Sridhar Sarnobat

1
Phương thức đầu tiên sẽ không bảo vệ quyền truy cập tệp
Xlsx

19

Điều này sẽ làm việc để tạo thành đầu ra. - có nghĩa là đầu vào tiêu chuẩn, được cung cấp qua đường ống từ tiếng vang.

echo -e "to be prepended \n another line" | cat - text.txt

Để ghi lại tập tin, một tập tin tạm thời là bắt buộc vì không thể quay trở lại tập tin đầu vào.

echo "to be prepended" | cat - text.txt > text.txt.tmp
mv text.txt.tmp text.txt

2
điều này không đưa văn bản vào tệp text.txttheo yêu cầu, nhưng hiển thị nó trên thiết bị xuất chuẩn. Đầu ra có thể được chuyển sang một tệp khác nếu hoạt động cho OP
Levon

1
điều này chắc chắn sẽ in "được chuẩn bị trước" và sau đó là nội dung của "text.txt". Nhưng tôi muốn văn bản được thêm vào tệp
Một Hai Ba

1
và nếu tôi có văn bản nhiều dòng thì sao? Làm thế nào để tôi đặt nội dung nhân vật dòng mới?
Một Hai Ba

Điều này sẽ rất tuyệt nhưng bash không thích nó: $ echo RewriteBase / | cat - web / .htaccess> web / .htaccess cat: web / .htaccess: tệp đầu vào là tệp đầu ra
geek-merlin

14

Thích câu trả lời của Adam

Chúng ta có thể làm cho nó dễ dàng hơn để sử dụng bọt biển . Bây giờ chúng tôi không cần tạo một tệp tạm thời và đổi tên nó bằng cách

echo -e "to be prepended \n another line" | cat - text.txt | sponge text.txt

đây là giải pháp duy nhất làm việc cho tôi Thật buồn cười khi tên máy chủ của tôi là spPal.
Ömer

9

Nếu nó được chấp nhận để thay thế tệp đầu vào:

Ghi chú:

  • Làm như vậy có thể có tác dụng phụ không mong muốn, đáng chú ý là có khả năng thay thế một liên kết tượng trưng bằng một tệp thông thường, kết thúc bằng các quyền khác nhau trên tệp và thay đổi ngày tạo (khai sinh) của tệp.

  • sed -i, như trong câu trả lời của Hoàng tử John Wesley , ít nhất cũng cố gắng khôi phục các quyền ban đầu, nhưng các hạn chế khác cũng được áp dụng.

Đây là một giải pháp thay thế đơn giản sử dụng một tệp tạm thời (nó tránh đọc toàn bộ tệp đầu vào vào bộ nhớ theo cách mà giải pháp của shime thực hiện):

{ printf 'to be prepended'; cat text.txt; } > tmp.txt && mv tmp.txt text.txt

Sử dụng lệnh nhóm ( { ...; ...; }) hiệu quả hơn một chút so với sử dụng một subshell ( (...; ...)), như trong giải pháp của 0xC0000022L .

Những ưu điểm là:

  • Thật dễ dàng để kiểm soát liệu văn bản mới có nên được thêm trực tiếp vào dòng đầu tiên hay liệu nó có nên được chèn dưới dạng (các) dòng mới hay không (chỉ cần thêm \nvào printfđối số).

  • Không giống như sedgiải pháp, nó hoạt động nếu tệp đầu vào trống ( 0byte).


Các sedgiải pháp có thể được đơn giản hóa nếu mục đích là để thêm vào trước một hoặc nhiều toàn bộ dây chuyền đến các nội dung hiện có (giả sử các tập tin đầu vào không bị để trống):

sed's ichèn chức năng toàn bộ dòng:

Vớ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

Một biến thể di động cũng hoạt động với 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

Lưu ý rằng dòng mới theo nghĩa đen sau khi \được yêu cầu.


Nếu tệp đầu vào phải được chỉnh sửa tại chỗ (giữ nguyên nút của nó với tất cả các thuộc tính của nó):

Sử dụng edtiện ích POSIX đáng kính :

Ghi chú:

  • edluôn luôn đọc toàn bộ tệp đầu vào vào bộ nhớ.

Để thêm trực tiếp vào dòng đầu tiên (như với sed, điều này sẽ không hoạt động nếu tệp đầu vào hoàn toàn trống ( 0byte)):

ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
  • -sedthông điệp trạng thái bị chặn .
  • Lưu ý cách các lệnh được cung cấp eddưới dạng nhiều dòng ở đây - document ( <<EOF\n...\nEOF), tức là thông qua stdin ; theo mặc định mở rộng chuỗi được thực hiện trong các tài liệu đó (các biến shell được nội suy); trích dẫn dấu phân cách mở để triệt tiêu điều đó (ví dụ <<'EOF':).
  • 1 làm cho dòng thứ 1 thành dòng hiện tại
  • Hàm sthực hiện thay thế chuỗi dựa trên regex trên dòng hiện tại, như trong sed; bạn có thể bao gồm các dòng mới theo nghĩa đen trong văn bản thay thế, nhưng chúng phải được \ghi chú.
  • wghi kết quả trở lại tệp đầu vào (để kiểm tra, thay thế wbằng ,pchỉ in kết quả mà không sửa đổi tệp đầu vào).

Để thêm một hoặc nhiều dòng :

Như với sed, ihàm luôn luôn thêm một dòng mới vào văn bản sẽ được chèn.

ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
  • 0 ilàm cho 0(phần đầu của tệp) dòng hiện tại và bắt đầu chế độ chèn ( i); lưu ý rằng số dòng được 1dựa trên cơ sở khác .
  • Các dòng sau đây là văn bản để chèn trước dòng hiện tại, được kết thúc bằng .dòng riêng của nó.

7

Có lẽ không có gì tích hợp, nhưng bạn có thể tự viết khá dễ dàng, như thế này:

#!/bin/bash
echo -n "$1" > /tmp/tmpfile.$$
cat "$2" >> /tmp/tmpfile.$$
mv /tmp/tmpfile.$$ "$2"

Ít nhất là như thế ...


6
+1 để sử dụng $$ (PID hiện tại) như một phần của tên tệp tạm thời. Nếu tạo tập lệnh để sử dụng chung, điều này sẽ ngăn người dùng khác có khả năng ghi đè tệp tạm thời bằng cách chạy cùng một tập lệnh cùng một lúc.
E Brown

3
Tuy nhiên, việc sử dụng $$không đủ cho mã sản xuất (tấn công google symlink); nhưng nó chắc chắn tốt hơn một tên tệp tĩnh.
tripleee

1
chỉ cần sử dụngmktemp
mewa

7

Trong một số trường hợp, văn bản được chuẩn bị trước chỉ có thể có sẵn từ stdin. Sau đó, sự kết hợp này sẽ làm việc.

echo "to be prepended" | cat - text.txt | tee text.txt

Nếu bạn muốn bỏ qua teeđầu ra, sau đó nối thêm > /dev/null.


Tôi thực sự thích câu trả lời này. Đó là một cách rất sạch sẽ và rõ ràng để làm điều đó. Việc sử dụng tee là một giải pháp tự nhiên cho vấn đề cố gắng đưa đầu ra vào tệp đầu vào. Cũng không có hack với đổi tên. Tốt hơn so với giải pháp được bình chọn cao nhất hiện nay, vì nó không tạo ra một tệp mới. Về cơ bản, đây là cách gần nhất bạn sẽ nhận được >> để trả trước thay vì nối thêm.
DC2

Đây là câu trả lời tốt nhất và sạch nhất.
nerdoc

6
Có đảm bảo rằng teekhông bị tắc nghẽn text.txttrước khi catđọc nó không? Tôi nghĩ là không - điều này sẽ làm cho giải pháp này trở nên nguy hiểm đối với sức khỏe của tập tin.
Jonathan Leffler

3
@JonathanLeffler có lẽ là không. Tôi đã thử mã này để kiểm tra : 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. Nếu lenAlà 1000000 (tệp 1Mb) và lenB10000 (nạp trước văn bản 10Kb), thì tệp "a.txt" được ghi đè bằng 20Kb chữ cái "b". Điều này là hoàn toàn bị phá vỡ. Bây giờ, nếu bạn sử dụng 1Mb a.txt và văn bản 1Mb để trả trước, teeđi vào một vòng lặp tạo tệp 7Gb +, tôi đã phải dừng lệnh. Vì vậy, rõ ràng là kết quả không thể đoán trước đối với kích thước lớn. Tôi không có thông tin cho dù nó nên hoạt động trên kích thước nhỏ.
VasiliNovikov

3
Để đặt nó theo thuật ngữ khái niệm: Lệnh này sẽ dẫn đến mất dữ liệu nếu tệp đầu vào xảy ra lớn hơn kích thước bộ đệm đường ống của hệ thống của bạn , hiện tại thường là 64 KB.
mkuity0

7

Giải pháp:

printf '%s\n%s' 'text to prepend' "$(cat file.txt)" > file.txt

Lưu ý rằng điều này là an toàn trên tất cả các loại đầu vào, vì không có mở rộng. Ví dụ: nếu bạn muốn trả trước !@#$%^&*()ugly text\n\t\n, nó sẽ chỉ hoạt động:

printf '%s\n%s' '!@#$%^&*()ugly text\n\t\n' "$(cat file.txt)" > file.txt

Phần cuối cùng còn lại để xem xét là loại bỏ khoảng trắng ở cuối tệp trong khi thay thế lệnh "$(cat file.txt)". Tất cả các công việc xung quanh cho điều này là tương đối phức tạp. Nếu bạn muốn duy trì dòng mới ở cuối file.txt, hãy xem điều này: https://stackoverflow.com/a/22607352/1091436


Yêu nó. Siêu di động và không cần tạo tệp mới. Cảm ơn!
pyb

1
Vấn đề duy nhất xảy ra nếu nội dung của file.txtnó lớn hơn có thể được gắn vào danh sách đối số printf.
Jonathan Leffler

6

Một cách khác sử dụng sed:

sed -i.old '1 {i to be prepended
}' inFile

Nếu dòng được chuẩn bị là multiline:

sed -i.old '1 {i\ 
to be prepended\
multiline
}' inFile

Tại sao không sed -i '1ito be prepended' inFile- hoặc nó chỉ được phép cho GNU sed?
người dùng không xác định

@userunknown: Trong tiêu chuẩn sed, ilệnh được theo sau bởi dấu gạch chéo ngược và dòng mới và mỗi dòng đầu vào ngoại trừ các kết thúc cuối cùng cũng có dấu gạch chéo ngược. GNU sedcho phép viết tắt dọc theo dòng bạn hỏi, nhưng chỉ GNU sedmới làm như vậy. '
Jonathan Leffler

3

Như đã thử nghiệm trong Bash (trong Ubuntu), nếu bắt đầu bằng tệp thử nghiệm qua;

echo "Original Line" > test_file.txt

bạn có thể thực thi;

echo "$(echo "New Line"; cat test_file.txt)" > test_file.txt

hoặc, nếu phiên bản bash quá cũ với $ (), bạn có thể sử dụng backticks;

echo "`echo "New Line"; cat test_file.txt`" > test_file.txt

và nhận các nội dung sau của "test_file.txt";

New Line
Original Line

Không có tập tin trung gian, chỉ bash / echo.


2
câu trả lời hay nhất, tôi đã viết một lớp lót này để xoay tệp của mình bằng câu trả lời này: for i in $ (<file2.txt); làm tiếng vang "$ (echo $ i; cat file.txt)"> file.txt; làm xong;
Aurangzeb

2

Một giải pháp khá thẳng về phía trước là:

    $ echo -e "string\n" $(cat file)

Điều này làm việc cho tôi ... chỉ là một sửa đổi nhỏ, để duy trì đa dòng từ tệp đầu vào, bạn phải gói nó trong dấu ngoặc kép để giống như thế này: echo -e "string \ n" "$ (cat ~ / file.txt) "> ~ / File.txt;
Craig Wayne

2
Không có sự catmở rộng tham số trong dấu ngoặc kép là một lý do khá tốt cho một downvote . Mã này đang phân chia nội dung của tệp thành các từ và chuyển từng từ đó thành một đối số riêng biệt echo. Bạn mất các ranh giới đối số ban đầu, bạn mất các dòng mới / tab / v.v. và các chuỗi như \t\ntrong văn bản gốc được thay thế bằng các tab và dòng mới thay vì được giữ nguyên như cũ.
Charles Duffy

1

Nếu bạn thích vi / vim, đây có thể là phong cách của bạn hơn.

printf '0i\n%s\n.\nwq\n' prepend-text | ed file

0
# 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

0

Tôi khuyên bạn nên xác định một chức năng và sau đó nhập và sử dụng chức năng đó khi cần thiết.

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
}

Sau đó sử dụng nó như vậy:

prepend_to_file test.txt "This is first"
prepend_to_file test.txt "This is second"

Nội dung tệp của bạn sau đó sẽ là:

This is second
This is first

Tôi sắp sử dụng phương pháp này để thực hiện cập nhật nhật ký thay đổi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.