Awk поиск и замена на строку, содержащую «\n», добавит новую строку вместо написания «\n»

Я использую Awk для поиска ключевого слова <replaceme> и замены его строкой. Эта строка содержит символы «\n», но Awk интерпретирует ее как новую строку. Я очень хочу сохранить обоих символов: \ и n.

У меня есть строка, содержащая настоящую новую строку, и мне нужно заменить ее символами «\n». Оно работает:

mytext=$'hello\nhow are you'
mytext2=${mytext//$'\n'/\\n}

echo "$mytext"
hello
how are you

echo "$mytext2"
hello\nhow are you

Тогда у меня есть ключевое слово <replaceme> в тексте, и я хотел бы заменить его содержанием $mytext2.

echo 'this is the text: <replaceme>' |
awk -v srch = "<replaceme>" -v repl = "$mytext2" '{
    gsub(srch,repl,$0); print $0 }'

Я получаю этот результат.

this is the text: hello
how are you

Но мне бы хотелось:

this is the text: hello\nhow are you

Подскажите, пожалуйста, как действовать?

Примечание: на самом деле текст «это текст:» находится в файле, и я использую cat myfile.txt | ок...

🤔 А знаете ли вы, что...
С помощью Bash можно создавать и запускать скрипты для автоматизации повседневных задач.


1
84
3

Ответы:

Решено

Вы можете использовать это awk решение:

mytext=$'hello\nhow are you'
echo 'this is the text: <replaceme>' |
awk -v srch = "<replaceme>" -v repl = "${mytext//$'\n'/\\\\n}" '{gsub(srch,repl)} 1'

this is the text: hello\nhow are you

