Jak dodać tekst na początku pliku w Bash?

285

Cześć Chcę dodać tekst do pliku. Na przykład chcę dodać zadania na początku pliku todo.txt. Wiem o echo 'task goes here' >> todo.txttym, ale to dodaje wiersz na końcu pliku (nie to, czego chcę).

użytkownik479534
źródło
2
Jeśli nie ma skutecznego sposobu, aby to zrobić w przypadku dużych plików: unix.stackexchange.com/questions/87772/...
Ciro Santilli 22 改造 中心 法轮功 六四 事件
@ czy możesz przyjrzeć się tej metodzie superuser.com/a/1352628/11116
Evan Carroll

Odpowiedzi:

374
echo 'task goes here' | cat - todo.txt > temp && mv temp todo.txt

lub

sed -i '1s/^/task goes here\n/' todo.txt

lub

sed -i '1itask goes here' todo.txt
Dennis Williamson
źródło
4
pierwszy działa świetnie! czy mógłbyś wyjaśnić logikę? nie jestem szczególnie pewien, jak interpretować składnię.
user479534
44
@ user8347: Pipe ( |) wiadomość ( echo '...'), do catktórej używa -(standardowe wejście) jako pierwszy plik i todo.txtjako drugi. catconCATenates wielu plików. Wyślij wyjście ( >) do pliku o nazwie temp. Jeśli nie ma błędów ( &&) z catpotem rename ( mv) z temppliku z powrotem do oryginalnego pliku ( todo.txt).
Dennis Williamson,
1
@itaifrenkel: Musiałbym zobaczyć, co zrobiłeś, ale jeśli catotrzyma dosłowny ukośnik odwrotny n, nie przekształci go w nowy wiersz. Coś jeszcze musiało to zrobić. Zamiast tego catspróbuj potokować, hexdump -Caby sprawdzić, czy faktycznie wysyłasz odwrotny ukośnik i n, czy jest to nowy wiersz. Możesz także spróbować cat -ewyświetlić zakończenia linii.
Dennis Williamson
1
Używanie 2 i 3 (3 wydaje mi się prostsze) pozwala na dodawanie tekstu do wielu plików jednocześnie.
Felix
4
@Kira: 1Środki wykonują następne polecenie tylko w pierwszej linii pliku, a ipolecenie jest wstawiane. Zajrzyj na stronę manuala w sekcji „Adresy” oraz w sekcji „Polecenia zerowe lub z jednym adresem”.
Dennis Williamson
73

Moim zdaniem prostszą opcją jest:

echo -e "task goes here\n$(cat todo.txt)" > todo.txt

Działa to, ponieważ polecenie wewnątrz $(...)jest wykonywane przed todo.txtnadpisaniem> todo.txt

Podczas gdy inne odpowiedzi działają dobrze, o wiele łatwiej mi to zapamiętać, ponieważ codziennie używam echa i kota.

EDYCJA: To rozwiązanie jest bardzo złym pomysłem, jeśli występują jakieś ukośniki odwrotne todo.txt, ponieważ dzięki -eflagom echo je zinterpretuje. Innym, o wiele łatwiejszym sposobem umieszczenia nowego wiersza w ciągu przedmowy jest ...

echo "task goes here
$(cat todo.txt)" > todo.txt

... po prostu używać nowych linii. Jasne, nie jest to już jedna linijka, ale realistycznie też nie była wcześniej liniowa. Jeśli robisz to w skrypcie i martwisz się wcięciem (np. Wykonujesz to wewnątrz funkcji), istnieje kilka sposobów obejścia tego problemu, w tym:

(echo 'task goes here' && cat todo.txt) > todo.txt
echo 'task goes here'$'\n'"$(cat todo.txt)" > todo.txt

Ponadto, jeśli zależy Ci na dodaniu nowej linii na końcu todo.txt, nie używaj ich. Cóż, z wyjątkiem przedostatniego. To nie zadziera z końcem.

John Alberts
źródło
1
W ogóle nie wykonuję $ (...).
SCL
2
To może działać lepiej (lub w ogóle) z podwójnymi cudzysłowami zamiast pojedynczych…
inkaphink
5
Czy -ekonwertowane sekwencje specjalne również nie będą konwertowane w todo.txt?
mk12
2
Obejścia wydajności -> cat: <docelowa nazwa pliku>: plik wejściowy jest plikiem wyjściowym ...
ingyhere
printf byłby o wiele bardziej spójny na różnych platformach i ogólnie powinien działać płynniej niż echo -e
Peter Berg
28

moreutilsMieć ładny narzędzie o nazwie sponge:

echo "task goes here" | cat - todo.txt | sponge todo.txt

„Absorbuje” STDIN, a następnie zapisuje do pliku, co oznacza, że ​​nie musisz się martwić o pliki tymczasowe i ich przenoszenie.

Możesz uzyskać moreutilsz wieloma dystrybucjami Linuksa, przez apt-get install moreutilslub w systemie OS X za pomocą Homebrew , z brew install moreutils.

slhck
źródło
Chciałbym pójść do (echo 'ble'; cat todo.txt):-)
Ciro Santilli新疆改造中心法轮功六四事件
11

Możesz używać Vima w trybie Ex:

ex -s -c '1i|task goes here' -c x todo.txt
  1. 1 wybierz pierwszą linię

  2. i wstawić

  3. x Zapisz i zamknij

Steven Penny
źródło
5

