Цикл while для перевірки наявності файлу в bash


94

Я працюю над сценарієм оболонки, який робить певні зміни у файлі txt, лише якщо він існує, проте цей цикл тесту не працює, цікаво, чому? Дякую!

while [ ! -f /tmp/list.txt ] ;
do
      sleep 2
done

3
Не можу сказати, що я здивований; цей цикл не намагається щось змінити.
Ігнасіо Васкес-Абрамс

Двокрапка є зайвою. Яким чином цей тестовий цикл не працює? Він буде ітеративно спати протягом 2 секунд, поки файл /tmp/list.txt не існує.
Джонатан Леффлер,

5
У мене працює - цикл завершується, коли файл створюється поза сценарієм.

1
насправді, цей цикл служить лише для того, щоб зачекати, поки файл не буде, решта мого сценарію вносить зміни ...: p
Zenet,

1
Тоді цикл while працює, це лише я ... вибачте.
Зенет,

Відповіді:


145

Коли ви говорите "не працює", звідки ви знаєте, що це не працює?

Ви можете спробувати з’ясувати, чи файл насправді існує, додавши:

while [ ! -f /tmp/list.txt ]
do
  sleep 2 # or less like 0.2
done
ls -l /tmp/list.txt

Ви також можете переконатися, що використовуєте оболонку Bash (або пов'язану з нею), набравши 'echo $ SHELL'. Я думаю, що CSH та TCSH використовують дещо іншу семантику для цього циклу.


Чому ви використовуєте інвертовану перевірку файлів? Чи не слід використовувати замість цього [-f /tmp/list.txt]?
valentt

2
@valentt Немає циклу букв, сказати буквально "поки файл НЕ існує, спи" ... якщо ви видалите "НЕ", цикл миттєво
розірветься

2
в 1 рядку:while [ ! -f /tmp/list.txt ]; do sleep 2; done; ls -l /tmp/list.txt
DrumM

55

Якщо ви працюєте на Linux і встановили inotify-tools, ви можете зробити це:

file=/tmp/list.txt
while [ ! -f "$file" ]
do
    inotifywait -qqt 2 -e create -e moved_to "$(dirname $file)"
done

Це зменшує затримку, спричинену сном, одночасно опитуючи кожні "х" секунд. Ви можете додати більше подій, якщо передбачаєте, що вони потрібні.


4
+1 для ефективності. Об’єднання сну потворно. Для людей, які не знають inotifywait - це в комплекті inotify-tools.
Michał Šrajer

7
Це надзвичайно зручний інструмент. Для тих, хто задається питанням, чому цикл, це вирішення можливих умов перегонів між створенням та очікуванням, а тому, що inotifywait --excludeповинен відфільтрувати імена файлів, але не --includeігнорувати все, крім імені файлу. Вищезазначена команда повинна використовувати -qqаргумент, а не >&/dev/nullхоча.
Крейг Рінгер,

t - це --timeout не частота перевірки, ні? Сенс inotifywait полягає в тому, що немає опитування
Алекс Дін,

1
@AlexDean Час очікування - запобігти перегоновим умовам. Опитування в режимі сну відбувається повільно, оскільки цикл не вийде під час сну, але inotifywait вийде до часу очікування, якщо побачить подію.
yingted 25.03.15

1
@AlexDean Так, але це необхідно для запобігання стану перегонів TOCTTOU. В іншому випадку inotifywaitможе зависати необмежений час, якщо файл створюється безпосередньо перед тим, як він починає прослуховувати події.
гукав

4

У мене була та ж проблема, покладіть! поза дужками;

while ! [ -f /tmp/list.txt ];
do
    echo "#"
    sleep 1
done

Крім того, якщо ви додасте ехо всередину циклу, він скаже вам, потрапляєте ви в цикл чи ні.


2

Я зіткнувся з подібною проблемою, і це призвело мене сюди, тому я просто хотів залишити своє рішення для тих, хто відчуває те саме.

Я виявив, що якщо я запустив cat /tmp/list.txtфайл, він буде порожнім, хоча я був впевнений, що вміст відразу розміщується у файлі. Виявляється, якщо я поставив sleep 1;перед тим, як cat /tmp/list.txtце спрацювало, як очікувалося. Повинна бути затримка між часом створення файлу та часом його написання, або чимось подібним.

Мій остаточний код:

while [ ! -f /tmp/list.txt ];
do
    sleep 1;
done;
sleep 1;
cat /tmp/list.txt;

Сподіваюся, це допоможе врятувати когось неприємних півгодини!


1

Як і @ zane-hooper, у мене була подібна проблема на NFS. У паралельних / розподілених файлових системах затримка між створенням файлу на одній машині та іншою машиною, коли вона "бачить", може бути дуже великою, тому я міг би зачекати до цілої хвилини після створення файлу до виходу циклу while (і також є наслідком "побачення" вже видаленого файлу).

Це створює ілюзію, що сценарій "не працює" , хоча насправді саме файлова система скидає кульку.

Це зайняло деякий час, щоб зрозуміти, сподіваюся, це заощадить комусь час.

PS Це також викликає неприємну кількість помилок "Обробник застарілих файлів".


0

працює з bash і sh:

touch /tmp/testfile
sleep 10 && rm /tmp/testfile &
until ! [ -f /tmp/testfile ]
do
   echo "testfile still exist..."
   sleep 1
done
echo "now testfile is deleted.."

0

Ось версія з часом очікування, щоб через певний час цикл закінчувався помилкою:

# After 60 seconds the loop will exit
timeout=60

while [ ! -f /tmp/list.txt ];
do
  # When the timeout is equal to zero, show an error and leave the loop.
  if [ "$timeout" == 0 ]; then
    echo "ERROR: Timeout while waiting for the file /tmp/list.txt."
    exit 1
  fi

  sleep 1

  # Decrease the timeout of one
  ((timeout--))
done

-3

робити це так

while true
do
  [ -f /tmp/list.txt ] && break
  sleep 2
done
ls -l /tmp/list.txt
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.