Расширение составной подстроки Bash

Я знаю, что могу использовать awk для анализа нескольких разделителей, но это порождает subprocessы. Я хотел знать, возможно ли расширение составного / вложенного bash-параметра.

У меня есть PDF-файлы в каталоге с именем «Px_MM-DD-YY_SSSSSSSSSS.pdf», где:

  • «Px» означает «Страница x», а x не имеет начальных нhive.
  • «ММ» соответствует двузначному месяцу с начальным нулем, если это применимо.
  • «DD» соответствует двухзначному дню с начальным нулем, если применимо.
  • «YY» соответствует двухзначному году с начальным нулем, если применимо.
  • «SSSSSSSSSS» соответствует десятизначному времени эпохи создания PDF-файла, что позволяет мне сохранять ревизии PDF-страницы.

У меня есть цикл for (я опущу «-mtime», когда буду готов работать со всеми PDF-файлами)

 for file in $(find -type f -iname '*_??????????.pdf' -mtime -1) do echo $file done 

где я хочу повторить только эпоху времени.

Я могу использовать это для цикла

 for file in $(find -type f -iname '*_??????????.pdf' -mtime -1) do echo ${file##*_} done 

и для файла, названного как “./P14_07-21-18_4X_1532144458.pdf”, “1532144458.pdf” отображается на экране.

Я могу использовать это для цикла

 for file in $(find -type f -iname '*_??????????.pdf' -mtime -1) do echo ${file%.*} done 

и для файла, названного как “./P14_07-21-18_4X_1532144458.pdf”, “./P14_07-21-18_4X_1532144458” отображается на экране.

Если я заменю строку echo ... на любой из приведенных ниже форматов

 echo ${${file##*_}:0:10} echo ${(${file##*_}):0:10} echo ${${file##*_}%.*} echo ${{file%.*}##_} echo ${${file%.*}##_} 

Я получаю -bash: ... : bad substitution . Правильно ли я понимаю синтаксис или не возможно расширение с помощью вложенных / составных команд?

Вы не можете выполнить вложенную замену с переменной в самой левой части . Таким образом, вы можете делать ${foo#$bar} , но не то, что вы показываете.

Поместите результат подстановки в переменную, если вы хотите использовать его в дальнейших подстановках.

Вы не можете использовать подстановку параметров для результата подстановки другого параметра, не сохранив сначала первоначальный результат в переменной и не применив в ней вторую подстановку.

Вы также перебираете результаты find , что не рекомендуется .

Правильный способ предоставить цикл с результатом find – это вызвать дочернюю оболочку и выполнить цикл там:

 find . -type f -iname '*_??????????.pdf' -mtime -1 -exec sh -c ' for pathname do timestamp=${pathname##*_} # remove up to last _ timestamp=${timestamp%.pdf} # remove .pdf printf "pathname=%s\ttimestamp=%s\n" "$pathname" "$timestamp" done' sh {} + 

Таким образом, вам не нужно беспокоиться о том, каковы реальные пути. Имена файлов (то есть имена файлов и каталогов и других типов файлов) в Unix могут содержать любые символы, отличные от / и \0 , например пробел и символ новой строки. Используя подстановку команд при find , вы заставляете оболочку, во-первых, выполнять разбиение слов (по умолчанию на пробелах, табуляциях и новых строках) и, во-вторых, выполнять генерацию имени файла по шаблонам, найденным в путевых именах, возвращаемых из find . Таким образом, ваш первоначальный цикл может закончиться циклически по совершенно другим словам, чем вы ожидаете.

Связанные с:

  • Понимание опции -exec `find`

Если я правильно понимаю, проблема использования Awk заключается в том, что вы вызываете один процесс Awk для каждого файла PDF (я предполагаю, что у вас ОГРОМНОЕ количество таких файлов).

Вы могли бы запустить что-то вроде

 find . ...... -print0 | perl -0nE '/.*_(\d{10}).pdf/ and say "$1.pdf"' 

Или, если вы сохраняете свою структуру:

 for file in $(find .....| perl ....) do ... done 

(и, конечно, замените команду Perl любым эквивалентом Awk, sed, Python)

(Если у вас есть возможность попробовать этот подход, сообщите нам time .... получено)