Czy jest jakaś alternatywa dla komendy „sed -i” w Solarisie?

11

W moim projekcie wymagam zastąpienia istniejącego tekstu w pliku, takim jak fooinnym tekstem, takim jak fooofoo:

abc.txt
name
foo
foo1

Więc próbowałem:

sed -i "s/foo/fooofoo/g" abc.txt

Jednak pojawia się ten błąd:

sed: nielegalna opcja - i

W instrukcji znalazłem, że muszę użyć:

sed -i\ "s/foo/fooofoo/g" abc.txt

Jednak to też nie działa.

Znalazłem w alternatywnych perli awkteż ale to rozwiązanie w Solaris sedbędą mile widziane.

Korzystam z tej wersji bash:

GNU bash, wersja 3.2.57 (1) -release (sparc-sun-solaris2.10)

tpsaitwal
źródło
W systemie Solaris 11 i nowszych wystarczy użyć, /usr/gnu/bin/sedaby uzyskać obsługę -i.
alanc

Odpowiedzi:

15

Zastosowanie ed. Jest dostępny na większości platform i może edytować pliki w miejscu.
Ponieważ sedopiera się edna składni zastępowania wzorców jest podobny:

ed -s infile <<\IN
,s/old/new/g
w
q
IN
don_crissti
źródło
Dlaczego edzamiast po exprostu ciekawy? (Wiem, że oba są zgodne z POSIX i podłączyłem do specyfikacji.;))
Wildcard
1
@Wildcard - mogę zapytać odwrotnie - dlaczego ex? Aby odpowiedzieć na twoje pytanie: ponieważ nigdy ex nie używam, nie znam go. btw, widziałem konfiguracje, w których edbył obecny, ale exnie był (ale nie odwrotnie) ... najbardziej zauważalny jest mój laptop :)
don_crissti 14.10.16
Dobra odpowiedź. :) Moja odpowiedź: Ponieważ cały czas używam Vima, bardzo dobrze znam expolecenia. Wszystkie vipolecenia, które można wpisać zaczynające się dwukropkiem, są expoleceniami. Istnieje oczywiście kilka rozszerzeń specyficznych dla Vima, ale tylko podstawowy zestaw poleceń jest niewiarygodnie wydajny i elastyczny i jest wystarczający do edytowania skryptów.
Wildcard
Mój system Manjaro ma, exa jeszcze nie ma ed.
połowie
8

Jeśli nie możesz zainstalować GNU sed, użyj:

sed "s/foo/fooofoo/g" abc.txt >abc.tmp && mv abc.tmp abc.txt

Używa przekierowania, aby wysłać dane wyjściowe sed do pliku tymczasowego. Jeśli sed zakończy się powodzeniem, to zastąpi abc.txtto plik tymczasowy.

Jak widać z kodu źródłowego GNU sed , właśnie to sed -irobi. Jest to więc tak samo wydajne jak sed -i.

Jeśli istnieje już szansa abc.tmp, możesz użyć mktemplub podobnego narzędzia do wygenerowania unikalnej nazwy tymczasowej.

John1024
źródło
Dzięki za pomoc. Czy jest jednak jakakolwiek opcja w solaris, dzięki której możemy zaktualizować istniejący plik bez tworzenia pliku tymczasowego?
tpsaitwal
10
Nie chcę ci tego łamać, ale nawet sed tworzy plik tymczasowy. git.savannah.gnu.org/cgit/sed.git/tree/sed/sed.c#n84
Jeff Schaller
1
Możesz to zrobić, jeśli nie zależy ci na twardych linkach - patrz mój przykład. Plik tymczasowy nadal jest ukryty (nienazwany). Bez tymczasowego ustawienia należy użyć ed, ponieważ wczytuje cały plik do pamięci.
Toby Speight
3

Jeśli chcesz ekwiwalent sed -i.bak, jest to dość proste.

Rozważ ten skrypt dla GNU sed:

#!/bin/sh

# Create an input file to demonstrate
trap 'rm -r "$dir"' EXIT
dir=$(mktemp -d)
grep -v '[[:upper:][:punct:]]' /usr/share/dict/words | head >"$dir/foo"

# sed program - removes 'aardvark' and 'aardvarks'
script='/aard/d'

##########
# What we want to do
sed -i.bak -e "$script" "$dir"
##########

# Prove that it worked
ls "$dir"
cat "$dir/foo"

Możemy po prostu zamienić zaznaczoną linię na

cp "$dir/foo" "$dir/foo.bak" && sed -e "$script" "$dir/foo.bak" >"$dir/foo"

To przenosi istniejący plik jako kopię zapasową i zapisuje nowy plik.

Jeśli chcemy odpowiednika

sed -i -e "$script" "$dir"  # no backup

to jest nieco bardziej złożone. Możemy otworzyć plik do odczytu jako standardowe wejście, a następnie rozłączyć go, zanim wydamy polecenie sed w celu zastąpienia:

( cp "$dir/foo" "$dir/foo.bak"; exec <"$dir/foo.bak"; rm "$dir/foo.bak"; exec sed -e "$script" >"$dir/foo" )

