내 시스템은 특정 이벤트가 발생할 때마다 새 텍스트 파일을 생성합니다.
파일 이름 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 셸과 마찬가지로 앞에 0이 붙은 숫자를 사용하면 더 쉽게 작업할 수 있습니다. 앞에 0이 있는 정수 리터럴은 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.txt
after file_9.txt
, file_g.txt
after file_f.txt
, file_aa.txt
after 를 제공 file_z.txt
하지만 file_ab.txt
이후 file_aa.txt
또는 file_11.txt
이후에는 제공 하지 않습니다.file_10.txt
file*
file_z.txt
뒤쪽에 file_aa.txt
후에.file_9.txt
file_10.txt
zsh
file*.txt(n)
대신 을 사용하여 후자의 문제를 해결할 수 있습니다 file*.txt
.
zsh
또는 이러한 기준으로 가 aa
36 abc
진법의 숫자로 인식되는 숫자 정렬 순서를 정의할 수 있습니다 .
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
답변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))))))