Замена BASH ${mytext//$'\n'/\\\\n} заменит каждый экземпляр \n на \\n перед передачей этой строки в качестве аргумента awkrepl.


Чистое решение awk (без bash):

echo 'this is the text: <replaceme>' |
awk -v srch = "<replaceme>" '
BEGIN {
   repl = ARGV[1]
   gsub(/\n/, "\\n", repl)
   delete ARGV[1]
}
{
   gsub(srch, repl)
} 1' "$mytext"

this is the text: hello\nhow are you

Вы специально ПРОСИТЕ awk преобразовать \n в буквальную новую строку, используя -v для установки переменных awk, поскольку интерпретация escape-последовательностей - это то, для чего предназначен -v. Если это не то поведение, которое вам нужно, просто не делайте этого - вместо этого используйте ENVIRON[] или ARGV[] для заполнения переменных awk, чтобы переменная оболочки или содержимое строки передавались в awk как есть:

$ mytext=$'hello\nhow are you'
$ mytext2=${mytext//$'\n'/\\n}
$ echo 'this is the text: <replaceme>' |
old = "<replaceme>" mytext2 = "$mytext2" awk '
    BEGIN { srch=ENVIRON["old"]; repl=ENVIRON["mytext2"] }
    { gsub(srch,repl,$0); print $0 }
'
this is the text: hello\nhow are you

$ mytext=$'hello\nhow are you'
$ mytext2=${mytext//$'\n'/\\n}
$ echo 'this is the text: <replaceme>' |
awk '
    BEGIN { srch=ARGV[1]; repl=ARGV[2]; ARGV[1]=ARGV[2] = "" }
    { gsub(srch,repl,$0); print $0 }
' "<replaceme>" "$mytext2"
this is the text: hello\nhow are you

См. Как использовать переменные оболочки в скрипте awk? для получения дополнительной информации.

Обратите внимание: поскольку вы используете gsub() для поиска и замены, вам все равно придется беспокоиться о метасимволах регулярного выражения, таких как . или *, в строке srch и метасимволе обратной ссылки & в строке repl.

Если вы хотите выполнить буквальный поиск и замену строк, вам нужно либо избежать любых возможных метасимволов, например. (непроверено):

old = "<replaceme>" mytext2 = "$mytext2" awk '
    BEGIN {
        srch = ENVIRON["old"]
        repl = ENVIRON["mytext2"]
        gsub(/[^^\\]/, "[&]", srch)
        gsub(  /\/  ,  "&&", srch)
        gsub(  /\^/  , "\\&", srch)
        gsub(   /&/  , "\\&", repl)
    }
    { gsub(srch,repl,$0); print $0 }
'

(см. Можно ли надежно экранировать метасимволы регулярных выражений с помощью sed для эквивалента sed и объяснения того, что делают эти gsub()) или использовать строковые операторы (index() и substr()) вместо регулярного выражения+обратной ссылки (gsub()).

Кстати, не очевидно, почему вы начинаете со строки hello\nhow are you, содержащей два символа \n, затем используете bash $'...', чтобы преобразовать ее в новую строку, затем используете ${mytext//$'\n'/\\n}, чтобы преобразовать ее обратно в \n, а затем вызываете awk с -v для преобразуйте его обратно в новую строку, а затем спросите, как напечатать его как исходные два символа \n. Вы делаете:

  1. 'hello\nhow are you' -> \n
  2. mytext=$'hello\nhow are you' -> <newline>
  3. mytext2=${mytext//$'\n'/\\n} -> \n
  4. awk -v repl = "$mytext2" -> <newline>
  5. awk '{... print ...}' -> \n (хочу)

Похоже, вы совершаете слишком много конверсий! Вы могли бы избежать первых 2 или 3 конверсий в своем вопросе, просто начав с mytext2='hello\nhow are you' или даже awk -v repl='hello\nhow are you'.


Спасибо всем вам за ваши ответы! Оно работает.

Да, мой пример неочевиден, я хотел поделиться кратким примером, описывающим проблему. Я также не хотел копировать/вставлять код, связанный с компанией, в которой я работаю, поэтому я создал этот фиктивный пример. К сожалению, у меня нет никаких знаний о awk, я использовал -v, потому что нашел в Интернете пример использования переменной с -v.

Чтобы я мог решить свою проблему с помощью repl="${mytext//$'\n'/\\n}".

Да, я много конвертирую. Можем ли мы избежать этого и найти более приятное решение?

Фактически, мой реальный вариант использования:

  • У меня есть скрипт, который создает несколько сертификатов.
  • содержимое сертификатов должно быть включено в файл certificats.json (со специальной структурой json).
  • Я создал certificats.json.tpl (шаблон), который содержит некоторые параметры <certificats_1>, <certificats_2>, <certificats_3> и т. д., куда необходимо скопировать/вставить содержимое файла.
  • Что я сделал: -- 1) прочитать certificats_1.pem с помощью 'cat' и поместить его в переменную $certificats_1. То же самое для _2, _3 и т. д. -- 2) с помощью awk замените <certificats_1> на $certificats_1 в certificats.json.tpl (шаблон), чтобы создать certificats.json. То же самое для _2 и _3 и т. д. -- 3) один сертификат имеет формат pem и содержит новые строки. Инструменты, обрабатывающие certificat.json, вместо этого требуют буквального \n.

Может ли awk принять содержимое файла в качестве аргумента?

Мое текущее решение, которое работает, но не элегантно Спасибо

сертификаты.json.tpl

{ 
  "pkcs12": [ 
    { 
      "name": "certificats_1-key", 
      "certificate-name": "certificats_1-cert", 
      "pkcs12": "<certificats_1>", 
      "password": "<password>" 
    }, 
    { 
      "name": "certificats_2-key", 
      "certificate-name": "certificats_2-cert", 
      "pkcs12": "<certificats_2>", 
      "password": "<password>" 
    }, 
    {
      "name": "certificats_3-key", 
      "certificate-name": "certificats_3-cert", 
      "pkcs12": "<certificats_3>", 
      "password": "<password>" 
    } 
  ], 
  "ca-certs": [ 
    { 
      "name": "certificats_trusted", 
      "pem": "<certificats_trusted>" 
    } 
  ] 
}
password = "thisisasecret"
certificats_1=$(cat certificats_1.p12.base64)
certificats_2=$(cat certificats_2.p12.base64)
certificats_3=$(cat certificats_3.p12.base64)
certificats_trusted=$(cat certificats_trusted.pem)


cat certificats.json.tpl | \
awk -v srch = "<password>"            -v repl = "$password"                                 '{ gsub(srch,repl,$0); print $0 }' | \
awk -v srch = "<certificats_1>"       -v repl = "$certificats_1"                            '{ gsub(srch,repl,$0); print $0 }' | \
awk -v srch = "<certificats_2>"       -v repl = "$certificats_2"                            '{ gsub(srch,repl,$0); print $0 }' | \
awk -v srch = "<certificats_3>"       -v repl = "$certificats_3"                            '{ gsub(srch,repl,$0); print $0 }' | \
awk -v srch = "<certificats_trusted>" -v repl = "${certificats_trusted//$'\n'/\\\\n}"       '{ gsub(srch,repl,$0); print $0 }'
> certificats.json