Czy istnieje polecenie systemu Unix, które dołącza jakieś dane tekstowe do pliku tekstowego?
Coś jak:
prepend "to be prepended" text.txt
<<(echo "to be prepended") < text.txt | sponge text.txt
Czy istnieje polecenie systemu Unix, które dołącza jakieś dane tekstowe do pliku tekstowego?
Coś jak:
prepend "to be prepended" text.txt
<<(echo "to be prepended") < text.txt | sponge text.txt
Odpowiedzi:
sed -i.old '1s;^;to be prepended;' inFile
-i
zapisuje 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.\n
po poprzedzeniu; w zależności od ich potrzeb. Zgrabne rozwiązanie.
inFile
zawierać katalogi w swojej ścieżce?
\n
. Powinienem był najpierw przeczytać komentarze. Cholera.
'1s;^;my-prepended-line\;\n;'
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt
git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';
i to się psuje, ponieważ konwertuje \t
i \n
.
echo "to be prepended"$'\n'"$(cat text.txt)"
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, sponge
nie 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
Używając tu-string - <<<
, możesz:
<<< "to be prepended" < text.txt | sponge text.txt
<<(echo "to be prepended") < text.txt
i <<< "to be prepended" < text.txt
konstrukcje nie działają w bash; wymagają zsh.
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
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ź.
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
text.txt
zgodnie 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
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
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 \n
do printf
argumentu).
W przeciwieństwie do sed
rozwiązania działa, jeśli plik wejściowy jest pusty ( 0
bajty).
sed
Rozwią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 i
wkł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 .
Używając czcigodnego ed
narzędzia POSIX :
Uwaga:
ed
nieodmiennie 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 ( 0
bajty)):
ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
-s
wyłączone ed
komunikaty o stanie.ed
w 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ąs
wykonuje 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.w
pisze plecy wynik do pliku wejściowego (do testowania, należy wymienić w
z ,p
tylko wydrukować wynik, bez modyfikowania pliku wejściowego).Aby dodać jeden lub więcej całych wierszy :
Podobnie jak w przypadku sed
, i
funkcja niezmiennie dodaje końcowy znak nowej linii do wstawianego tekstu.
ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
0 i
ustawia 0
(początek pliku) w bieżącej linii i uruchamia tryb wstawiania ( i
); zwróć uwagę, że numery linii są 1
oparte na innych zasadach..
w osobnym wierszu.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 ...
$$
Jednak użycie jest niewystarczające dla kodu produkcyjnego (atak na link symboliczny google); ale z pewnością jest lepsze niż statyczna nazwa pliku.
mktemp
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ąć tee
dane wyjściowe, dołącz > /dev/null
.
tee
która nie odbije się, text.txt
zanim cat
ją przeczytasz? Myślę, że nie - co uczyniłoby to rozwiązanie niebezpiecznym dla zdrowia pliku.
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 lenA
ma wartość 1000000 (plik 1 lenB
MB ) 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, tee
przechodzi 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.
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
file.txt
jest większa niż można zmieścić na liście argumentów do printf
.
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
sed -i '1ito be prepended' inFile
- czy jest to dozwolone tylko dla GNU sed?
sed
po i
poleceniu 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 sed
dopuszcza skróty zgodne z pytaniami, o które pytasz, ale tylko GNU sed
to robi. '
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.
Innym dość prostym rozwiązaniem jest:
$ echo -e "string\n" $(cat file)
cat
rozwinię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 \t
iw \n
oryginalnym tekście są zastępowane tabulatorami i znakami nowej linii zamiast pozostawać takie, jakie były.
Jeśli lubisz vi / vim, to może być bardziej w Twoim stylu.
printf '0i\n%s\n.\nwq\n' prepend-text | ed file
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.