Shell satu liner untuk menambahkan ke file

131

Ini mungkin solusi yang kompleks .

Saya mencari operator sederhana seperti ">>", tetapi untuk membuat prapengisian.

Saya khawatir itu tidak ada. Saya harus melakukan sesuatu seperti

 mv myfile tmp
 cat myheader tmp> myfile

Adakah yang lebih pintar?

elmarco
sumber
Ada apa dengan ini mktemp? Anda selalu dapat membersihkan file temp setelahnya ...
candu
Terkait: stackoverflow.com/questions/9533679/…
Anton Tarasenko

Jawaban:

29

The hack di bawah ini adalah jawaban yang off-the-manset cepat yang bekerja dan menerima banyak upvotes. Kemudian, ketika pertanyaan itu menjadi lebih populer dan lebih banyak waktu berlalu, orang-orang yang marah mulai melaporkan bahwa itu agak berhasil, tetapi hal-hal aneh bisa terjadi, atau itu sama sekali tidak berhasil, jadi pertanyaan itu secara turun-temurun dimundurkan untuk sementara waktu. Sangat menyenangkan.

Solusinya mengeksploitasi implementasi yang tepat dari deskriptor file pada sistem Anda dan, karena implementasi bervariasi secara signifikan antara nixes, keberhasilannya sepenuhnya bergantung pada sistem, pasti non-portabel, dan tidak boleh diandalkan untuk apa pun yang bahkan sangat penting.

Sekarang, dengan semua itu, jawabannya adalah:


Membuat deskriptor file lain untuk file ( exec 3<> yourfile) kemudian menulis untuk itu ( >&3) tampaknya mengatasi baca / tulis pada dilema file yang sama. Bekerja untuk saya pada file 600K dengan awk. Namun mencoba trik yang sama menggunakan 'kucing' gagal.

Melewati prependage sebagai variabel to awk ( -v TEXT="$text") mengatasi masalah kutipan literal yang mencegah melakukan trik ini dengan 'sed'.

#!/bin/bash
text="Hello world
What's up?"

exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3
John Mee
sumber
3
PERINGATAN: periksa output untuk memastikan tidak ada yang berubah kecuali baris pertama. Saya menggunakan solusi ini, dan meskipun tampaknya berhasil, saya perhatikan bahwa beberapa baris baru tampaknya ditambahkan. Saya tidak mengerti dari mana ini berasal, jadi saya tidak dapat memastikan bahwa solusi ini benar-benar memiliki masalah, tetapi waspadalah.
conradlee
1
Perhatikan dalam contoh bash di atas bahwa tanda kutip ganda membentang di atas carriage return untuk menunjukkan beberapa baris yang berurutan. Periksa shell Anda dan editor teks bersikap kooperatif. Terlintas dalam pikiran.
John Mee
4
Gunakan ini dengan hati-hati! Lihat komentar berikut untuk alasannya: stackoverflow.com/questions/54365/…
Alex
3
Jika sekarang tidak dapat diandalkan, bagaimana dengan memperbarui jawaban Anda dengan solusi yang lebih baik?
zakdances
Sebuah hack berguna jika dan hanya jika diketahui tepatnya mengapa ia bekerja dan, oleh karena itu, di bawah apa yang hati-hati mengamati kendala . Namun demikian, penafian Anda, peretasan Anda tidak memenuhi kriteria ini ("sepenuhnya bergantung pada sistem", "tampaknya dapat diatasi", "bekerja untuk saya"). Jika kami menganggap penafian Anda dengan serius, tidak ada yang harus menggunakan peretasan Anda - dan saya setuju. Jadi apa yang ditinggalkan untuk kita? Gangguan bising. Saya melihat dua opsi: (a) hapus jawaban Anda, atau (b) ubah menjadi dongeng peringatan yang menjelaskan mengapa - mungkin menggoda - pendekatan ini tidak dapat bekerja secara umum .
mklement0
102

Ini masih menggunakan file temp, tapi setidaknya itu ada dalam satu baris:

echo "text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile

Kredit: BASH: Sertakan Teks / Baris ke File