Robimy to w podpowłoce, aby nasz oryginalny stdin był nadal dostępny po tym. Możliwe jest przełączanie wejść i przełączanie z powrotem bez podpowłoki, ale ten sposób wydaje mi się jaśniejszy.

Pamiętaj, że staramy się najpierw skopiować, a nie utworzyć nowy fooplik - jest to ważne, jeśli plik jest znany pod więcej niż jedną nazwą (tzn. Ma twarde linki) i chcesz mieć pewność, że nie zerwiesz linków .

Toby Speight
źródło
3

Po pierwsze, powinieneś wiedzieć, że i\polecenie, do którego się odwołujesz, służy do wstawiania wiersza tekstu - a nie do zapisywania edytowanego tekstu z powrotem do pliku. Nie ma POSIX określony sposób użyć seddo tego.

Co można zrobić, to użyć ex, który jest określony przez POSIX :

printf '%s\n' '%s/find/replace/g' 'x' | ex file.txt

xKomenda jest na „zapisz i wyjdź”. Możesz także użyć, wqale xjest krótszy.

%Znak na początku środki dowodzenia substytutem „Zastosuj tego polecenia do każdej linii w buforze.” Możesz 1,$s/find/replace/gtakże użyć .


Jedną z głównych różnic między exi sedjest to, że sedjest edytorem strumieniowym . Działa tylko sekwencyjnie, linia po linii. exjest o wiele bardziej elastyczny i można bezpośrednio edytować interaktywny plik tekstowy ex. Jest bezpośrednim poprzednikiem vi.

Dzika karta
źródło
1

Używanie sedi brak widocznego pliku tymczasowego:

Możesz uniknąć tworzenia osobnego widocznego „pliku tymczasowego”:

exec 3<abc.txt
rm abc.txt
sed 's/foo/fooofoo/' <&3 >abc.txt
exec 3<&-

Wyjaśnienie

Uniksowych systemów rzeczywistości nie usunąć zawartość pliku z dysku aż nadszedł zarówno odłączone w systemie plików, a nie otworzyć w innym procesie. Więc możesz zrobić, exec 3<aby otworzyć plik w powłoce w celu odczytania na deskryptorze pliku 3, rmpliku (który odłącza go od systemu plików), a następnie wywołać za sedpomocą deskryptora pliku 3 używanego jako jego dane wejściowe.

Pamiętaj, że jest to bardzo różne od tego:

# Does not work.
sed 's/foo/fooofoo/' <abc.txt >abc.txt

Różnica polega na tym, że gdy zrobisz to za pomocą jednego polecenia, powłoka po prostu otwiera ten sam plik zarówno do odczytu, jak i do zapisu z opcją obcięcia pliku - ponieważ nadal jest to ten sam plik, tracisz zawartość. Ale jeśli otworzysz go do odczytu, a następnie rm, a następnie otworzysz tę samą ścieżkę do zapisu, tak naprawdę tworzysz nowy plik pod tą samą ścieżką (ale w nowej lokalizacji i-węzła i lokalizacji dysku, ponieważ oryginał jest nadal otwarty): więc zawartość jest nadal dostępna.

Następnie, gdy skończysz, możesz zamknąć deskryptor pliku, który wcześniej otworzyłeś (to robi exec 3<&-specjalna składnia), co zwalnia oryginalny plik, aby system operacyjny mógł usunąć (oznaczyć jako nieużywany) swoje miejsce na dysku.

Ostrzeżenia

O tym rozwiązaniu należy pamiętać:

  1. Dostajesz tylko jedno „przejście” przez zawartość - nie ma przenośnego sposobu, aby powłoka „szukała” z powrotem w deskryptorze pliku - więc gdy program odczyta część zawartości, inne programy zobaczą tylko resztę pliku. I sedprzeczyta cały plik.

  2. Istnieje niewielka szansa na utratę oryginalnego pliku, jeśli twoja powłoka / skrypt / sed zostanie zabita, zanim zostanie wykonana.

mtraceur
źródło
Żeby było jasne, @TobySpeight opublikował już przykład tego rozwiązania na końcu swojej odpowiedzi, ale myślałem, że przydałoby się bardziej szczegółowe opracowanie.
mtraceur
Do downvoter'a: chociaż jestem pewien, że samo pozostawienie opinii negatywnej sprawia, że ​​czujesz się dobrze z tobą, byłbym wdzięczny, gdybyś przyczynił się więcej, przekazując opinie na temat problemów, które masz z tą odpowiedzią, jak można ją poprawić itp. ,
mtraceur
może możesz użyć perla -i
c4f4t0r
@ c4f4t0r: Przepraszamy za późną odpowiedź: Tak, perl -ito kolejna dobra opcja. Pytanie dotyczyło w szczególności rozwiązania zgodnego z Solaris sed, w przeciwieństwie do rozwiązań z perli awko których wspominali już o znalezieniu. Ponadto moja odpowiedź miała głównie na celu dodanie czegoś, co moim zdaniem było przydatne i którego nie znam w żadnej z pozostałych odpowiedzi.
mtraceur