Polecenie systemu Unix do dołączania tekstu do pliku na początku

126

Czy istnieje polecenie systemu Unix, które dołącza jakieś dane tekstowe do pliku tekstowego?

Coś jak:

prepend "to be prepended" text.txt
Raz Dwa Trzy
źródło
szukasz pojedynczego polecenia, czy może mały skrypt / alias jest w porządku?
Levon
2
Jeśli połączysz wszystkie odpowiedzi, jest to prawdopodobnie najbardziej zwarty sposób:<<(echo "to be prepended") < text.txt | sponge text.txt
Sridhar Sarnobat
To pytanie naprawdę pobudziło kreatywność wielu ludzi!
Richard Jessop

Odpowiedzi:

111
sed -i.old '1s;^;to be prepended;' inFile
  • -izapisuje zmianę w miejscu i tworzy kopię zapasową, jeśli podano jakiekolwiek rozszerzenie. (W tym przypadku .old)
  • 1s;^;to be prepended;zastępuje początek pierwszego wiersza podanym ciągiem zastępującym, używając ;jako separatora polecenia.
Książę John Wesley
źródło
19
Gdybyś mógł dodać jakieś wyjaśnienie do polecenia, byłoby to pomocne dla innych niezbyt zaznajomionych z działaniem seda. OP może chcieć wstawić \npo poprzedzeniu; w zależności od ich potrzeb. Zgrabne rozwiązanie.
Levon
Tak, wyjaśnienie byłoby świetne. Czy może inFilezawierać katalogi w swojej ścieżce?
zakdances
3
@Levon Całkowicie chciałem wstawić plik \n. Powinienem był najpierw przeczytać komentarze. Cholera.
chishaku
W moim przypadku chcę mieć średnik na końcu dodanej linii, więc poszedłem z'1s;^;my-prepended-line\;\n;'
SamGamgee
5
Zauważ, że '\ n' działa tylko dla GNU sed, a nie BSD (jak OSX, FreeBSD, itp.). Aby być bardziej przenośnym z tymi, zobacz: stackoverflow.com/questions/1421478/…
jwd
164
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt
shime
źródło
2
W jednym wierszu iw oryginalnym pliku tekstowym! To jest poprawna prosta odpowiedź. Sugeruję dodanie nowej linii \ n echo -e "na początku \ n $ (cat text.txt)"> text.txt
VincentPerrin.com
32
To rozwiązanie ma problemy ... konwertuje wystąpienia "\ n" na rzeczywiste nowe wiersze ... problematyczne, jeśli przygotowujesz plik kodu zawierający ciągi zawierające "\ n".
Paul Go
1
Miałem to w swoim pliku git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';i to się psuje, ponieważ konwertuje \ti \n.
hIpPy
8
To (1) ładuje cały plik do pamięci i (2) umieszcza cały plik w jednym argumencie wiersza poleceń. Dobra do prostych rzeczy, ale uważaj na ograniczenia.
jwd
2
Ostatnio próbowałem, uważam, że to by się zepsuło na naprawdę dużych plikach, gdzie cat nie przeczytał całej zawartości text.txt przed jego nadpisaniem. Jeśli opcja -e na echo jest problemem, możesz zacytować nową linię w bash lub zrobićecho "to be prepended"$'\n'"$(cat text.txt)"
jbo5112
44

Zastępowanie procesu

Dziwię się, że nikt o tym nie wspomniał.

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

co jest prawdopodobnie bardziej naturalne niż przyjęta odpowiedź (wydrukowanie czegoś i przekierowanie do polecenia podstawienia jest leksykograficznie sprzeczne z intuicją).

... i przejmując to, co powiedział Ryan powyżej, spongenie potrzebujesz tymczasowego pliku:

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

EDYCJA: Wygląda na to, że to nie działa w Bourne Shell /bin/sh


Here String (tylko zsh)

Używając tu-string - <<<, możesz:

<<< "to be prepended" < text.txt | sponge text.txt
Sridhar Sarnobat
źródło
3
Ta odpowiedź jest dla mnie zwycięzcą. Prosty i osiągalny tylko z wbudowanymi. Dobra robota!
PiersyP
1
@PiersyP Zgadzam się! To bardzo pomogło, dziękuję Sridhar-Sarnobat.
Problem w tym, że jest 1 plik, a nie 2 pliki. Więc pierwsza linia odpowiedzi tak naprawdę nie działa. Ostatnia linia może działać (ale wymaga gąbki).
VasiliNovikov
1
Co robi gąbka?
Hemanta,
1
Twoje <<(echo "to be prepended") < text.txti <<< "to be prepended" < text.txtkonstrukcje nie działają w bash; wymagają zsh.
Anders Kaseorg,
32