Jason Navarrete
sumber
4
Apa yang dilakukan -setelah itu cat?
makbol
Apakah ada cara untuk menambahkan "teks" tanpa baris baru setelahnya?
chishaku
@chishakuecho -n "text"
Sparhawk
3
Peringatan: jika yourfilesymlink ini tidak akan melakukan apa yang Anda inginkan.
dshepherd
Apakah jawabannya berbeda dengan ini: echo "text"> / tmp / out; cat file Anda >> / tmp / out; mv / tmp / keluar file Anda ??
Richard
31
echo '0a
your text here
.
w' | ed some_file

ed adalah Editor Standar! http://www.gnu.org/fun/jokes/ed.msg.html

mengembang
sumber
7
atau, alih-alih meng-hardcoding teks yang akan dimasukkan:0r header.file
glenn jackman
Karena pertanyaannya menanyakan satu liner:echo -e '0a\nyour text here\n.\nw' | ed some_file
raphinesse
20

John Mee: metode Anda tidak dijamin berfungsi, dan mungkin akan gagal jika Anda menambahkan lebih dari 4096 byte barang (setidaknya itulah yang terjadi dengan gnu awk, tapi saya rasa implementasi lain akan memiliki kendala yang sama). Tidak hanya itu akan gagal dalam kasus itu, tetapi itu akan memasuki loop tanpa akhir di mana ia akan membaca outputnya sendiri, sehingga membuat file tumbuh sampai semua ruang yang tersedia terisi.

Cobalah sendiri:

exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3

(peringatan: bunuh setelah beberapa saat atau itu akan mengisi sistem file)

Selain itu, sangat berbahaya untuk mengedit file seperti itu, dan itu saran yang sangat buruk, seolah-olah terjadi sesuatu saat file sedang diedit (crash, disk penuh) Anda hampir dijamin akan dibiarkan dengan file dalam keadaan tidak konsisten.

anonim
sumber
6
Ini mungkin komentar, bukan jawaban terpisah.
lkraav
10
@ Ikraav: mungkin, tetapi "komentar" sangat panjang dan rumit (dan bermanfaat), itu tidak akan terlihat bagus dan mungkin tidak cocok dengan kapasitas komentar StackOverflow yang terbatas pula.
Hendy Irawan
18

Tidak mungkin tanpa file temp, tapi begini oneliner

{ echo foo; cat oldfile; } > newfile && mv newfile oldfile

Anda dapat menggunakan alat lain seperti ed atau perl untuk melakukannya tanpa file temp.

Vinko Vrsalovic
sumber
16

Mungkin perlu dicatat bahwa sering kali merupakan ide yang baik untuk menghasilkan file sementara dengan aman menggunakan utilitas seperti mktemp , setidaknya jika skrip akan dijalankan dengan hak akses root. Misalnya, Anda dapat melakukan hal berikut (lagi dalam bash):

(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )
ehdr
sumber
15

Jika Anda memerlukan ini di komputer yang Anda kontrol, instal paket "moreutils" dan gunakan "spon". Maka Anda dapat melakukan:

cat header myfile | sponge myfile
pengguna2227573
sumber
Ini bekerja dengan baik bagi saya ketika dikombinasikan dengan jawaban @Vinko Vrsalovic:{ echo "prepended text"; cat myfile } | sponge myfile
Jesse Hallett
13

Menggunakan bash heredoc Anda dapat menghindari kebutuhan file tmp:

cat <<-EOF > myfile
  $(echo this is prepended)
  $(cat myfile)
EOF

Ini berfungsi karena $ (cat myfile) dievaluasi ketika skrip bash dievaluasi, sebelum cat dengan redirect dieksekusi.

Eric Woodruff
sumber
9

dengan asumsi bahwa file yang ingin Anda edit adalah my.txt

$cat my.txt    
this is the regular file

Dan file yang ingin Anda tambahkan adalah tajuk

$ cat header
this is the header

Pastikan untuk memiliki baris kosong terakhir di file header.
Sekarang Anda bisa menambahkannya dengan

$cat header <(cat my.txt) > my.txt

Anda berakhir dengan

$ cat my.txt
this is the header
this is the regular file

Sejauh yang saya tahu ini hanya bekerja di 'bash'.

