Почему этот скрипт не регистрирует последний файл?

Почему этот скрипт не регистрирует последний файл?

Этот скрипт иногда дает сбой, а иногда работает успешно (очень раздражающая ситуация):

#!/bin/bash
set -Eeuxo pipefail

test_dir="$(mktemp -d)"
touch "$test_dir/file"{001..312}
latest_file="$(find $test_dir -type f | sort -r | head -n1)"
echo $latest_file

В случае успеха он регистрирует что-то вроде этого:

++ mktemp -d
+ test_dir=/tmp/tmp.yWelcpBYB7
+ touch /tmp/tmp.yWelcpBYB7/file001 /tmp/tmp.yWelcpBYB7/file002 ... /tmp/tmp.yWelcpBYB7/file312
++ find /tmp/tmp.yWelcpBYB7 -type f
++ head -n1
++ sort -r
+ latest_file=/tmp/tmp.yWelcpBYB7/file312
+ echo /tmp/tmp.yWelcpBYB7/file312
/tmp/tmp.yWelcpBYB7/file312

В случае сбоя регистрируется что-то вроде этого:

++ mktemp -d
+ test_dir=/tmp/tmp.VzTqmgpZyG
+ touch /tmp/tmp.VzTqmgpZyG/file001 /tmp/tmp.VzTqmgpZyG/file002 ... /tmp/tmp.VzTqmgpZyG/file312
++ find /tmp/tmp.VzTqmgpZyG -type f
++ sort -r
++ head -n1
+ latest_file=/tmp/tmp.VzTqmgpZyG/file312

Обратите внимание, что echo $latest_fileлиниянетвыполняется здесь, хотя он отображается в xtrace

Я не могу добиться успешного запуска, если использую 10 000 файлов, поэтому я подозреваю, что это как-то связано сголоваостановканаходитьрано.

#!/bin/bash
set -Eeuxo pipefail

test_dir="$(mktemp -d)"
touch "$test_dir/file"{0000..9999}
latest_file="$(find $test_dir -type f | sort -r | head -n1)"
echo $latest_file

Если я подавляю остановку при ошибке (используянабор +е), это удается:

#!/bin/bash
set -Eeuxo pipefail

test_dir="$(mktemp -d)"
touch "$test_dir/file"{0000..9999}
set +e
latest_file="$(find $test_dir -type f | sort -r | head -n1)"
set -e
echo $latest_file

Почему этот скрипт не регистрирует последний файл?

решение1

Проблема в том, что -e. Почему? -eзаставляет bash прерывать работу в случае, если процесс завершается с ненулевым кодом выхода (полные правила немного сложнее). Если есть канал, учитывается только последняя команда.

Вы head -n1создаете внутреннюю ситуацию ошибки, поскольку вам приходится как бы разрывать канал (вы можете проверить это с помощью strace), чтобы игнорировать оставшуюся часть вывода от sort.

Итак, чтобы ваш скрипт работал надежно с -e, вы можете добавить catв конец конвейера. headвсе равно разорвет конвейер, но поскольку это уже не последняя команда в нем, она не будет учитываться -e. catявляется пустой операцией для конвейера:

#!/bin/bash
set -Eeuxo pipefail

test_dir="$(mktemp -d)"
touch "$test_dir/file"{0000..9999}
latest_file="$(find $test_dir -type f | sort -r | head -n1 | cat)"
echo $latest_file

пожалуйста, проверьтеПочему set -e (или set -o errexit, или trap ERR) не делает то, что я ожидал?узнать, почему -eтакая нестабильная функция и какие проблемы она может вызвать. В конце есть много примеров. Мой любимый:

#!/bin/bash

set -e
foo=$(expr 1 - 1)
echo survived

Это будетнетРаспечататьвыжил, эта строка не будет выполнена. Но, если бы у вас было foo=$(expr 2 - 1), то echoбыло бы выполнено!

Лучше бы вам реализовать собственную проверку ошибок, -eэто не лучшее решение.

Связанный контент