Как найти следующий доступный суффикс файла (file_a.txt file_b.txt и т. Д.)

Моя система создает новый текстовый файл каждый раз, когда происходит определенное событие.
Файлы должны быть названы file_a.txt file_b.txt file_c.txt и т. Д.

В сценарии оболочки Bash, как узнать, какое имя файла следует использовать дальше?

Например, если file_a.txt и file_b.txt существуют, но не file_c.txt , то следующее доступное имя файла – file_c.txt .

Это может быть число, если это проще.
Я начал разрабатывать алгоритм, но, вероятно, есть более простой способ?

Примечание. Файлы удаляются каждый день, поэтому вероятность достижения z равна нулю. Итак, после z любая стратегия приемлема: aa , используя целые числа или даже используя UUID.

  • Приостановка rsync через скрипт bash?
  • Сценарий для подключения с ssh к удаленному серверу с паролем
  • Проблема bc о длинном выражении
  • Выполнить все файлы в списке
  • Как ssh на нескольких ipaddress и получить вывод и ошибку на локальной машине * nix?
  • Отправка сценария в tty вместо vt (определенная проблема, приветствуются другие решения)
  • Узнайте, на какой строке в текстовом файле соответствует слово
  • scp при попытке скопировать файлы из Linux в windows
  • 5 Solutions collect form web for “Как найти следующий доступный суффикс файла (file_a.txt file_b.txt и т. Д.)”

    Вот грубый способ (без проверки ошибок) сделать это исключительно в bash:

     #helper function to convert a number to the corresponding character chr() { [ "$1" -lt 256 ] || return 1 printf "\\$(printf '%03o' "$1")" } #helper function to convert a character to the corresponding integer ord() { LC_CTYPE=C printf '%d' "'$1" } #increment file fn_incr(){ #first split the argument into its constituent parts local fn prefix letter_and_suffix letter suffix next_letter fn=$1 prefix=${fn%_*} letter_and_suffix=${fn#${prefix}_} letter=${letter_and_suffix%%.*} suffix=${letter_and_suffix#*.} #increment the letter part next_letter=$(chr $(($(ord "$letter") + 1))) #reassemble echo "${prefix}_${next_letter}.${suffix}" } 

    Пример использования:

     fn_incr foo_bar_A.min.js #=> foo_bar_B.min.js 

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

    Использование простых старых целых чисел может быть лучшим выбором здесь, так как вам не придется вручную управлять переполнением 9 ++ влево.


    chr() и ord() были бесстыдно украдены из сценария Bash, чтобы получить значения ASCII для алфавита

    Если вам все равно, на Linux (точнее, с GNU coreutils ):

     tmpfile=$(TMPDIR=. mktemp --backup=numbered) … # create the content mv --backup=numbered -- "$tmpfile" file.txt 

    Это использует схему file.txt резервного копирования GNU: file.txt , file.txt.~1~ , file.txt.~2~ , …

    Другим относительно компактным способом, с числами, которые можно разместить в более удобном месте, является использование квалификаторов glob zsh для поиска последнего файла и вычисления следующего файла с некоторым расширением параметра .

     latest=(file_<->.txt(n[-1])) if ((#latest == 0)); then next=file_1.txt else latest=$latest[1] next=${${latest%.*}%%<->}$((${${latest%.*}##*[^0-9]}+1)).${latest##*.} fi mv -- $tmpfile $next 

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

     move_to_next () { shift $(($#-2)) case ${1%.*} in *\*) mv -- "$2" file_0001.txt;; *) set -- "${1%.*}" "${1##*.}" "$2" set -- "${1%_*}" "$((1${1##*_}+1)).$2" "$3";; mv -- "$3" "${1}_${2#1}";; esac } move_to_next file_[0-9]*.txt "$tmpfile" 

    Пытаться:

     perl -le 'print $ARGV[-1] =~ s/[\da-zA-Z]+(?=\.)/++($i=$&)/er' file*.txt 

    Это даст вам file_10.txt после file_9.txt , file_g.txt после file_f.txt , file_aa.txt после file_z.txt , но не file_ab.txt после file_aa.txt или file_11.txt после file_10.txt потому что file* shell glob будет сортировать file_z.txt после file_aa.txt и file_9.txt после file_10.txt .

    Это последнее, с которым вы можете работать с zsh , используя file*.txt(n) вместо file*.txt .

    Или вы можете определить числовой порядок сортировки в zsh , основываясь на тех aa , abc которые распознаются как числа в базе 36:

     b36() REPLY=$((36#${${REPLY:r}#*_})) perl ... file_*.txt(no+b36) 

    (обратите внимание, что порядок … 7, 8, 9, a / A, b / B …, z / Z, 10, 11 … поэтому вы не хотите смешивать file_123.txt и file_aa.txt ).

    Это выводит следующее последовательное имя файла. Идентификатор может быть любой длины и может быть либо числовым, либо буквенным. Этот образец загрунтован, чтобы использовать альфа-идентификатор, причем первым идентификатором a

     pfix='file_' sfix='.txt' idbase=a # 1st alpha id when no files exist - use a decimal number for numeric id's idpatt='[az]' # alpha glob pattern - use '[0-9]' for numeric id's shopt -s extglob idhigh=$( ls -1 "$pfix"+($idpatt)"$sfix" 2>/dev/null | awk 'length>=l{ l=length; id=substr($0,'${#pfix}'+1,length-'${#pfix}-${#sfix}') } END{ print id }' ) [[ -z $idhigh ]] && echo "$pfix$idbase$sfix" || perl -E '$x="'$idhigh'"; $x++; print "'${pfix}'"."$x"."'${sfix}'\n"' 

    Если подходящий файл не существует, вывод:

     file_a.txt 

    Если самый высокий файл соответствия – file_zzz.txt , вывод:

     file_aaaa.txt 

    Эта проблема может быть решена с помощью python с использованием различных блоков итератора, доступных в модуле itertools

     from os.path import isfile from string import ascii_lowercase from itertools import dropwhile, imap, chain, product, repeat, count next(dropwhile(isfile, imap('file_{}.txt'.format, imap(''.join, chain.from_iterable( product(ascii_lowercase, repeat=x) for x in count(1)))))) 
    Linux и Unix - лучшая ОС в мире.