cb0
sumber
mengapa ini bekerja? Apakah tanda kurung menyebabkan kucing tengah mengeksekusi di shell terpisah dan membuang seluruh file sekaligus?
Catskul
Saya pikir <(cat my.txt) membuat file sementara di suatu tempat di sistem file. File ini kemudian dibaca sebelum file asli dimodifikasi.
cb0
Seseorang pernah menunjukkan kepada saya apa yang dapat Anda lakukan dengan sintaks "<()". Namun saya tidak pernah belajar bagaimana metode ini disebut tidak dapat menemukan sesuatu di halaman bash. Jika seseorang tahu lebih banyak tentangnya, beri tahu saya.
cb0
1
Tidak bekerja untuk saya. Tidak tahu kenapa Saya menggunakan GNU bash, versi 3.2.57 (1) -release (x86_64-apple-darwin14), saya menggunakan OS X Yosemite. Saya berakhir dengan dua baris this is the headerdi my.txt. Bahkan setelah saya memperbarui Bash untuk 4.3.42(1)-releasemendapatkan hasil yang sama.
Kohányi Róbert
1
Bash tidak membuat file sementara, ia membuat FIFO (pipa) yang ditulis oleh perintah dalam proses substitusi ( <(...)) , jadi tidak ada jaminan yang my.txtdibaca secara penuh di muka , yang tanpanya teknik ini tidak akan berfungsi.
mklement0
9

Ketika Anda mulai mencoba melakukan hal-hal yang menjadi sulit dalam shell-script, saya akan sangat menyarankan melihat ke dalam penulisan ulang skrip dalam bahasa scripting yang "tepat" (Python / Perl / Ruby / etc)

Adapun untuk mendahului baris ke file, tidak mungkin untuk melakukan ini melalui perpipaan, seperti ketika Anda melakukan hal seperti cat blah.txt | grep something > blah.txt, itu secara tidak sengaja mengosongkan file. Ada perintah utilitas kecil yang disebut spongeAnda dapat menginstal (Anda lakukan cat blah.txt | grep something | sponge blah.txtdan itu buffer konten file, kemudian menulisnya ke file). Ini mirip dengan file temp tetapi Anda tidak perlu melakukan itu secara eksplisit. tapi saya akan mengatakan itu persyaratan "lebih buruk" daripada, katakanlah, Perl.

Mungkin ada cara untuk melakukannya melalui awk, atau serupa, tetapi jika Anda harus menggunakan shell-script, saya pikir file temp adalah cara termudah (/ hanya?) ..

dbr
sumber
7

Seperti yang disarankan Daniel Velkov, gunakan tee.
Bagi saya, itu solusi cerdas sederhana:

{ echo foo; cat bar; } | tee bar > /dev/null
Max Tsepkov
sumber
7

EDIT: Ini rusak. Lihat Perilaku aneh saat membuat ulang file dengan cat dan tee

Solusi untuk masalah yang ditimpa menggunakan tee:

cat header main | tee main > /dev/null
Daniel Velkov
sumber
Tunggu sebentar. Berakhir dengan "tee: main: Tidak ada ruang tersisa di perangkat". main adalah beberapa GB, meskipun kedua file teks asli hanya beberapa KB.
Marcos
3
Jika solusi yang Anda posting rusak (dan sebenarnya memenuhi harddrives pengguna), bantulah komunitas dan hapus jawaban Anda.
aioobe
1
Bisakah Anda mengarahkan saya ke pedoman yang mengatakan bahwa jawaban yang salah harus dihapus?
Daniel Velkov
3

Yang saya gunakan. Yang ini memungkinkan Anda untuk menentukan pesanan, karakter tambahan, dll sesuai keinginan Anda:

echo -e "TEXTFIRSt\n$(< header)\n$(< my.txt)" > my.txt

PS: hanya itu tidak berfungsi jika file berisi teks dengan garis miring terbalik, karena itu akan ditafsirkan sebagai karakter melarikan diri

nemisj
sumber
3

Sebagian besar untuk bersenang-senang / golf shell, tetapi

ex -c '0r myheader|x' myfile

akan melakukan trik, dan tidak ada jalur pipa atau pengalihan. Tentu saja, vi / ex bukan untuk penggunaan non-aktif, jadi vi akan muncul sebentar.

benjwadams
sumber
Resep terbaik dari sudut pandang saya karena menggunakan perintah incumbent dan melakukan itu dengan cara langsung.
Diego
2

Mengapa tidak menggunakan perintah ed saja (seperti yang disarankan oleh fluffle di sini)?

ed membaca seluruh file ke dalam memori dan secara otomatis melakukan edit file di tempat!

