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 | ок...

Измените repl на repl = "${mytext2//\/\\\\}". Вам необходимо убедиться, что обратная косая черта перед n удвоена, чтобы \n нельзя было проанализировать как новую строку.

Wiktor Stribiżew 27.08.2024 09:56
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
84
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 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

1. Вам не нужны 5 команд awk, достаточно одной, и 2. Вам точно не нужно использовать cat

anubhava 27.08.2024 19:43

Не добавляйте дополнительную информацию в «ответ» или комментарии и не меняйте существенно вопрос после того, как получили ответы. Просто примите ответ на заданный вами вопрос, а затем задайте новый дополнительный вопрос, если он у вас есть.

Ed Morton 27.08.2024 20:45

Относительно «Можем ли мы избежать и найти более приятное решение?» - да. Относительно «Может ли awk принимать содержимое файла в качестве аргумента?» - да. Однако не используйте sed, awk или аналогичные символы для управления JSON, используйте такой инструмент, как jq, который понимает JSON.

Ed Morton 27.08.2024 20:47

Другие вопросы по теме