Создайте новый файл, но добавьте номер, если имя файла уже существует в bash

Я нашел похожие вопросы, но не в Linux/Bash

Я хочу, чтобы мой script создавал файл с заданным именем (через пользовательский ввод), но добавлял номер в конце, если имя файла уже существует.

Пример:

$ create somefile
Created "somefile.ext"
$ create somefile
Created "somefile-2.ext"

Спасибо за чтение

Ответы

Ответ 1

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

name=somefile
if [[ -e $name.ext ]] ; then
    i=0
    while [[ -e $name-$i.ext ]] ; do
        let i++
    done
    name=$name-$i
fi
touch "$name".ext

Ответ 2

Чтобы избежать условий гонки:

name=some-file

n=
set -C
until
  file=$name${n:+-$n}.ext
  { command exec 3> "$file"; } 2> /dev/null
do
  ((n++))
done
printf 'File is "%s"\m' "$file"
echo some text in it >&3

И кроме того, у вас есть файл, открытый для записи на fd 3.

Изменить

Собственно, это действительно не снимает условия гонки. set -C только предотвращает клонирование обычных файлов (так что cmd > /dev/null, например, не прерывается) и имеет условие гонки в большинстве оболочек.

Если есть файл с этим именем, оболочка сначала выполняет stat(2) на нем, чтобы проверить, является ли он обычным файлом или нет (fifo, directory, device...). Только если файл не существует или является обычным файлом, 3> "$file" использует флаг O_EXCL, чтобы гарантировать, что он не сбивает файл.

Итак, если у него есть файл fifo или файл устройства, он будет использоваться (при условии, что он может быть открыт только для записи), и обычный файл может быть взломан, если он создается в качестве замены для fifo/device/directory... между этими stat(2) и open(2) без O_EXCL!

Для рабочей версии вы можете использовать zsh:

zmodload zsh/system

name=some-file

n=
until
  file=$name${n:+-$n}.ext
  sysopen -w -o excl -u 3 -- "$file" 2> /dev/null
do
  ((n++))
done
printf 'File is "%s"\m' "$file"
echo some text in it >&3

Ответ 3

Попробуйте что-то вроде этого (непроверенный, но вы получите идею):

filename=$1

# If file doesn't exist, create it
if [[ ! -f $filename ]]; then
    touch $filename
    echo "Created \"$filename\""
    exit 0
fi

# If file already exists, find a similar filename that is not yet taken
digit=1
while true; do
    temp_name=$filename-$digit
    if [[ ! -f $temp_name ]]; then
        touch $temp_name
        echo "Created \"$temp_name\""
        exit 0
    fi
    digit=$(($digit + 1))
done

В зависимости от того, что вы делаете, замените вызовы на touch на любой код, необходимый для создания файлов, с которыми вы работаете.

Ответ 4

Попробуйте что-то вроде этого

name=somefile
path=$(dirname "$name")
filename=$(basename "$name")
extension="${filename##*.}"
filename="${filename%.*}"
if [[ -e $path/$filename.$extension ]] ; then
    i=2
    while [[ -e $path/$filename-$i.$extension ]] ; do
        let i++
    done
    filename=$filename-$i
fi
target=$path/$filename.$extension

Ответ 5

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

Он также может быть скопирован для имени файла.

LAST_SOLUTION=$(echo $(ls -d SOLUTION_[[:digit:]][[:digit:]][[:digit:]][[:digit:]] 2> /dev/null) | awk '{ print $(NF) }')
if [ -n "$LAST_SOLUTION" ] ; then
    mkdir SOLUTION_$(printf "%04d\n" $(expr ${LAST_SOLUTION: -4} + 1))
else
    mkdir SOLUTION_0001
fi

Ответ 6

Простая переупаковка choroba answer как обобщенная функция:

autoincr() {
    f="$1"
    ext=""

    # Extract the file extension (if any), with preceeding '.'
    [[ "$f" == *.* ]] && ext=".${f##*.}"

    if [[ -e "$f" ]] ; then
        i=1
        f="${f%.*}";

        while [[ -e "${f}_${i}${ext}" ]]; do
            let i++
        done

        f="${f}_${i}${ext}"
    fi
    echo "$f"
}

touch "$(autoincr "somefile.ext")"