Jadi, jika file Anda tidak terlalu besar ...

# 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

Namun solusi lain akan menggunakan pegangan file terbuka seperti yang disarankan oleh Jürgen Hötzel dalam output Redirect dari sed 's / c / d /' myFile ke 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

Semua ini bisa diletakkan pada satu baris, tentu saja.

Torf
sumber
2

Varian pada solusi cb0 untuk "tanpa file temp" untuk menambahkan teks tetap:

echo "text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified ) 

Sekali lagi ini bergantung pada eksekusi sub-shell - (()) - untuk menghindari kucing menolak memiliki file yang sama untuk input dan output.

Catatan: Menyukai solusi ini. Namun, di Mac saya file asli hilang (pikir seharusnya tidak tetapi tidak). Itu dapat diperbaiki dengan menulis solusi Anda sebagai: echo "text to prepend" | cat - file_to_be_modified | cat> tmp_file; mv tmp_file file_to_be_modified

Alberto A. Medina
sumber
1
Saya juga menemukan file asli hilang. Ubuntu Lucid.
Hedgehog
2

Inilah yang saya temukan:

echo -e "header \n$(cat file)" >file
Hammad Akhwand
sumber
1
Ini sama dengan saran sebelumnya oleh @nemisj No?
Hedgehog
2
sed -i -e '1rmyheader' -e '1{h;d}' -e '2{x;G}' myfile
lemah
sumber
2

PERINGATAN: ini membutuhkan sedikit lebih banyak pekerjaan untuk memenuhi kebutuhan OP.

Harus ada cara untuk membuat pendekatan sed oleh @shixilun bekerja terlepas dari keraguannya. Harus ada perintah bash untuk keluar dari spasi ketika membaca file ke string pengganti sed (misalnya, ganti karakter baris baru dengan '\ n'. Perintah Shell visdan catdapat menangani karakter yang tidak dapat dicetak, tetapi bukan spasi, jadi ini tidak akan menyelesaikan OP masalah:

sed -i -e "1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt

gagal karena baris baru mentah dalam skrip pengganti, yang perlu diawali dengan karakter kelanjutan garis () dan mungkin diikuti oleh &, untuk menjaga shell dan sed bahagia, seperti jawaban SO ini

sed memiliki batas ukuran 40K untuk perintah pencarian-ganti non-global (tanpa trailing / g setelah pola) sehingga kemungkinan akan menghindari masalah buffer overrun menakutkan dari awk yang memperingatkan anonim.

hobs
sumber
2
sed -i -e "1s/^/new first line\n/" old_file.txt
shixilun
sumber
persis apa yang saya cari, tetapi memang bukan jawaban yang tepat untuk pertanyaan
masterxilo
2

Dengan $ (command) Anda dapat menulis output dari suatu perintah ke dalam variabel. Jadi saya melakukannya dalam tiga perintah dalam satu baris dan tidak ada file temp.

originalContent=$(cat targetfile) && echo "text to prepend" > targetfile && echo "$originalContent" >> targetfile
JuSchu
sumber
1

Jika Anda memiliki file besar (beberapa ratus kilobyte dalam kasus saya) dan akses ke python, ini jauh lebih cepat daripada catsolusi pipa:

python -c 'f = "filename"; t = open(f).read(); open(f, "w").write("text to prepend " + t)'

crizCraig
sumber
1

Solusi dengan 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}"

Anda juga bisa:

printf "${new_line}\n$(cat ${target_file})" > "${target_file}"

Tetapi dalam hal ini Anda harus yakin tidak ada di %mana pun, termasuk konten file target, karena itu dapat ditafsirkan dan mengacaukan hasil Anda.

pengguna137369
sumber
2
Ini tampaknya meledak (dan memotong file Anda!) Jika Anda memiliki sesuatu dalam file target Anda yang printf tafsirkan sebagai opsi format.
Alan H.
echosepertinya pilihan yang lebih aman. echo "my new line\n$(cat my/file.txt)" > my/file.txt
Alan H.
@AlanH. Saya memperingatkan bahaya dalam jawabannya.
user137369
Sebenarnya, Anda hanya memperingatkan tentang persen dalam ${new_line}, bukan file target
Alan H.
Bisakah Anda lebih eksplisit? Ini adalah peringatan IMO yang sangat besar. "Di mana saja" tidak membuat ini sangat jelas.
Alan H.
1

