Почему я получаю эту неизвестную ошибку опции

Я новичок в сценариях UNIX и пытаюсь использовать команду sed для замены текста, но продолжаю получать эту ошибку:

sed: -e выражение #1, символ 51: неизвестная опция для `s'

Вот фрагмент кода, который у меня есть:

sed -e "/^#PBS -o ${rundir}/posttest/s#_CMODEL#${cmodel}#"  \
      -e "/^#PBS -e ${rundir}/posttest/s#_CMODEL#${cmodel}#"  \
     ${rundir}/run_post.shell >${rundir}/run_post.${cmodel}.sh

Пожалуйста, дайте мне знать, что я делаю неправильно. Большое спасибо!!!


person user3431573    schedule 21.05.2015    source источник
comment
Есть ли в ${rundir} / символа?   -  person Barmar    schedule 21.05.2015
comment
Если вы ожидаете, что /^#PBS -o ${rundir}/posttest/ будет шаблоном регулярного выражения, указывающим, к каким строкам будет применяться команда s, вам нужно избежать внутреннего /. В противном случае было бы полезно некоторое объяснение того, что вы пытаетесь сделать.   -  person rici    schedule 21.05.2015
comment
@Barmar да ${rundir} имеет / в нем это что-то меняет   -  person user3431573    schedule 21.05.2015
comment
Вам нужно экранировать все косые черты в регулярном выражении, как явные перед posttest, так и те, которые находятся в переменной.   -  person Barmar    schedule 21.05.2015
comment
@rici Я пытаюсь заменить _CMODEL тем, что хранится в переменной cmodel. Прямо сейчас этого не происходит, когда имя файла остается posttest_CMODEL   -  person user3431573    schedule 21.05.2015
comment
Как избежать косых черт в переменной?   -  person user3431573    schedule 21.05.2015
comment
Если вы используете bash, вы можете попробовать sed -e "/^#PBS -\(o\|e\) ${rundir//\//\\\/}\/postest/s#_CMODEL#${cmodel}#" ${rundir}/run_post.shell >${rundir}/run_post.${cmodel}.sh.   -  person alvits    schedule 21.05.2015
comment
Ой, забыл убрать опцию -e в моем предыдущем комментарии.   -  person alvits    schedule 21.05.2015
comment
Ухх, вместо того, чтобы экранировать содержимое вашей переменной, вы всегда можете просто использовать разделители регулярных выражений, которые не являются косой чертой. В качестве разделителя можно использовать практически любой неспециальный символ. Попробуйте : или ! или даже ,. например: s/foo/bar/ совпадает с s!foo!bar! или s,foo,bar,   -  person Sammitch    schedule 21.05.2015
comment
@alvits: хороший улов. Но -[eo] намного приятнее для глаз, чем -\(o\|e\), а \| — это расширение Gnu sed, которое может быть доступно не во всех sed.   -  person rici    schedule 21.05.2015
comment
@Sammitch - это полезно только для команды s. Попробуйте это для address локации и посмотрите, что получится. Сравните sed -n '/root/p' /etc/passwd с sed -n '!root!p' /etc/passwd или sed -n '|root|p' /etc/passwd и поделитесь с нами своим опытом.   -  person alvits    schedule 21.05.2015
comment
@rici - я полностью с тобой согласен.   -  person alvits    schedule 21.05.2015


Ответы (2)


Проблема в том, что регулярное выражение, которое вы используете в качестве адреса, заканчивается первым /, который может быть в ${rundir} или может быть / сразу после него; это точно не / после posttest.

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

Учитывая это предупреждение, у вас есть несколько вариантов. Если вы знаете, что какой-то символ, скажем, :, не появляется в ${rundir}, вы можете использовать этот символ в качестве терминатора регулярного выражения. (Обратите внимание на обратную косую черту, с помощью которой вы сообщаете sed, что последующее является регулярным выражением.)

sed -e "\:^#PBS -o ${rundir}/posttest:s#_CMODEL#${cmodel}#"  \
    -e "\:^#PBS -e ${rundir}/posttest:s#_CMODEL#${cmodel}#"  \
    "${rundir}/run_post.shell" >"${rundir}/run_post.${cmodel}.sh"

В качестве альтернативы вы можете экранировать букву / с обратной косой чертой и использовать синтаксис замены шаблона bash для экранирования с обратной косой черты всех / в $rundir:

${rundir//\//\\/}

(Как упоминалось выше, вам действительно следует избегать всех возможных метасимволов, включая обратную косую черту.)

person rici    schedule 21.05.2015

Вам нужно избежать косых черт в регулярном выражении, которое используется в качестве адреса. В противном случае первая косая черта завершит регулярное выражение, а следующий символ будет воспринят как команда sed для работы с выбранными строками. Вам нужно сделать это как для косой черты в $rundir, так и для косой черты, которая у вас есть между ${rundir} и posttest.

Для переменной вы можете экранировать косые черты, используя синтаксис bash ${parameter/pattern/replacement}, описанный здесь.

sed -e "/^#PBS -o ${rundir//\//\\/}\/posttest/s#_CMODEL#${cmodel}#"  \
      -e "/^#PBS -e ${rundir//\//\\/}\/s#_CMODEL#${cmodel}#"  \
     ${rundir}/run_post.shell >${rundir}/run_post.${cmodel}.sh
person Barmar    schedule 21.05.2015