To jest jedna możliwość:

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

prawdopodobnie nie będziesz łatwo obejść pliku pośredniego.

Alternatywy (mogą być uciążliwe przy ucieczce z powłoki):

sed -i '0,/^/s//to be prepended/' text.txt
0xC0000022L
źródło
Wydaje mi się, że strategia 1) jest najbardziej intuicyjną ze wszystkich odpowiedzi. I niewielkie zmiany: cat <(echo "to be prepended") text.txt > newfile.txt . Pomyśl o tym, nie jestem pewien, czy mój jest powiązany, więc zamieszczam osobną odpowiedź.
Sridhar Sarnobat
1
Pierwsza metoda nie zachowa uprawnień do plików
Xlsx
Jedynym sposobem na zachowanie uprawnień jest użycie gąbki
Sridhar Sarnobat
20

To zadziała, aby utworzyć wynik. - oznacza standardowe wejście, które jest dostarczane przez potok z echa.

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

Aby przepisać plik, wymagany jest plik tymczasowy, ponieważ nie można przesłać z powrotem do pliku wejściowego.

echo "to be prepended" | cat - text.txt > text.txt.tmp
mv text.txt.tmp text.txt
Adam
źródło
2
nie dołącza to jednak tekstu do pliku text.txtzgodnie z żądaniem, ale wyświetla go na standardowe wyjście. Dane wyjściowe mogą zostać przeniesione do innego pliku, jeśli to zadziała dla OP
Levon
1
to na pewno wypisałoby „do dołączenia na początku”, a następnie zawartość „text.txt”. Ale chciałbym, żeby tekst był dołączony do pliku na początku
jeden dwa trzy
1
a co jeśli mam tekst wielowierszowy? Jak umieścić tam znak nowej linii?
One Two Three
Byłoby fajnie, ale bash tego nie lubi: $ echo RewriteBase / | cat - web / .htaccess> web / .htaccess cat: web / .htaccess: plik wejściowy to plik wyjściowy
geek-merlin
14

Wolę odpowiedź Adama

Możemy ułatwić korzystanie z gąbki . Teraz nie musimy tworzyć pliku tymczasowego i zmieniać jego nazwy o

echo -e "to be prepended \n another line" | cat - text.txt | sponge text.txt
ryan
źródło
to było jedyne rozwiązanie działające dla mnie. to zabawne, że nazwa mojego serwera to spongebob.
Ömer An
11

Jeśli zastąpienie pliku wejściowego jest dopuszczalne :

Uwaga:

  • Może to mieć nieoczekiwane skutki uboczne, w szczególności potencjalnie zastąpienie linku symbolicznego zwykłym plikiem, w wyniku czego otrzymamy inne uprawnienia do pliku i zmianę daty utworzenia (urodzenia) pliku.

  • sed -i, jak w odpowiedzi księcia Johna Wesleya , próbuje przynajmniej przywrócić pierwotne uprawnienia, ale inne ograniczenia mają również zastosowanie.

Oto prosta alternatywa, która używa pliku tymczasowego (pozwala uniknąć wczytywania całego pliku wejściowego do pamięci, tak jak robi to rozwiązanie shime ):

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

Użycie polecenia grupy ( { ...; ...; }) jest nieco bardziej wydajne niż użycie podpowłoki ( (...; ...)), jak w rozwiązaniu 0xC0000022L .

Zalety to:

  • Łatwo jest kontrolować, czy nowy tekst ma być dodawany bezpośrednio do pierwszego wiersza, czy też powinien być wstawiany jako nowy wiersz (po prostu dołącz \ndo printfargumentu).

  • W przeciwieństwie do sedrozwiązania działa, jeśli plik wejściowy jest pusty ( 0bajty).


sedRozwiązanie może być uproszczona, jeżeli zamiarem jest dołączana jeden lub więcej całych linii z istniejącym zawartości (przy założeniu, że plik źródłowy jest niepusty)

sed„S iwkładki funkcyjne całych liniach:

Z 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

Wersja przenośna, która działa również z 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

