команда sed для замены последнего вхождения слова в файле содержимым другого файла

Я не использую Ubuntu, но не думаю, что это возможно в более новых версиях glibc. См. commit .

Если не считать написания собственного детектора разрушения стека, конечно.

Вы можете посмотреть источник функции, печатающей это сообщение:

void
__attribute__ ((noreturn))
__fortify_fail_abort (_Bool need_backtrace, const char *msg)
{
  /* The loop is added only to keep gcc happy.  Don't pass down
     __libc_argv[0] if we aren't doing backtrace since __libc_argv[0]
     may point to the corrupted stack.  */
  while (1)
    __libc_message (need_backtrace ? (do_abort | do_backtrace) : do_abort,
            "*** %s ***: %s terminated\n",
            msg,
            (need_backtrace && __libc_argv[0] != NULL
             ? __libc_argv[0] : ""));
}

Эта функция будет вызываться с помощью need_backtrace = Falseиз __stack_chk_fail, которая сама вызывается из кода защиты стека, скомпилированного в двоичный файл.

0
28.05.2021, 18:16
4 ответа

В три шага, используя синтаксис sed, совместимый с /usr/bin/sedв macOS, и либо bash, либоzsh(две основные оболочки в текущих системах macOS):

sed -n '/hello/=' sample.txt |
sed -e '$!d' -e $'s/.*/&r sample1.txt\\\n&d/' |
sed -f /dev/stdin sample.txt

Здесь используется sedв три шага:

  1. Находит все строки в sample.txt, соответствующие hello, и выводит номера строк, соответствующие этим строкам.

  2. Удаляет все, кроме последнего номера строки, выведенные на первом шаге (с помощью $!d, «если это не последняя строка, удалите ее» )и создает две -строки sedскрипт, который изменит последнюю совпадающую строку, сначала прочитав sample1.txt, а затем удалив исходную строку. Учитывая, что последнее совпадение helloнаходится в строке 3 в исходном файле, этот скрипт будет выглядеть как

    3r sample1.txt
    3d
    
  3. Применяет созданный скрипт sedк файлу sample.txt.

Если вы хотите сделать редактирование «в -месте», чтобы изменить исходный sample.txt, тогда используйте

sed -n '/hello/=' sample.txt |
sed -e '$!d' -e $'s/.*/&r sample1.txt\\\n&d/' |
sed -i '' -f /dev/stdin sample.txt

Тот же набор команд, но с использованием ваших переменных $sample_file_pathи $sample_file_path_1для двух путей к файлам:

sed -n '/hello/=' "$sample_file_path" |
sed -e '$!d' -e 's,.*,&r '"$sample_file_path_1"$'\\\n&d,' |
sed -i '' -f /dev/stdin "$sample_file_path"

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

6
28.07.2021, 11:29

Попробуйте комбинацию tacи awk:

tac sample.txt \
| awk -v replacement="$(tac sample1.txt)" '/hello/ && !n++ {gsub(/hello/,replacement,$0)}1' \
| tac

Обратите внимание, что это также заменит часть helloиз nothello. Если вы хотите заменить только независимые hello, вам нужно добавить границы слов в шаблон:/\<hello\>/вместо/hello/

2
28.07.2021, 11:29

Мы можем использовать утилиту редактора потока sed, чтобы получить желаемый результат.

sed -n '
  /hello/!{
    H;1h;$!d;g
    s///w file.tmp
    t a
    p;q
  }
  x;1!p;$!d
  :a
  r file2
' file1
sed -e '1d' file.tmp
  • накапливает линии в ожидании, пока не увидит строку hello
  • в строке hello, он печатает то, что было сохранено в ожидании, и снова запускает цикл.
  • только в EOFили последней строке мы печатаем пробелы и шаблоны.

Или другой способ —tac-perl-tacконвейер:

tac file1 |
perl -0777 -pe 's?hello\n?qx/tac file2/?e' - | 
tac
0
28.07.2021, 11:29

С помощью одного вызова + с использованиемPosix-ly конструкций sedмы можем сделать так, как показано. USP этого подхода заключается в том, что код sed сам генерирует файл, который он позже будет считывать в поток печати.

sed -n '
  /\n/{
    s/.$//w file.tmp
    b a
  }
  /hello/!{
    H;1h;$!d
    s/.*//;x
    //!{p;q;}
    G;D
  }
  x;1!p;$!d;:a
  r file2
  r file.tmp
' file1

Краткое объяснение того, как это работает:

  • Накопить приветственную строку и последующие не приветственные строки в резервном пространстве.
  • Теперь, когда мы читаем следующую строку, и она оказывается приветственной, мы знаем, что строка приветствия в пространстве не может быть последней, поэтому мы очищаем область удержания.
  • Это следующий цикл событий, в котором мы сохраняем строку приветствия наверху, а последующие строки без приветствия — под ней.
  • Цикл (flushhold;store /hello/+!/hello/+!/hello/... )продолжается до тех пор, пока не будет достигнут EOF.
  • Теперь в eof может возникнуть один из 3 возможных сценариев :
    • Во всем файле никогда не было строки /hello/, поэтому удержание увеличилось, чтобы содержать общее количество строк, и мы сбрасываем его с помощью конструкции //!{p;q;}. На данном этапе мы уже находимся в eof и проверили регулярное выражение /hello/, поэтому можем повторно использовать его как //.
    • Второй сценарий — наличие /hello/+ по крайней мере одного не приветствия. Здесь нам нужно обрезать верхнюю часть строки /hello/ и заменить ее содержимым file2, так как это ПОСЛЕДНЯЯ строка /hello/. Но ложка дегтя в том, что нам нужна другая часть (не приветственных строк ). Если бы мы просто сделали r file2, это поместило бы содержимое в конец. Поэтому мы должны придумать что-то, чтобы преодолеть это. Что мы делаем, так это сохраняем эти не приветственные строки в файле, а затем выполняем r tmpfile. ПБМ решил.
    • Третий вариант: последняя строка была приветственной. Здесь tmpfile не существует, и мы используем свойство команды чтения, чтобы не жаловаться на несуществующие файлы.
2
28.07.2021, 11:29

Теги

Похожие вопросы