sed — завершенная команда `s’

#bash #sed

#bash #sed

Вопрос:

У меня есть этот мир кода:

 cat BP.csv | while read line ; do
    goterm=$(awk '{print $1}') ;
    name=$(awk '{print $2}') ;
    grep -w "$goterm" GOEA.csv | sed "s/$goterm/pi/g" ;
done
  

файл BP.csv имеет этот формат:

 GO:0008283  cell proliferation
GO:0009405  pathogenesis
GO:0010201  response to continuous far red light stimulus by the high-irradiance response system
GO:0009641  shade avoidance
  

пока GOEA.csv имеет такой формат:

 4577    GO:0006807  0.994   2014_06_01
4577    GO:0016788  0.989   2014_06_01
4577    GO:0043169  0.977   2014_06_01
4577    GO:0043170  0.963   2014_06_01
  

sed не работает. Я хочу изменить GO:0043170 , например, на строку «pi», но это дает:

 sed: -e expression #1, char 12: unterminated `s' command
  

Почему?
Спасибо.

Комментарии:

1. Каково значение goterm?

2. @Avinash Raj, серьезно? Разве вы не видите, что goterm — это первый столбец файла BP.csv?

3. Ick, ты ведь понимаешь, что эти две awk команды обе считываются из stdin , а не из $line переменной, верно?

4. @Debasis, ты действительно об этом спрашиваешь?

5. @ams прав, поскольку ваш awk , в частности, выглядит неисправным. Весь скрипт может быть переписан в самом awk.

Ответ №1:

Вы запускаете свою awk команду без ввода, попробуйте это:

 cat BP.csv | while read line ; do
    goterm=$(awk '{print $1}' <<< "$line") ;
    name=$(awk '{print $2}' <<< "$line" ) ;
    grep -w "$goterm" GOEA.csv | sed "s/$goterm/pi/g" ;
done
  

Комментарии:

1. 1. awk фактически выполнялся с использованием stdin, поэтому $goterm содержал список значений первого столбца, разделенный переводом строки, начиная со строки 2 и далее. Поскольку s выражение тогда имело неэкранированный перевод строки в середине, sed сказал, что оно было незавершенным.

2. @thatotherguy вы правы, иначе awk зависал бы в ожидании ввода.

Ответ №2:

Давайте немного почистим этот код:

 while read goterm name
do
    grep -w "$goterm" GOEA.csv | sed "s/$goterm/pi/g"
done < BP.cvs
  

Проблема в том, что ваши инструкции awk пытаются считывать данные из STDIN точно так же, как это делает ваш while . Вы читаете из того же входного потока.

Что вы хотите сделать, так это извлечь значения из вашей строки. Я использую read для этого. read Инструкция использует значения в $IFS для разделения входных данных. Обычно это пробелы, табуляции и новые строки. read Считывает каждую переменную, которую вы вводите в строку, и последнее прочитанное значение содержит всю оставшуюся часть строки.

Таким образом:

 while read line
  

считывает всю строку, пока:

 while goterm name
  

приведет к разрыву строки как

 goterm="GO:0008283"
name="cell proliferation"
  

Еще одна вещь. Когда вы используете grep и sed вместе, вам, вероятно, сойдет с рук просто sed :

 while read goterm name
do
    sed -n "/$goterm/s/$goterm/pi/gp" GOEA.csv
done < BP.csv
  

Формат для команды sed является:

 /lines/command/parameters/
  

Итак, я ищу строки с $goterm в них, затем я заменяю $goterm на pi . -n Средства не выводят строки, поскольку sed их обрабатывает, и p средства для вывода строк, в которых была найдена замена.

Кстати, csv как суффикс файла означает значения, разделенные запятыми, но ни один файл не выглядит так, как будто он разделен запятой. Являются ли эти вкладки разделяющими каждое поле. Если это так, вам нужно будет изменить $IFS , чтобы это были вкладки.

Ответ №3:

Я бы реструктурировал все это примерно так:

 while read goterm restofline
do
  grep -w "${goterm}" GOEA.csv | sed -e "s/${goterm}/pi/g"
done < BP.csv
  

Для awk всего этого нет причин, поскольку bash read встроенный компонент выполнит элементарное разделение полей за вас, если вы зададите ему несколько переменных. Кроме того, вы все равно не используете name , так что это не нужно. cat также не является необходимым.

В зависимости от вашего конкретного варианта использования даже grep может быть ненужным, что делает внутреннюю команду простой sed -ne "s/${goterm}/pi/gp" GOEA.csv . Если только вашей целью для grep -w не является исключение строк, где ${goterm} является подстрокой слова вместо целого слова…

Для справки в будущем, вставив set -x над вашим циклом в вашем скрипте, вы увидите точные команды, которые выполняются, чтобы вы могли сравнить их с вашими ожиданиями.