Сед на OSX вставьте в определенную строку

24

Так что я некоторое время использовал «sed» в linux, но столкнулся с некоторыми трудностями при попытке использовать его в OSX, поскольку «POSIX sed» и «GNU sed» имеют так много небольших различий. В настоящее время я борюсь с тем, как вставить строку текста после определенного номера строки. (в данном случае строка 4)

На Linux я бы сделал что-то вроде этого:

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

Итак, на OSX я попробовал это:

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

Однако это продолжает давать мне сообщение «дополнительные символы после \ в конце команды». Есть идеи, что здесь не так? У меня есть опечатка? Или я не понимаю другую разницу между версиями sed?

CRThaze
источник
2
Можно подтвердить, что это не работает на MacOS 10.6.8. Отлично работает на Debian 6.0.3.
Тинк

Ответы:

24

Строго говоря, спецификация POSIX дляsed требует новой строки после a\:

[1addr]a\
text

Запишите текст в стандартный вывод, как описано ранее.

Это делает написание острот немного боли, которая, вероятно , является причиной для последующего расширения GNU к a, iи cкоманды:

В качестве расширения GNU, если между aи новой \строкой есть иная, чем пробельная последовательность, то текст этой строки, начиная с первого непробельного символа после a, берется в качестве первой строки текстового блока. (Это позволяет упростить в сценариях однострочной надстройки.) Это расширение также работает с iи cкомандами.

Таким образом, чтобы быть переносимым с вашим sedсинтаксисом, вам нужно будет включить новую строку после a\каким-либо образом. Самый простой способ - просто вставить цитируемую строку:

$ sed -e 'a\
> text'

(где $и >находятся подсказки оболочки). Если вы обнаружите, что боль, bash[1] имеет $' 'синтаксис цитирования для вставки escape-символов в стиле C, так что просто используйте

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

[1] и mksh (r39b +) и некоторые не-bash-оболочки Борна (например, / bin / sh в FreeBSD 9+)

jw013
источник
Очень информативный ответ. Спасибо за подсказку по синтаксису цитирования bash.
Джексон
14

Как описано в этом ответе , полезно использовать команды sed, такие как iand, и aиспользовать несколько -e "..."предложений. Каждый из этих пунктов будет пониматься как разделенный новыми строками. iИ aкоманды трудно использовать в инлайн СЭД сценарии иначе (они предназначены для использования в многострочного СЭД файл сценария вызывается с помощью sed -f file ...). Похоже, вы не можете использовать неявный символ новой строки, введенный в конце -eпредложения, чтобы отделить a\строку текста от строки, которая должна быть добавлена. Но вы можете использовать его для завершения строки текста, которая будет добавлена.

В этом конкретном случае то, что вы пытаетесь сделать, может фактически быть выполнено с помощью одного -e ...предложения. Вам просто нужно правильно использовать aкоманду. В соответствии со стандартом POSIX, после него aдолжен следовать символ «a» \, затем за ним должна следовать новая строка, затем будет вставлена ​​оставшаяся часть следующей строки (до тех пор, пока не встретится новая строка или конец -eпредложения). Так что вы могли бы сделать:

sed -i "" -e $'4 a\\\n'"mode '0755'" file.txt
dubiousjim
источник
2
Если вы хотите разобраться, на какие функции вы полагаетесь, это Gnu-isms, эта страница может помочь: wiki.alpinelinux.org/wiki/Regex . Это ограничено только функциями регулярного выражения; не другие команды sed.
dubiousjim
5

GNU sed, кажется, гораздо чаще используется, чем POSIX sed, основываясь на примерах / руководствах, которые я использовал в любом случае.

Я обнаружил, что гораздо проще просто установить GNU sed на любую машину с OSX, которую я использую. Если вам интересно, лучший способ сделать это - установить его через Homebrew .

Вы можете либо просто $ brew install gnu-sed, либо получить все общие утилиты GNU с $ brew install coreutils.

Затем, когда вы столкнетесь с проблемой синтаксиса с dateкакой-либо другой программой, вы можете просто использовать версию GNU. В конце концов я просто решил, что всегда проще использовать версии GNU и ставить их раньше, чем системные версии в моем PATH.

декан
источник
5

Perl не страдает от таких зависимых от платформы GNU против не-GNU против «проприетарных» идиосинкразий. Вы могли бы сделать:

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 -eсигнализирует о начале сценария.

$.Обозначает линию-номер, начиная с линии-1. Таким образом, командная строка читает «файл», и когда номер строки равен 4, она печатает эту строку, за которой следует то, что вы хотите вставить.

Спешу добавить, что Perl обязан своими корнями sedAWK и C, поэтому синтаксис простых подстановок и т. Д. Не очень крутая кривая обучения.

JRFerguson
источник
Хороший ответ! Но вы можете немного упростить это. Попробуйте это: perl -wlpi.old -e 'print "mode 0755" if $. == 5' file.txt. -pПереключатель хорошо работает здесь (так как мы хотим напечатать каждую строку); нам просто нужно изменить логику с «печать после строки 4» на «печать до строки 5». И -lпереключатель делает так, что «\ n» печатается автоматически с каждым print, так что вам не нужно печатать его самостоятельно.
JL