To prawdopodobnie skomplikowane rozwiązanie .
Szukam prostego operatora, takiego jak „>>”, ale do poprzedzania.
Obawiam się, że nie istnieje. Muszę zrobić coś takiego
mv myfile tmp cat myheader tmp> myfile
Coś mądrzejszego?
To prawdopodobnie skomplikowane rozwiązanie .
Szukam prostego operatora, takiego jak „>>”, ale do poprzedzania.
Obawiam się, że nie istnieje. Muszę zrobić coś takiego
mv myfile tmp cat myheader tmp> myfile
Coś mądrzejszego?
Odpowiedzi:
Poniższy hack był szybką, spontaniczną odpowiedzią, która zadziałała i otrzymała wiele pozytywnych głosów. Potem, gdy pytanie stawało się coraz bardziej popularne i mijało więcej czasu, oburzeni ludzie zaczęli zgłaszać, że to trochę działa, ale mogą się zdarzyć dziwne rzeczy, albo po prostu nie działa, więc przez jakiś czas było wściekle odrzucane. Taka zabawa.
Rozwiązanie wykorzystuje dokładną implementację deskryptorów plików w twoim systemie, a ponieważ implementacja różni się znacznie między nixami, jego sukces jest całkowicie zależny od systemu, definitywnie nieprzenośny i nie należy na nim polegać w przypadku niczego nawet mało ważnego.
Teraz, gdy to wszystko zniknęło, odpowiedź brzmiała:
Stworzenie innego deskryptora pliku dla pliku file ( exec 3<> yourfile
), a następnie zapisywanie do this ( >&3
) wydaje się przezwyciężyć dylemat odczytu / zapisu tego samego pliku. U mnie działa na plikach 600K z awk. Jednak próba tej samej sztuczki przy użyciu „kota” kończy się niepowodzeniem.
Przekazanie przedrostka jako zmiennej do awk ( -v TEXT="$text"
) rozwiązuje problem dosłownych cudzysłowów, który uniemożliwia wykonanie tej sztuczki z 'sed'.
#!/bin/bash
text="Hello world
What's up?"
exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3
To nadal używa pliku tymczasowego, ale przynajmniej jest w jednym wierszu:
echo "text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile
-
później cat
?
echo -n "text"
yourfile
jest dowiązaniem symbolicznym, to nie zrobi tego, co chcesz.
echo '0a
your text here
.
w' | ed some_file
ed jest edytorem standardów! 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: Twoja metoda nie ma gwarancji, że zadziała i prawdopodobnie zawiedzie, jeśli dodasz więcej niż 4096 bajtów rzeczy (przynajmniej tak się dzieje z gnu awk, ale przypuszczam, że inne implementacje będą miały podobne ograniczenia). W takim przypadku nie tylko zakończy się niepowodzeniem, ale wejdzie w nieskończoną pętlę, w której odczyta własne wyjście, dzięki czemu plik będzie rósł, dopóki cała dostępna przestrzeń nie zostanie wypełniona.
Wypróbuj sam:
exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3
(ostrzeżenie: zabij go po chwili lub zapełni system plików)
Co więcej, edytowanie plików w ten sposób jest bardzo niebezpieczne i jest to bardzo zła rada, ponieważ jeśli coś się stanie podczas edycji pliku (awaria, pełny dysk), prawie na pewno pozostaniesz z plikiem w niespójnym stanie.
Niemożliwe bez pliku tymczasowego, ale oto oneliner
{ echo foo; cat oldfile; } > newfile && mv newfile oldfile
Możesz użyć innych narzędzi, takich jak ed lub perl, aby zrobić to bez plików tymczasowych.
Warto zauważyć, że często dobrym pomysłem jest bezpieczne wygenerowanie pliku tymczasowego za pomocą narzędzia takiego jak mktemp , przynajmniej jeśli skrypt zostanie kiedykolwiek wykonany z uprawnieniami roota. Możesz na przykład wykonać następujące czynności (ponownie w bash):
(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )
Jeśli potrzebujesz tego na kontrolowanych komputerach, zainstaluj pakiet "moreutils" i użyj "sponge". Następnie możesz:
cat header myfile | sponge myfile
{ echo "prepended text"; cat myfile } | sponge myfile
Używając bash heredoc można uniknąć konieczności posiadania pliku tmp:
cat <<-EOF > myfile
$(echo this is prepended)
$(cat myfile)
EOF
To działa, ponieważ $ (cat myfile) jest sprawdzane podczas sprawdzania skryptu bash, przed wykonaniem cat z przekierowaniem.
zakładając, że plik, który chcesz edytować, to my.txt
$cat my.txt
this is the regular file
Plik, który chcesz dodać na początku, to nagłówek
$ cat header
this is the header
Upewnij się, że w pliku nagłówkowym znajduje się ostatnia pusta linia.
Teraz możesz go poprzedzić
$cat header <(cat my.txt) > my.txt
Skończysz z
$ cat my.txt
this is the header
this is the regular file
O ile wiem, działa to tylko w „bash”.
this is the header
w moim.txt. Nawet po zaktualizowaniu Bash 4.3.42(1)-release
, otrzymuję ten sam wynik.
<(...)
) , więc nie ma gwarancji, że my.txt
zostanie odczytany w całości z góry , bez czego ta technika nie zadziała.
Kiedy zaczynasz robić rzeczy, które stają się trudne w skrypcie powłoki, stanowczo sugerowałbym sprawdzenie przepisania skryptu w „odpowiednim” języku skryptowym (Python / Perl / Ruby / itp.)
Jeśli chodzi o dołączanie wiersza z wyprzedzeniem do pliku, nie można tego zrobić za pomocą pipingu, ponieważ gdy robisz coś podobnego cat blah.txt | grep something > blah.txt
, nieumyślnie pusta plik. Istnieje małe polecenie narzędzia o nazwie, sponge
które możesz zainstalować (robisz to cat blah.txt | grep something | sponge blah.txt
i buforuje zawartość pliku, a następnie zapisuje ją do pliku). Jest podobny do pliku tymczasowego, ale nie musisz tego robić jawnie. ale powiedziałbym, że to „gorszy” wymóg niż, powiedzmy, Perl.
Może istnieć sposób na zrobienie tego przez awk lub podobny, ale jeśli musisz użyć skryptu powłoki, myślę, że plik tymczasowy jest zdecydowanie najłatwiejszym (/ jedynym?) Sposobem.
Jak sugeruje Daniel Velkov, użyj koszulki.
Dla mnie to proste, inteligentne rozwiązanie:
{ echo foo; cat bar; } | tee bar > /dev/null
EDYCJA: To jest zepsute. Zobacz Dziwne zachowanie podczas poprzedzania pliku z kotem i tee
Obejście problemu z nadpisaniem polega na użyciu tee
:
cat header main | tee main > /dev/null
Głównie dla zabawy / shell golfa, ale
ex -c '0r myheader|x' myfile
załatwi sprawę i nie ma żadnych potoków ani przekierowań. Oczywiście, vi / ex nie jest tak naprawdę do użytku nieinteraktywnego, więc vi na chwilę się wyświetli.
Dlaczego po prostu nie użyć polecenia ed (jak już sugeruje tu fluffle)?
ed czyta cały plik do pamięci i automatycznie przeprowadza edycję pliku w miejscu!
Więc jeśli twój plik nie jest tak duży ...
# 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
Jeszcze innym obejściem byłoby użycie otwartych uchwytów plików, zgodnie z sugestią Jürgena Hötzela w Przekieruj dane wyjściowe z seda 's / c / d /' myFile do 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
Wszystko to można oczywiście umieścić w jednej linii.
Wariant rozwiązania cb0 z „brakiem pliku tymczasowego” w celu dodania stałego tekstu na początku:
echo "text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified )
Znowu polega to na wykonaniu podpowłoki - (..) - aby uniknąć odmowy cat odmowy posiadania tego samego pliku na wejściu i wyjściu.
Uwaga: podobało mi się to rozwiązanie. Jednak na moim komputerze Mac oryginalny plik został utracony (sądziłem, że nie powinien, ale tak się dzieje). Można to naprawić, pisząc rozwiązanie jako: echo "tekst na początek" | cat - file_to_be_modified | cat> tmp_file; mv tmp_file file_to_be_modified
Oto, co odkryłem:
echo -e "header \n$(cat file)" >file
OSTRZEŻENIE: wymaga to nieco więcej pracy, aby spełnić potrzeby PO.
Powinien istnieć sposób, aby podejście seda @shixilun działało pomimo jego obaw. Musi istnieć polecenie bash, aby uniknąć białych spacji podczas wczytywania pliku do ciągu zastępującego sed (np. Zamień znaki nowej linii na '\ n'. Polecenia powłoki vis
i cat
mogą obsługiwać znaki niedrukowalne, ale nie białe spacje, więc to nie rozwiąże operacji problem:
sed -i -e "1s/^/$(cat file_with_header.txt)/" file_to_be_prepended.txt
kończy się niepowodzeniem ze względu na nieprzetworzone znaki nowej linii w skrypcie zastępczym, które należy poprzedzić znakiem kontynuacji linii () i być może poprzedzonym znakiem &, aby zachować powłokę i seda w dobrym stanie, jak ta odpowiedź SO
sed
ma ograniczenie rozmiaru do 40 KB dla nieglobalnych poleceń wyszukiwania i zamiany (bez końcowego / g po wzorcu), więc prawdopodobnie pozwoliłoby uniknąć przerażających problemów z przepełnieniem bufora w awk, przed którymi ostrzegał anonimowy.
sed -i -e "1s/^/new first line\n/" old_file.txt
Jeśli piszesz w BASH, możesz po prostu wydać:
cat - twój plik / tmp / out && mv / tmp / out twój plik
To jest właściwie w złożonym przykładzie, który sam zadałeś w swoim własnym pytaniu.
cat - yourfile <<<"text" > /tmp/out && mv /tmp/out yourfile
, ale to wystarczająco różni się od mojej odpowiedzi, że powinna to być jego własna odpowiedź.
Rozwiązanie z 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}"
Możesz też:
printf "${new_line}\n$(cat ${target_file})" > "${target_file}"
Ale w takim przypadku musisz być pewien, że %
nigdzie nie ma , w tym zawartości pliku docelowego, ponieważ można to zinterpretować i zepsuć wyniki.
echo
wydaje się bezpieczniejszą opcją. echo "my new line\n$(cat my/file.txt)" > my/file.txt
${new_line}
pliku docelowym, a nie o pliku docelowym
Możesz użyć wiersza poleceń perla:
perl -i -0777 -pe 's/^/my_header/' tmp
Gdzie -i utworzy bezpośrednią zamianę pliku, a -0777 siorbi cały plik i dopasuje ^ tylko początek. -pe wypisze wszystkie linie
Lub jeśli my_header jest plikiem:
perl -i -0777 -pe 's/^/`cat my_header`/e' tmp
Gdzie / e pozwoli na ocenę kodu w zamianie.
current=`cat my_file` && echo 'my_string' > my_file && echo $current >> my_file
gdzie „my_file” to plik, do którego należy dołączyć „my_string”.
Najbardziej podoba mi się podejście ed @ fluffle . W końcu przełączniki wiersza poleceń dowolnego narzędzia a polecenia edytora skryptowego są tutaj zasadniczo takie same; nie widząc „czystości” rozwiązania edytora skryptów jako mniejszego lub czegoś innego.
Oto moja jedna linijka dołączona do .git/hooks/prepare-commit-msg
pliku w repozytorium w .gitmessage
celu zatwierdzenia wiadomości:
echo -e "1r $PWD/.gitmessage\n.\nw" | ed -s "$1"
Przykład .gitmessage
:
# Commit message formatting samples:
# runlevels: boot +consolekit -zfs-fuse
#
Robię 1r
zamiast tego 0r
, ponieważ pozostawi to pustą linię gotową do zapisu na górze pliku z oryginalnego szablonu. Nie umieszczaj pustej linii na swoim .gitmessage
, skończysz z dwoma pustymi liniami. -s
blokuje wyświetlanie informacji diagnostycznych przez ed.
W związku z przechodzeniem przez to odkryłem, że dla vim-buffów dobrze jest mieć również:
[core]
editor = vim -c ':normal gg'
Myślę, że to najczystsza odmiana ed:
cat myheader | { echo '0a'; cat ; echo -e ".\nw";} | ed myfile
jako funkcja:
function prepend() { { echo '0a'; cat ; echo -e ".\nw";} | ed $1; }
cat myheader | prepend myfile
IMHO nie ma rozwiązania powłoki (i nigdy nie będzie), które działałoby konsekwentnie i niezawodnie niezależnie od rozmiarów dwóch plików myheader
i myfile
. Powodem jest to, że jeśli chcesz to zrobić bez powracania do pliku tymczasowego (i bez pozwalania powłoce na ciche powtarzanie się do pliku tymczasowego, np. Przez konstrukcje takie jak exec 3<>myfile
, rurociąg do tee
itp.
"Prawdziwe" rozwiązanie, którego szukasz, musi majstrować przy systemie plików, więc nie jest dostępne w przestrzeni użytkownika i byłoby zależne od platformy: prosisz o zmodyfikowanie wskaźnika systemu plików używanego przez myfile
do bieżącej wartości wskaźnika systemu plików for myheader
i zamień w systemie plików EOF
of myheader
połączonym odnośnikiem do bieżącego adresu systemu plików wskazywanego przez myfile
. Nie jest to trywialne i oczywiście nie może tego zrobić użytkownik niebędący superużytkownikiem, a prawdopodobnie też superużytkownik ... Graj z i-węzłami itp.
Możesz to mniej więcej udawać, używając urządzeń pętlowych. Zobacz na przykład ten wątek SO .
mktemp
? Zawsze możesz później wyczyścić plik tymczasowy ...