Anda dapat menggunakan baris perintah perl:

perl -i -0777 -pe 's/^/my_header/' tmp

Di mana -i akan membuat pengganti inline file dan -0777 akan menyeruput seluruh file dan membuat ^ cocok hanya dengan permulaan. -pe akan mencetak semua baris

Atau jika my_header adalah file:

perl -i -0777 -pe 's/^/`cat my_header`/e' tmp

Dimana / e akan memungkinkan eval kode dalam substitusi.

pengguna5704481
sumber
0
current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file

di mana "my_file" adalah file untuk menambahkan "my_string" ke.

vinyll
sumber
0

Saya menyukai pendekatan ed @ fluffle yang terbaik. Lagi pula, saklar baris perintah alat apa pun vs perintah editor yang ditulis pada dasarnya adalah hal yang sama di sini; tidak melihat solusi editor scripted "kebersihan" menjadi lebih rendah atau yang lainnya.

Inilah one-liner saya yang ditambahkan .git/hooks/prepare-commit-msguntuk menambahkan .gitmessagefile in-repo untuk melakukan pesan:

echo -e "1r $PWD/.gitmessage\n.\nw" | ed -s "$1"

Contoh .gitmessage:

# Commit message formatting samples:
#       runlevels: boot +consolekit -zfs-fuse
#

Saya melakukan 1ralih - alih 0r, karena itu akan meninggalkan baris kosong siap-untuk-menulis di atas file dari template asli. Jangan meletakkan garis kosong di atas Anda .gitmessage, Anda akan berakhir dengan dua garis kosong. -smenekan keluaran info diagnostik ed.

Sehubungan dengan melalui ini, saya menemukan bahwa untuk vim-buff juga baik untuk memiliki:

[core]
        editor = vim -c ':normal gg'
lkraav
sumber
0

variabel, bagaimana?

NEWFILE=$(echo deb http://mirror.csesoc.unsw.edu.au/ubuntu/ $(lsb_release -cs) main universe restricted multiverse && cat /etc/apt/sources.list)
echo "$NEWFILE" | sudo tee /etc/apt/sources.list
Jayen
sumber
0

Saya pikir ini adalah variasi terbersih dari ed:

cat myheader | { echo '0a'; cat ; echo -e ".\nw";} | ed myfile

sebagai fungsi:

function prepend() { { echo '0a'; cat ; echo -e ".\nw";} | ed $1; }

cat myheader | prepend myfile
Dave Butler
sumber
0

Jika Anda menulis dalam BASH, sebenarnya, Anda bisa menerbitkan:

cat - file Anda / tmp / out && mv / tmp / keluar file Anda

Itu sebenarnya dalam Contoh Kompleks yang Anda poskan sendiri dalam pertanyaan Anda sendiri.

Tim Kennedy
sumber
Seorang editor menyarankan untuk mengubah ini cat - yourfile <<<"text" > /tmp/out && mv /tmp/out yourfile, namun itu cukup berbeda dari jawaban saya bahwa itu seharusnya jawabannya sendiri.
Tim Kennedy
0

IMHO tidak ada solusi shell (dan tidak akan pernah menjadi satu) yang akan bekerja secara konsisten dan andal apa pun ukuran kedua file myheaderdan myfile. Alasannya adalah bahwa jika Anda ingin melakukannya tanpa berulang ke file sementara (dan tanpa membiarkan shell berulang secara diam-diam ke file sementara, misalnya melalui konstruksi seperti exec 3<>myfile, perpipaan ketee , dll.

Solusi "nyata" yang Anda cari perlu mengutak-atik filesystem, dan karena itu tidak tersedia di userspace dan akan bergantung pada platform: Anda meminta untuk memodifikasi pointer filesystem yang digunakan dengan myfilenilai saat ini dari pointer filesystem untuk myheaderdan mengganti di filesystem EOFdari myheaderdengan link dirantai ke alamat filesystem saat ini ditunjuk oleh myfile. Ini bukan hal sepele dan jelas tidak bisa dilakukan oleh yang bukan pengguna super, dan mungkin juga bukan oleh pengguna super ... Bermain dengan inode, dll.

Anda bisa lebih atau kurang memalsukan ini menggunakan perangkat loop. Lihat misalnya utas SO ini .

Jaybee
sumber