Zwróć uwagę, że \wymagany jest dosłowny znak nowej linii po znaku .


Jeśli plik wejściowy musi być edytowany na miejscu (zachowując jego i-węzeł ze wszystkimi atrybutami):

Używając czcigodnego ednarzędzia POSIX :

Uwaga:

  • ednieodmiennie najpierw wczytuje plik wejściowy jako całość do pamięci.

Aby dołączyć bezpośrednio do pierwszej linii (tak jak w przypadku sed, nie zadziała, jeśli plik wejściowy jest całkowicie pusty ( 0bajty)):

ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
  • -swyłączone edkomunikaty o stanie.
  • Zwróć uwagę, jak polecenia są dostarczane edw postaci wielowierszowego dokumentu here ( <<EOF\n...\nEOF), tj. Przez stdin ; domyślnie interpretacja łańcuchów jest wykonywana w takich dokumentach (zmienne powłoki są interpolowane); zacytować ogranicznik otwierający, aby to pominąć (np <<'EOF'.).
  • 1 ustawia pierwszą linię jako bieżącą
  • funkcja swykonuje oparte na wyrażeniach regularne podstawianie napisów w bieżącej linii, jak w sed; możesz dołączyć dosłowne znaki nowej linii w zastępowanym tekście, ale muszą być one \poprzedzone znakiem -escap.
  • wpisze plecy wynik do pliku wejściowego (do testowania, należy wymienić wz ,ptylko wydrukować wynik, bez modyfikowania pliku wejściowego).

Aby dodać jeden lub więcej całych wierszy :

Podobnie jak w przypadku sed, ifunkcja niezmiennie dodaje końcowy znak nowej linii do wstawianego tekstu.

ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
  • 0 iustawia 0(początek pliku) w bieżącej linii i uruchamia tryb wstawiania ( i); zwróć uwagę, że numery linii są 1oparte na innych zasadach.
  • Kolejne wiersze to tekst wstawiany przed bieżącą linią, zakończony .w osobnym wierszu.
mklement0
źródło
7

Prawdopodobnie nic wbudowanego, ale możesz łatwo napisać własne, na przykład:

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

Coś takiego przynajmniej ...

Rob I
źródło
6
+1 za użycie $$ (aktualny PID) jako części tymczasowej nazwy pliku. Jeśli utworzysz skrypt do użytku ogólnego, zapobiegnie to potencjalnemu nadpisaniu pliku tymczasowego przez innego użytkownika przez jednoczesne uruchomienie tego samego skryptu.
E Brown
3
$$Jednak użycie jest niewystarczające dla kodu produkcyjnego (atak na link symboliczny google); ale z pewnością jest lepsze niż statyczna nazwa pliku.
tripleee
1
po prostu użyjmktemp
mewa
7

W pewnych okolicznościach tekst poprzedzony może być dostępny tylko ze standardowego wejścia. Wtedy ta kombinacja zadziała.

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

Jeśli chcesz pominąć teedane wyjściowe, dołącz > /dev/null.

sehari24jam
źródło
Bardzo podoba mi się ta odpowiedź. To bardzo czysty i przejrzysty sposób, aby to zrobić. Użycie tee jest naturalnym rozwiązaniem problemu próby wyświetlenia wyjścia cat do pliku wejściowego. Żadnych hacków ze zmianą nazwy. Lepsze niż obecnie najwyżej ocenione rozwiązanie, ponieważ nie tworzy nowego pliku. Jest to w zasadzie najbliższe >> dodawanie zamiast dołączania.
DC2,
To zdecydowanie najlepsza i najczystsza odpowiedź.
nerdoc
6
Czy istnieje gwarancja, teektóra nie odbije się, text.txtzanim catją przeczytasz? Myślę, że nie - co uczyniłoby to rozwiązanie niebezpiecznym dla zdrowia pliku.
Jonathan Leffler
3
@JonathanLeffler prawdopodobnie nie. Próbowałem ten kod do testu: 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. Jeśli lenAma wartość 1000000 (plik 1 lenBMB ) i 10000 (10 KB z dołączonym tekstem), to plik „a.txt” jest zastępowany 20 KB liter „b”. To jest całkowicie zepsute. Teraz, jeśli używasz 1Mb a.txt i 1Mb tekstu do dodania na początku, teeprzechodzi do pętli generującej plik 7Gb +, musiałem zatrzymać polecenie. Jest więc oczywiste, że wynik jest nieprzewidywalny w przypadku dużych rozmiarów. Nie mam informacji, czy powinien działać na małych rozmiarach.
VasiliNovikov
3
Mówiąc w kategoriach koncepcyjnych: To polecenie spowoduje utratę danych, jeśli plik wejściowy będzie większy niż rozmiar buforu potoku systemu , który obecnie wynosi zwykle 64 KB.
mklement0
7

