sed na wstawce OSX w pewnej linii

24

Od jakiegoś czasu używam „sed” na Linuksie, ale miałem trochę trudności z próbą użycia go na OSX, ponieważ „POSIX sed” i „GNU sed” mają tak wiele małych różnic. Obecnie mam problem z tym, jak wstawić wiersz tekstu po określonym numerze wiersza. (w tym przypadku wiersz 4)

Na Linuksie zrobiłbym coś takiego:

sed --in-place "4 a\  mode '0755'" file.txt

Więc na OSX próbowałem tego:

sed -i "" "4 a\ mode '0755'" file.txt

Daje mi to jednak błąd „dodatkowe znaki po \ na końcu polecenia”. Jakieś pomysły, co tu jest nie tak? Czy mam literówkę? Czy też nie rozumiem innej różnicy między wersjami sed?

CRThaze
źródło
2
Potwierdza to, że nie działa w systemie MacOS 10.6.8. Działa dobrze na Debianie 6.0.3.
zadzwonić

Odpowiedzi:

24

Ściśle mówiąc, specyfikacja POSIX dlased wymaga nowej linii po a\:

[1addr]a\
text

Napisz tekst na standardowe wyjście, jak opisano wcześniej.

To sprawia, że pisanie jednej wkładki trochę bólu, co jest prawdopodobnie powodem następującym rozszerzeniem GNU do a, ioraz ckomendy:

Jako rozszerzenie GNU, jeśli między wierszem aa nowym \wierszem znajduje się inna niż sekwencja białych znaków , to tekst tego wiersza, rozpoczynający się od pierwszego niebiałego znaku po znaku a, jest traktowany jako pierwszy wiersz bloku tekstowego . (Umożliwia to uproszczenie skryptowania dodawania jednowierszowego.) To rozszerzenie działa również z poleceniami ii c.

Dlatego, aby być przenośnym ze swoją sedskładnią, musisz w a\jakiś sposób dołączyć nowy wiersz . Najprostszym sposobem jest po prostu wstawienie cytowanego nowego wiersza:

$ sed -e 'a\
> text'

(gdzie $i >są podpowiedziami powłoki). Jeśli okaże się, że ból bash[1] ma $' 'składnię cytowania do wstawiania znaków ucieczki w stylu C, po prostu użyj

sed -e 'a\'$'\n''text'

[1] i mksh (r39b +) i niektóre powłoki bourne bez bash (np. / Bin / sh w FreeBSD 9+)

jw013
źródło
Bardzo pouczająca odpowiedź. Dziękujemy za podpowiedź na temat składni cytatów bash.
cjackson
14

Jak opisano w tej odpowiedzi , jest to przydatne, gdy używasz komend sed, takich jak ii aużywasz wielu -e "..."klauzul. Każdą z tych klauzul należy rozumieć jako oddzieloną znakami nowej linii. W przeciwnym razie polecenia ii asą trudne do użycia w wbudowanych skryptach sed (są zaprojektowane do użycia w wielowierszowym pliku skryptu sed wywoływanym przy użyciu sed -f file ...). Wygląda na to, że nie można użyć niejawnej nowej linii wprowadzonej na końcu -eklauzuli, aby oddzielić a\i wiersz tekstu, który należy dołączyć. Ale możesz go użyć do zakończenia wiersza tekstu, który chcesz dołączyć.

W tym konkretnym przypadku to, co próbujesz zrobić, może zostać osiągnięte za pomocą jednej -e ...klauzuli. Musisz tylko apoprawnie użyć polecenia. Zgodnie ze standardem POSIX, po nim amusi następować a \, następnie musi następować nowa linia, następnie zostanie wstawiona reszta następnego linii (aż -enapotka się nową linię lub koniec klauzuli). Więc możesz zrobić:

sed -i "" -e $'4 a\\\n'"mode '0755'" file.txt
dubiousjim
źródło
2
Jeśli chcesz dowiedzieć się, na jakich funkcjach polegasz, to Gnu-isms, ta strona może pomóc: wiki.alpinelinux.org/wiki/Regex . Jest to jednak ograniczone tylko do funkcji wyrażenia regularnego; nie inne komendy sed.
dubiousjim
5

Wydaje się, że GNU sed jest znacznie częściej używany niż sed POSIX, na podstawie przykładów / samouczków, których i tak użyłem.

Przekonałem się, że dużo łatwiej jest po prostu zainstalować GNU sed na dowolnym komputerze OSX, którego używam. Jeśli jesteś zainteresowany, najlepszym sposobem na to jest zainstalowanie go przez Homebrew .

Możesz albo po prostu $ brew install gnu-seduzyskać wszystkie popularne narzędzia GNU $ brew install coreutils.

Następnie, gdy napotkasz problem ze składnią w programie datelub innym programie, możesz po prostu użyć wersji GNU. W końcu zdecydowałem, że łatwiej jest zawsze używać wersji GNU i umieścić je wcześniej niż wersje systemowe w moim PATH.

Dziekan
źródło
5

Perl nie cierpi na takie zależne od platformy GNU vs. non-GNU vs. „zastrzeżone” osobliwości. Mógłbyś:

perl -ni.old -e 'print;if ($.==4) {print "mode 0755\n"}' file

-n' option creates a loop that reads every line of the input file. Unlike its cousin-P (not used here) it doesn't automatically print every line read. The-i invokes in-place replacement. The argument to-i (viz. ".old") can be dropped or changed. It leaves a backup of the unmodified file. The -esygnalizuje początek skryptu.

$.Oznacza line-numer, zaczynając od linii-1. W ten sposób wiersz poleceń odczytuje „plik”, a gdy numer wiersza jest równy 4, wypisuje ten wiersz, a następnie cokolwiek chcesz wstrzyknąć.

Chciałbym dodać, że Perl zawdzięcza swoje korzenie sedAWK i C, więc składnia prostych podstawień itp. Nie jest bardzo stromą krzywą uczenia się.

JRFerguson
źródło
Niezła odpowiedź! Ale możesz to trochę uprościć. Spróbuj tego: perl -wlpi.old -e 'print "mode 0755" if $. == 5' file.txt. -pPrzełącznik działa dobrze tutaj (ponieważ chcemy wydrukować każdą linię); musimy tylko zmienić logikę z „drukowanie po linii 4” na „drukowanie przed linią 5”. A -lprzełącznik czyni go tak, że „\ n” jest drukowany automatycznie z każdym print, więc nie trzeba go wydrukować samodzielnie.
JL