Możesz utworzyć nowy plik tymczasowy.

echo "new task" > new_todo.txt
cat todo.txt >> new_todo.txt
rm todo.txt
mv new_todo.txt todo.txt

Możesz także użyć sedlub awk. Ale w zasadzie dzieje się to samo.

Keith
źródło
1
Powiedz, że brakuje Ci miejsca na dysku, więc new_todo.txtzapisuje się tylko częściowo. Wygląda na to, że Twoje rozwiązanie traci oryginalny plik.
NPE
Kto zabraknie miejsca na dysku? ;-) To tylko prosty przykład.
Keith
2
@Keith Ktoś pracujący na maszynie wirtualnej, który nie spodziewał się, że będzie potrzebował szczególnie dużego dysku wirtualnego. Lub ktoś przenosi duży plik. W każdym razie prawdziwym argumentem przeciwko temu są uprawnienia do katalogu; jeśli nie masz uprawnień do tworzenia nowych plików w danym katalogu, jedyną komendą, którą z powodzeniem wykonasz w skrypcie, jest rmoryginalny plik.
Parthian Shot
3

Jeśli plik tekstowy jest wystarczająco mały, aby zmieścić się w pamięci, nie trzeba tworzyć pliku tymczasowego, aby go zastąpić. Możesz załadować to wszystko do pamięci i zapisać z powrotem do pliku.

echo "$(echo 'task goes here' | cat - todo.txt)" > todo.txt

Nie można dodawać wierszy na początku pliku bez zapisywania całego pliku.

Rucent88
źródło
Wystarczy zadać oczywiste pytanie: gdzie jest limit znaków zmiennych powłoki?
nixda
O ile mi wiadomo, jest to ograniczone tylko ilością dostępnej pamięci. Wypełniłem do pamięci zmienne znacznie przekraczające 100 MB. text=$(cat file). Uważaj jednak, aby używać tylko tekstu, ponieważ zmienne powłoki nie są binarnie czyste mywiki.wooledge.org/BashFAQ/058
Rucent88
2

Nie można wstawiać treści na początku pliku. Jedyne, co możesz zrobić, to albo zastąpić istniejącą zawartość, albo dołączyć bajty po bieżącym końcu pliku.

Każde rozwiązanie twojego pytania wymaga utworzenia pliku tymczasowego (lub bufora) (w pamięci lub na dysku), który ostatecznie zastąpi oryginalny plik.

Uważaj, aby nie utracić danych, zachowując oryginalny plik podczas tworzenia nowego, jeśli w tym czasie system plików będzie pełny. na przykład:

cat <(echo task go there) todo.txt > todo.txt.new && mv todo.txt.new todo.txt
jlliagre
źródło
Downvoters mogą wyjaśnić swoją motywację. Żadna z pozostałych odpowiedzi, w tym odpowiedź zaakceptowana, nie stoi w sprzeczności z moją odpowiedzią.
jlliagre 12.04.16
Trudno to parsować, ponieważ <...> wyglądają jak nawiasy, które, jak zakładam, nie są.
Odstęp
To mi nie działa. echo HOME=\"/g/Users/timregan/\" | cat - 'F:\Program Files\Git\etc\profile'działa, ale cat <echo HOME=\"/g/Users/timregan/\" 'F:\Program Files\Git\etc\profile'wyświetla błąd „echo: brak takiego pliku lub katalogu”
dumbledad
@dumbledad Zastanawiasz się nad moją odpowiedzią. Nie ma w tym nic do przeanalizowania. Odstęp między <i (złamałby składnię. Spróbujcat <(echo HOME=\"/g/Users/timregan/\") 'F:\Program Files\Git\etc\profile'
jlliagre
1

Możesz użyć tee:

echo 'task goes here' | cat - todo.txt | tee todo.txt
Radu Rădeanu
źródło
3
Nie, nie możesz askubuntu.com/a/752451
Steven Penny
0

GitBash + Windows10 + Multline :

Oto wersja, która pozwala używać ciągów wieloliniowych .

##############################################
## This section for demo purpose only,      ##
## So you can save entire file as           ##
## whatever.sh and run it.                  ##
##                                          ##
##############################################
> MY_TARGET_FILE.txt ##Make Or Clear File
echo "[STARTER_CONTENT]" >> MY_TARGET_FILE.txt
##############################################

## Below is main code:

##################################################
TARGET_FILE_VARIABLE="MY_TARGET_FILE.txt"
ADD_TO_HEAD_VARIABLE=$(cat << "HEREDOC_HEAD_TEXT"
//|  +-------------------------------------+   |//
//|  |                                     |   |//
//|  |     MESSAGE_FOR_HEAD_OF_FILE        |   |//
//|  |                                     |   |//
//|  +-------------------------------------+   |//
HEREDOC_HEAD_TEXT
)
##################################################
TAR=$TARGET_FILE_VARIABLE                       ##
TEX=$ADD_TO_HEAD_VARIABLE                       ##
echo "$TEX" | cat - $TAR > TEMP && mv TEMP $TAR ##
##################################################

## Expected contents of MY_TARGET_FILE.txt :
## //|  +-------------------------------------+   |//
## //|  |                                     |   |//
## //|  |     MESSAGE_FOR_HEAD_OF_FILE        |   |//
## //|  |                                     |   |//
## //|  +-------------------------------------+   |//
## [STARTER_CONTENT]
J MADISON
źródło