Создайте файл, но не сработайте, если он существует, с bash [duplicate]

SyntaxError: unexpected EOF while parsing означает, что конец вашего исходного кода был достигнут до того, как все кодовые блоки были завершены. Блок кода начинается с выражения типа for i in range(100): и требует, по крайней мере, одной строки, которая содержит код, который должен быть в нем.

Кажется, что вы выполняли вашу программу по строкам в консоли ipython. Это работает для отдельных операторов типа a = 3, но не для блоков кода, например для циклов. См. Следующий пример:

In [1]: for i in range(100):
  File "<ipython-input-1-ece1e5c2587f>", line 1
    for i in range(100):
                        ^
SyntaxError: unexpected EOF while parsing

Чтобы избежать этой ошибки, вы должны ввести весь блок кода как один вход:

In [2]: for i in range(5):
   ...:     print(i, end=', ')
0, 1, 2, 3, 4,
8
задан Jimm 11 December 2012 в 23:36
поделиться

5 ответов

Решение 100% чистого bash:

set -o noclobber
{ > file ; } &> /dev/null

Эта команда создает файл с именем file, если нет файла с именем file. Если есть файл с именем file, то ничего не делайте (но возвращайте ненулевой код возврата).

Прыжки по команде touch:

  • t обновить временную метку, если файл уже существует
  • 100% bash builtin
  • Код возврата, как ожидалось: сбой, если file уже существует или невозможно создать file; если file не существует и был создан.

Минусы:

  • необходимо установить опцию noclobber (но все в порядке скрипт, если вы осторожны с перенаправлением или после этого отмените его).

Я предполагаю, что это решение действительно является аналогом bash системного вызова open с O_CREAT | O_EXCL.

18
ответ дан gniourf_gniourf 22 August 2018 в 07:43
поделиться
  • 1
    +1 - короткий и чистый. – Don Branson 12 December 2012 в 00:02
  • 2
    «& Gt; & gt; & gt; файл & Quot; гарантированный атомный? – Achal Dave 13 February 2016 в 02:33
  • 3
    (set -o noclobber;>file) &>/dev/null делает то же самое, но не влияет на параметр noclobber в текущей оболочке. – Rich Remer 22 November 2017 в 08:42
  • 4
    jc, это настолько сложно для такой общей потребности lol – Olegzandr Denman 1 March 2018 в 02:19

Вы можете создать его под произвольно созданным именем, а затем переименовать (mv -n random desired) на место с нужным именем. Переименование не будет выполнено, если файл уже существует.

Нравится это:

#!/bin/bash

touch randomFileName
mv -n randomFileName lockFile

if [ -e randomFileName ] ; then
    echo "Failed to acquired lock"
else
    echo "Acquired lock"
fi
2
ответ дан Don Branson 22 August 2018 в 07:43
поделиться
  • 1
    My & quot; mv & quot; не является атомарным. Сначала он использует "stat" чтобы проверить, существует ли целевой файл, и если он не использует «переименование». Но "переименовать" будет просто перезаписать цель, если она существовала. Таким образом, существует небольшой временной интервал (между «статусом» и «переименованием»), в котором файл, созданный с именем адресата, будет перезаписан. – SIGSEGV 14 November 2014 в 17:05
  • 2
    Ах, интересно. mv вообще не поможет, тогда, извините. – Don Branson 14 November 2014 в 17:07

touch - это команда, которую вы ищете. Он обновляет временные метки предоставленного файла, если файл существует или создает его, если он этого не делает.

-2
ответ дан Kylo 22 August 2018 в 07:43
поделиться
  • 1
    Кроме того, это не сработает, если файл уже существует, как это делает O_EXCL. – Don Branson 11 December 2012 в 23:31
  • 2
    Я не смотрел на источник касания, но он не может быть атомарным. Otoh, он изменяет как время доступа, так и время модификации, если файл существует. – ott-- 11 December 2012 в 23:35
  • 3
    касание не является атомарным – Jimm 11 December 2012 в 23:37

Вот функция bash с использованием трюка mv -n:

function mkatomic() {
  f="$(mktemp)"
  mv -n "$f" "$1"
  if [ -e "$f" ]; then
    rm "$f"
    echo "ERROR: file exists:" "$1" >&2
    return 1
  fi
}

Примеры:

$ mkatomic foo
$ wc -c foo
0 foo
$ mkatomic foo
ERROR: file exists: foo
4
ответ дан thejoshwolfe 22 August 2018 в 07:43
поделиться

Чтобы быть ясным, гарантировать, что файл будет создан только в том случае, если он не существует, это не то же самое, что атомарность. Операция является атомной тогда и только тогда, когда два или несколько отдельных потоков пытаются сделать одно и то же в одно и то же время, точно один будет успешным, а все остальные потерпят неудачу.

Лучший способ, которым я знаю создайте файл атомарно в сценарии оболочки, следуя этому шаблону (и это не идеально):

  1. создайте файл, который имеет чрезвычайно высокую вероятность отсутствия (используя приличный случайный выбор номера или что-то в имя файла) и поместить в него какой-то уникальный контент (что-то, что ни один другой поток не имел бы - снова, случайное число или что-то еще)
  2. убедитесь, что файл существует и содержит содержимое, которое вы ожидаете от него
  3. создать жесткую ссылку из этого файла в желаемый файл
  4. убедиться, что нужный файл содержит ожидаемое содержимое

В частности, touch не является атомарным, так как он создаст файл, если его там нет, или просто обновить метку времени. Возможно, вы сможете играть в игры с разными отметками времени, но чтение и синтаксический анализ временной метки, чтобы увидеть, выиграл ли вы гонку, сложнее, чем указано выше. mkdir может быть атомарным, но вам нужно будет проверить код возврата, потому что в противном случае вы можете только сказать, что «да, каталог был создан, но я не знаю, какой поток выиграл». Если вы находитесь в файловой системе, которая не поддерживает жесткие ссылки, вам, возможно, придется согласиться на менее идеальное решение.

2
ответ дан twalberg 22 August 2018 в 07:43
поделиться
  • 1
    Открытый syscall поддерживает флаг O_EXCL, который в сочетании с O_CREAT приведет к сбою открытого вызова, если файл уже существует. Это атомная операция. – Ryan Patterson 31 May 2018 в 18:10
  • 2
    @RyanPatterson Исправить. Но вы не можете называть open() с O_EXCL непосредственно из сценария bash, что и было в исходном вопросе. – twalberg 31 May 2018 в 18:44