Заменить цифры в строке шаблоном, содержащим количество замененных цифр - PullRequest
2 голосов
/ 27 января 2020

Я пытаюсь заменить число, состоящее из n цифр в имени файла, шаблоном, подобным% 03ld или% 04ld соответственно (в зависимости от количества цифр).

Например:

  • "img/img_000.png" -> "img/img_%03ld.png"
  • "RA20190201_A/img_1030.tif" -> "RA20190201_A/img_%04ld.tif"
  • ".../SomeImage_z004.tif" -> ".../SomeImage_z%03ld.tif"

As Вы можете видеть

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

Я думаю, что это можно легко сделать, используя python (или другие языки), но я пытаюсь сделать это один вкладыш с linux встроенными инструментами, такими как awk или sed с использованием труб.

Мне удалось посчитать цифры с помощью функции awk s gsub:

> echo "9001_bla/img_0001.png" | awk '{print gsub(/[0-9]/, "")}'
8

Но мне не удалось выделить подсчет на соответствующей части и выполнить фактическую замену.

Как этого добиться?

Ответы [ 4 ]

1 голос
/ 28 января 2020

Это может работать для вас (параллельно с GNU):

parallel echo "{//}/{= s:.*/::;s/\d+/length($&)/e;s//%0$&ld/ =}" :::: file

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

Альтернативный GNU sed & Bash:

sed -E 's/^(.*\/[^0-9]*)(.*)(\..*)/v=\2;echo \1%0${#v}ld\3/e' file
1 голос
/ 27 января 2020

Не лучшее решение, но:

sed "$(seq 10 -1 1 | sed 's@.*@ s/[0-9]\\{&\\}\\(\\.[^\\.]*\\)$/%0&ld\\1/; t @')"
  • seq 10 -1 1 - генерировать числа от 10 до 1
  • Внутренний sed
    • s - заменить команду
    • @ - разделитель
    • .* - заменить целое число, ie. в каждой строке
    • @ - разделитель
    • s/[0-9]\\{&\\}\\.\\([^\\.]*\\)/%0&ld.\\1/; t - вместо числа & подставляется число, а двойные \\ заменяются на одинарные \.
    • @ - разделитель
  • Таким образом, выход внутреннего sed составляет:

     s/[0-9]\{10\}\(\.[^\.]*\)$/%010ld\1/; t
     s/[0-9]\{9\}\(\.[^\.]*\)$/%09ld\1/; t
     s/[0-9]\{8\}\(\.[^\.]*\)$/%08ld\1/; t
     s/[0-9]\{7\}\(\.[^\.]*\)$/%07ld\1/; t
     s/[0-9]\{6\}\(\.[^\.]*\)$/%06ld\1/; t
     s/[0-9]\{5\}\(\.[^\.]*\)$/%05ld\1/; t
     s/[0-9]\{4\}\(\.[^\.]*\)$/%04ld\1/; t
     s/[0-9]\{3\}\(\.[^\.]*\)$/%03ld\1/; t
     s/[0-9]\{2\}\(\.[^\.]*\)$/%02ld\1/; t
     s/[0-9]\{1\}\(\.[^\.]*\)$/%01ld\1/; t
    
  • Внешний sed запускает вывод внутреннего sed:

    • s - заменить
    • / - разделитель
    • [0-9]\{10\}\(\.[^\.]*\)$ - сопоставить 10 чисел с последующим точка, за которой следует что угодно, но не точка и конец строки. Сохраните точку и расширение в обратной ссылке.
    • / - разделитель
    • %010ld\.\1 - замените его на %0, число 10, ld, соответствующую точку с расширением
    • / - разделитель
    • ; - разделитель команд
    • t - если последняя команда s была успешной, она переходит к началу сценария. Поэтому, если одна команда s успешно выполняется, она читает следующую строку и перезапускается.
    • И это повторяется для каждого числа.
1 голос
/ 27 января 2020

Если доступен gnu awk, мы можем использовать FPAT и функцию gensub() для написания однострочного:

awk -v FPAT="[0-9]+" '$0=gensub(/(.*[^0-9])([0-9]+)(\.[^.]*$)/, 
      "\\1%0"length($NF)"ld\\3","g")'

Небольшой тест:

kent$  cat f
123_foo_t_1.txt
123_foo_t_12.txt
123_foo_t_123.txt
123_foo_t_1234.txt
123_foo_t_12345.txt
123_foo_t_123456.txt

kent$  awk -v FPAT="[0-9]+" '$0=gensub(/(.*[^0-9])([0-9]+)(\.[^.]*$)/, 
      "\\1%0"length($NF)"ld\\3","g")' f
123_foo_t_%01ld.txt
123_foo_t_%02ld.txt
123_foo_t_%03ld.txt
123_foo_t_%04ld.txt
123_foo_t_%05ld.txt
123_foo_t_%06ld.txt
1 голос
/ 27 января 2020

Один из способов сделать это в awk

awk 'BEGIN {
  FS=OFS="."
}
match($(NF-1),/[0-9]+$/) {
  $(NF-1)=(substr($(NF-1),1,RSTART-1) "%0" RLENGTH "ld")
} 1'
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...