Đây có lẽ là một giải pháp phức tạp .
Tôi đang tìm kiếm một toán tử đơn giản như ">>", nhưng để trả trước.
Tôi sợ nó không tồn tại. Tôi sẽ phải làm một cái gì đó như
mv myfile tmp mèo myheader tmp> myfile
Bất cứ điều gì thông minh hơn?
Đây có lẽ là một giải pháp phức tạp .
Tôi đang tìm kiếm một toán tử đơn giản như ">>", nhưng để trả trước.
Tôi sợ nó không tồn tại. Tôi sẽ phải làm một cái gì đó như
mv myfile tmp mèo myheader tmp> myfile
Bất cứ điều gì thông minh hơn?
Câu trả lời:
Vụ hack dưới đây là một câu trả lời nhanh chóng có hiệu quả và nhận được rất nhiều sự ủng hộ. Sau đó, khi câu hỏi trở nên phổ biến hơn và thời gian trôi qua, những người bị xúc phạm bắt đầu báo cáo rằng nó có hiệu quả nhưng những điều kỳ lạ có thể xảy ra, hoặc nó hoàn toàn không hoạt động, vì vậy nó đã bị hạ bệ trong một thời gian. Vui như vậy.
Giải pháp khai thác việc triển khai chính xác các mô tả tệp trên hệ thống của bạn và, vì việc triển khai thay đổi đáng kể giữa các nixes, nên thành công của nó hoàn toàn phụ thuộc vào hệ thống, chắc chắn không thể mang theo và không nên dựa vào bất cứ điều gì thậm chí quan trọng.
Bây giờ, với tất cả những gì ngoài câu trả lời là:
Tạo một mô tả tệp khác cho tệp ( exec 3<> yourfile
) từ đó ghi vào đó ( >&3
) dường như khắc phục được tình trạng đọc / ghi trên cùng một tình huống khó xử. Hoạt động với tôi trên các tệp 600K với awk. Tuy nhiên, thử cùng một mẹo sử dụng 'mèo' không thành công.
Vượt qua phần bổ sung dưới dạng một biến để awk ( -v TEXT="$text"
) khắc phục vấn đề trích dẫn theo nghĩa đen, điều này ngăn cản việc thực hiện thủ thuật này với 'sed'.
#!/bin/bash
text="Hello world
What's up?"
exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3
Điều này vẫn sử dụng tệp tạm thời, nhưng ít nhất nó nằm trên một dòng:
echo "text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile
-
sau cat
?
echo -n "text"
yourfile
là một liên kết tượng trưng thì điều này sẽ không làm những gì bạn muốn.
echo '0a
your text here
.
w' | ed some_file
ed là biên tập viên tiêu chuẩn! http://www.gnu.org/fun/jokes/ed.msg.html
0r header.file
echo -e '0a\nyour text here\n.\nw' | ed some_file
John Mee: phương pháp của bạn không được đảm bảo để hoạt động và có thể sẽ thất bại nếu bạn nạp trước hơn 4096 byte nội dung (ít nhất đó là những gì xảy ra với gnu awk, nhưng tôi cho rằng các triển khai khác sẽ có các ràng buộc tương tự). Nó không chỉ thất bại trong trường hợp đó, mà nó sẽ đi vào một vòng lặp vô tận, nơi nó sẽ đọc đầu ra của chính nó, do đó làm cho tệp phát triển cho đến khi tất cả không gian có sẵn được lấp đầy.
Hãy thử nó cho chính mình:
exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3
(cảnh báo: giết nó sau một thời gian hoặc nó sẽ lấp đầy hệ thống tập tin)
Hơn nữa, rất nguy hiểm khi chỉnh sửa các tệp theo cách đó và đó là lời khuyên rất tệ, như thể có điều gì đó xảy ra trong khi tệp đang được chỉnh sửa (sự cố, đĩa đầy) bạn gần như được đảm bảo để lại tệp ở trạng thái không nhất quán.
Không thể không có tệp tạm thời, nhưng ở đây có một oneliner
{ echo foo; cat oldfile; } > newfile && mv newfile oldfile
Bạn có thể sử dụng các công cụ khác như ed hoặc perl để làm điều đó mà không cần tệp tạm thời.
Có thể đáng lưu ý rằng thường là một ý tưởng tốt để tạo tệp tạm thời một cách an toàn bằng cách sử dụng một tiện ích như mktemp , ít nhất là nếu tập lệnh sẽ được thực thi với quyền root. Ví dụ, bạn có thể làm như sau (một lần nữa trong bash):
(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )
Nếu bạn cần điều này trên các máy tính mà bạn điều khiển, hãy cài đặt gói "moreutils" và sử dụng "miếng bọt biển". Sau đó, bạn có thể làm:
cat header myfile | sponge myfile
{ echo "prepended text"; cat myfile } | sponge myfile
Sử dụng bash heredoc bạn có thể tránh sự cần thiết của tệp tmp:
cat <<-EOF > myfile
$(echo this is prepended)
$(cat myfile)
EOF
Điều này hoạt động vì $ (cat myfile) được đánh giá khi tập lệnh bash được đánh giá, trước khi con mèo có chuyển hướng được thực thi.
giả sử rằng tệp bạn muốn chỉnh sửa là my.txt
$cat my.txt
this is the regular file
Và tệp bạn muốn thêm vào là tiêu đề
$ cat header
this is the header
Hãy chắc chắn để có một dòng trống cuối cùng trong tệp tiêu đề.
Bây giờ bạn có thể đăng ký trước với
$cat header <(cat my.txt) > my.txt
Bạn kết thúc với
$ cat my.txt
this is the header
this is the regular file
Theo tôi biết điều này chỉ hoạt động trong 'bash'.
this is the header
trong my.txt. Ngay cả sau khi tôi cập nhật Bash để 4.3.42(1)-release
tôi cũng nhận được kết quả tương tự.
<(...)
) viết, do đó không có gì đảm bảo my.txt
được đọc đầy đủ , mà không có kỹ thuật này sẽ không hoạt động.
Khi bạn bắt đầu cố gắng làm những điều trở nên khó khăn trong shell-script, tôi sẽ khuyên bạn nên tìm cách viết lại tập lệnh bằng ngôn ngữ kịch bản "phù hợp" (Python / Perl / Ruby / etc)
Đối với việc chuẩn bị một dòng vào một tệp, bạn không thể thực hiện việc này thông qua đường ống, vì khi bạn làm bất cứ điều gì như thế cat blah.txt | grep something > blah.txt
, nó vô tình làm trống tệp. Có một lệnh tiện ích nhỏ gọi là sponge
bạn có thể cài đặt (bạn làmcat blah.txt | grep something | sponge blah.txt
và nó đệm nội dung của tệp, sau đó ghi nó vào tệp). Nó tương tự như một tệp tạm thời nhưng bạn không cần phải làm điều đó một cách rõ ràng. nhưng tôi sẽ nói rằng đó là một yêu cầu "tồi tệ" hơn, nói, Perl.
Có thể có một cách để làm điều đó thông qua awk, hoặc tương tự, nhưng nếu bạn phải sử dụng shell-script, tôi nghĩ rằng một tệp tạm thời là cách dễ nhất (/ chỉ?).
Giống như Daniel Velkov gợi ý, sử dụng tee.
Đối với tôi, đó là giải pháp thông minh đơn giản:
{ echo foo; cat bar; } | tee bar > /dev/null
EDIT: Điều này đã bị hỏng. Xem hành vi kỳ lạ khi chuẩn bị một tập tin với mèo và tee
Giải pháp cho vấn đề ghi đè đang sử dụng tee
:
cat header main | tee main > /dev/null
Chủ yếu là cho vui / vỏ golf, nhưng
ex -c '0r myheader|x' myfile
sẽ thực hiện các mẹo, và không có đường ống hoặc chuyển hướng. Tất nhiên, vi / ex không thực sự cho sử dụng không tương tác, vì vậy vi sẽ nhanh chóng xuất hiện.
Tại sao không chỉ đơn giản là sử dụng lệnh ed (như đã được đề xuất bởi fluffle ở đây)?
ed đọc toàn bộ tập tin vào bộ nhớ và tự động thực hiện chỉnh sửa tập tin tại chỗ!
Vì vậy, nếu tệp của bạn không lớn ...
# cf. "Editing files with the ed text editor from scripts.",
# http://wiki.bash-hackers.org/doku.php?id=howto:edit-ed
prepend() {
printf '%s\n' H 1i "${1}" . wq | ed -s "${2}"
}
echo 'Hello, world!' > myfile
prepend 'line to prepend' myfile
Tuy nhiên, một cách giải quyết khác sẽ là sử dụng các tệp xử lý tệp mở như được đề xuất bởi Jürgen Hötzel trong đầu ra Redirect từ sed 's / c / d /' myFile sang myFile
echo cat > manipulate.txt
exec 3<manipulate.txt
# Prevent open file from being truncated:
rm manipulate.txt
sed 's/cat/dog/' <&3 > manipulate.txt
Tất cả điều này có thể được đặt trên một dòng duy nhất, tất nhiên.
Một biến thể trên giải pháp của cb0 cho "không có tệp tạm thời" để thêm vào văn bản cố định:
echo "text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified )
Một lần nữa, điều này phụ thuộc vào thực thi shell phụ - (..) - để tránh con mèo từ chối có cùng một tệp cho đầu vào và đầu ra.
Lưu ý: Thích giải pháp này. Tuy nhiên, trong máy Mac của tôi, tệp gốc bị mất (nghĩ rằng nó không nên nhưng nó có). Điều đó có thể được khắc phục bằng cách viết giải pháp của bạn dưới dạng: echo "văn bản để trả trước" | mèo - file_to_be_modified | mèo> tmp_file; mv tmp_file file_to_be_modified
Đây là những gì tôi khám phá:
echo -e "header \n$(cat file)" >file
CẢNH BÁO: việc này cần thêm một chút công việc để đáp ứng nhu cầu của OP.
Cần có một cách để làm cho cách tiếp cận sed của @shixilun hoạt động bất chấp những nghi ngờ của anh ta. Phải có lệnh bash để thoát khoảng trắng khi đọc tệp vào chuỗi thay thế sed (ví dụ: thay thế các ký tự dòng mới bằng '\ n'. Các lệnh Shell vis
và cat
có thể xử lý các ký tự không thể in được, nhưng không phải khoảng trắng, vì vậy điều này sẽ không giải quyết được OP vấn đề:
sed -i -e "1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt
thất bại do các dòng mới thô trong tập lệnh thay thế, cần được thêm vào một ký tự tiếp tục dòng () và có lẽ theo sau là &, để giữ cho shell và sed hạnh phúc, như câu trả lời SO này
sed
có giới hạn kích thước 40K cho các lệnh thay thế tìm kiếm không toàn cầu (không có dấu / g sau mẫu), do đó có thể sẽ tránh được các vấn đề tràn bộ đệm đáng sợ của awk mà cảnh báo ẩn danh đã cảnh báo.
sed -i -e "1s/^/new first line\n/" old_file.txt
Một giải pháp với printf
:
new_line='the line you want to add'
target_file='/file you/want to/write to'
printf "%s\n$(cat ${target_file})" "${new_line}" > "${target_file}"
Bạn cũng có thể làm:
printf "${new_line}\n$(cat ${target_file})" > "${target_file}"
Nhưng trong trường hợp đó, bạn phải chắc chắn rằng không có bất kỳ %
nơi nào, kể cả nội dung của tệp mục tiêu, vì điều đó có thể được diễn giải và làm hỏng kết quả của bạn.
echo
có vẻ như là một lựa chọn an toàn hơn echo "my new line\n$(cat my/file.txt)" > my/file.txt
${new_line}
chứ không phải tệp mục tiêu
Bạn có thể sử dụng dòng lệnh perl:
perl -i -0777 -pe 's/^/my_header/' tmp
Trong đó -i sẽ tạo một sự thay thế nội tuyến của tệp và -0777 sẽ làm lu mờ toàn bộ tệp và làm cho ^ chỉ khớp với phần đầu. -pe sẽ in tất cả các dòng
Hoặc nếu my_header là một tệp:
perl -i -0777 -pe 's/^/`cat my_header`/e' tmp
Trường hợp / e sẽ cho phép một mã thay thế.
Tôi thích cách tiếp cận ed của fluffle là tốt nhất. Xét cho cùng, bất kỳ công cụ dòng lệnh nào của công cụ so với các lệnh biên tập theo kịch bản về cơ bản đều giống nhau ở đây; không thấy một giải pháp biên tập kịch bản "sạch sẽ" là ít hơn hoặc không có gì.
Đây là một lớp lót của tôi được thêm vào .git/hooks/prepare-commit-msg
để thêm vào một .gitmessage
tệp in-repo để cam kết thông báo:
echo -e "1r $PWD/.gitmessage\n.\nw" | ed -s "$1"
Ví dụ .gitmessage
:
# Commit message formatting samples:
# runlevels: boot +consolekit -zfs-fuse
#
Tôi đang làm 1r
thay vì 0r
, vì điều đó sẽ để lại dòng sẵn sàng để viết trống trên đầu tệp từ mẫu ban đầu. Đừng đặt một dòng trống lên trên của bạn .gitmessage
sau đó, bạn sẽ kết thúc với hai dòng trống. -s
ngăn chặn đầu ra thông tin chẩn đoán của ed.
Liên quan đến việc trải qua điều này, tôi phát hiện ra rằng đối với vim-buff cũng rất tốt để có:
[core]
editor = vim -c ':normal gg'
Tôi nghĩ rằng đây là biến thể sạch nhất của ed:
cat myheader | { echo '0a'; cat ; echo -e ".\nw";} | ed myfile
như một chức năng:
function prepend() { { echo '0a'; cat ; echo -e ".\nw";} | ed $1; }
cat myheader | prepend myfile
Nếu bạn đang viết kịch bản trong BASH, thực tế, bạn chỉ có thể phát hành:
mèo - yourfile / tmp / out && mv / tmp / out yourfile
Đó thực sự là trong Ví dụ phức tạp mà chính bạn đã đăng trong câu hỏi của riêng bạn.
cat - yourfile <<<"text" > /tmp/out && mv /tmp/out yourfile
, tuy nhiên điều đó đủ khác với câu trả lời của tôi rằng đó phải là câu trả lời của riêng họ.
IMHO không có giải pháp shell (và sẽ không bao giờ là một) sẽ hoạt động ổn định và đáng tin cậy bất kể kích thước của hai tệp myheader
và myfile
. Lý do là nếu bạn muốn làm điều đó mà không lặp lại một tệp tạm thời (và không để cho trình bao lặp lại âm thầm thành một tệp tạm thời, ví dụ như thông qua các cấu trúc như exec 3<>myfile
, chuyển sangtee
, v.v.
Giải pháp "thực" mà bạn đang tìm kiếm cần tìm hiểu về hệ thống tệp và do đó, nó không có sẵn trong không gian người dùng và sẽ phụ thuộc vào nền tảng: bạn đang yêu cầu sửa đổi con trỏ hệ thống tệp được sử dụng theo myfile
giá trị hiện tại của con trỏ hệ thống tệp cho myheader
và thay thế trong hệ thống tập tin với EOF
các myheader
với một liên kết bị xích vào địa chỉ hệ thống tập tin hiện tại chỉ bằngmyfile
. Điều này không tầm thường và rõ ràng không thể được thực hiện bởi một người không phải là siêu người dùng, và có lẽ không phải bởi siêu người dùng đó ... Chơi với các nút, v.v.
Tuy nhiên, bạn có thể ít nhiều giả mạo điều này bằng cách sử dụng các thiết bị lặp. Xem ví dụ chủ đề SO này .
mktemp
vậy? Bạn luôn có thể dọn sạch tệp tạm thời sau đó ...