Rozwiązanie:

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

Zauważ, że jest to bezpieczne dla wszystkich rodzajów wejść, ponieważ nie ma rozszerzeń. Na przykład, jeśli chcesz wstawić na początku !@#$%^&*()ugly text\n\t\n, zadziała:

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

Ostatnią częścią do rozważenia jest usunięcie białych znaków na końcu pliku podczas podstawiania poleceń "$(cat file.txt)". Wszystkie obejścia tego problemu są stosunkowo złożone. Jeśli chcesz zachować znaki nowej linii na końcu pliku.txt, zobacz: https://stackoverflow.com/a/22607352/1091436

VasiliNovikov
źródło
Kocham to. Super przenośny i nie ma potrzeby generowania nowego pliku. Dzięki!
pyb
1
Jedyny problem z tym pojawia się, jeśli zawartość file.txtjest większa niż można zmieścić na liście argumentów do printf.
Jonathan Leffler
6

Inny sposób użycia sed:

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

Jeśli wiersz, który ma zostać dodany na początku, jest wielowierszowy:

sed -i.old '1 {i\ 
to be prepended\
multiline
}' inFile
Mert Nuhoglu
źródło
Dlaczego nie sed -i '1ito be prepended' inFile- czy jest to dozwolone tylko dla GNU sed?
użytkownik nieznany
@userunknown: Standardowo sedpo ipoleceniu następuje ukośnik odwrotny i znak nowej linii, a każdy wiersz wejścia oprócz ostatniego również kończy się ukośnikiem odwrotnym. GNU seddopuszcza skróty zgodne z pytaniami, o które pytasz, ale tylko GNU sedto robi. '
Jonathan Leffler
3

Zgodnie z testami w Bash (w Ubuntu), jeśli zaczynasz od pliku testowego przez;

echo "Original Line" > test_file.txt

możesz wykonać;

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

lub, jeśli wersja bash jest za stara dla $ (), możesz użyć lewych apostrofów;

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

i otrzymaj następującą zawartość pliku „test_file.txt”;

New Line
Original Line

Brak pliku pośredniego, tylko bash / echo.

AlexanderESmith
źródło
2
najlepsza odpowiedź, napisałem tę jedną linijkę, aby obrócić mój plik za pomocą tej odpowiedzi: for i in $ (<file2.txt); do echo "$ (echo $ i; cat file.txt)"> plik.txt; Gotowe;
Aurangzeb
2

Innym dość prostym rozwiązaniem jest:

    $ echo -e "string\n" $(cat file)
Poklepać
źródło
To zadziałało dla mnie ... tylko niewielka modyfikacja, aby zachować multilinie z pliku wejściowego, musisz zawinąć go w cudzysłowy, więc coś takiego: echo -e "string \ n" "$ (cat ~ / file.txt) "> ~ / plik.txt;
Craig Wayne
2
Brak catrozwinięcia parametrów w podwójnych cudzysłowach jest całkiem dobrym powodem do odrzucenia . Ten kod dzieli zawartość pliku na słowa i przekazuje każde z tych słów jako oddzielny argument do echo. Tracisz oryginalne granice argumentów, tracisz znaki nowej linii / tabulatory / itd., A ciągi takie jak \tiw \noryginalnym tekście są zastępowane tabulatorami i znakami nowej linii zamiast pozostawać takie, jakie były.
Charles Duffy,
1

Jeśli lubisz vi / vim, to może być bardziej w Twoim stylu.

printf '0i\n%s\n.\nwq\n' prepend-text | ed file
Ian Kelling
źródło
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
Jim
źródło
0

Poleciłbym zdefiniować funkcję, a następnie zaimportować ją i użyć w razie potrzeby.

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
}

Następnie użyj tego w ten sposób:

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

Zawartość twojego pliku będzie wtedy:

This is second
This is first

Mam zamiar użyć tego podejścia do wdrożenia aktualizacji dziennika zmian.

AwesomeBobX64
źródło