利用可能な次のファイルサフィックス(file_a.txt file_b.txtなど)を見つける方法

利用可能な次のファイルサフィックス(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は0です。したがってz、すべての戦略が許可されます。aa整数、さらにはUUIDも使用してください。

答え1

以下は(エラーチェックなし)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()恥ずかしく盗んだ。アルファベットASCII値を取得するBashスクリプト

答え2

本当に気にしない場合は、Linuxで(より正確には以下を使用してください。)GNUコアツール):

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

これはGNUを使用しますバックアップ命名方式file.txt、、、、、file.txt.~1~file.txt.~2~

別の比較的簡単なアプローチは、数値をより便利な位置に配置できることを活用することです。zshのglob修飾子最新のファイルを見つけて、いくつかを使用して次のファイルを計算します。パラメータ拡張

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シェルと同様に、ゼロの付いた数字を使用すると簡単に作業できます。前にゼロがある整数リテラルは、8進数で解析されます。

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"

答え3

努力する:

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

これはシェルグローブソートのためにfile_10.txtafter file_9.txtfile_g.txtafter file_f.txtfile_aa.txtafter を提供しますfile_z.txtが、file_ab.txtそれ以降file_aa.txtfile_11.txt提供しません。file_10.txtfile*file_z.txt 後ろに file_aa.txtfile_9.txtfile_10.txt

zshfile*.txt(n)代わりに、後者の問題を解決できますfile*.txt

zshあるいは、これらの基準を使用して、aa36abc進数の数字として認識される数値ソート順序を定義することもできます。

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

(手順は…7、8、9、a/A、b/B…、z/Z、10、11…なので合計を混ぜたくありません。file_123.txtfile_aa.txt

答え4

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))))))

関連情報