З того, що я збираю, ви хочете перевірити це під час використання
tee -- "$OUT_FILE"
(зауважте, --
або це не працює для імен файлів, які починаються з -), tee
вдалося б відкрити файл для запису.
Це те, що:
- довжина шляху файлу не перевищує межу PATH_MAX
- файл існує (після роздільної здатності символьного посилання) і не є типом каталогу, і ви маєте на нього дозвіл на запис.
- якщо файл не існує, ім’я файлу існує (після роздільної здатності символьної посилання) як каталог, і ви маєте на нього дозвіл на запис та пошук, а довжина імені файлу не перевищує межу NAME_MAX файлової системи, в якій знаходиться каталог.
- або файл є символьним посиланням, яке вказує на файл, який не існує і не є циклом символьної посилання, але відповідає критеріям трохи вище
Зараз ми будемо ігнорувати такі файлові системи, як vfat, ntfs або hfsplus, які мають обмеження щодо того, які імена файлів значень байтів можуть містити, квоту диска, ліміт процесу, selinux, apparmor чи інший механізм захисту, таким чином, повна файлова система, не залишається вхід, пристрій файли, які з тієї чи іншої причини неможливо відкрити, файли, які виконуються в даний час, відображені в деякому адресному просторі процесу, і все це також може вплинути на можливість відкриття або створення файлу.
З zsh
:
zmodload zsh/system
tee_would_likely_succeed() {
local file=$1 ERRNO=0 LC_ALL=C
if [ -d "$file" ]; then
return 1 # directory
elif [ -w "$file" ]; then
return 0 # writable non-directory
elif [ -e "$file" ]; then
return 1 # exists, non-writable
elif [ "$errnos[ERRNO]" != ENOENT ]; then
return 1 # only ENOENT error can be recovered
else
local dir=$file:P:h base=$file:t
[ -d "$dir" ] && # directory
[ -w "$dir" ] && # writable
[ -x "$dir" ] && # and searchable
(($#base <= $(getconf -- NAME_MAX "$dir")))
return
fi
}
У bash
або будь-якій оболонці Борна просто замініть
zmodload zsh/system
tee_would_likely_succeed() {
<zsh-code>
}
з:
tee_would_likely_succeed() {
zsh -s -- "$@" << 'EOF'
zmodload zsh/system
<zsh-code>
EOF
}
Тут є zsh
специфічні особливості $ERRNO
(яка викриває код помилки останнього системного виклику) та $errnos[]
асоціативний масив для перекладу у відповідні стандартні імена макросів C. І $var:h
(від csh), і $var:P
(потребує zsh 5.3 або вище).
bash поки не має еквівалентних функцій.
$file:h
може бути замінений dir=$(dirname -- "$file"; echo .); dir=${dir%??}
, або з GNU dirname
: IFS= read -rd '' dir < <(dirname -z -- "$file")
.
Тому $errnos[ERRNO] == ENOENT
, що підхід може бути запущений ls -Ld
у файлі та перевірити, чи відповідає повідомлення про помилку ENOENT помилкою. Робити це надійно і портативно, хоч і складно.
Одним із підходів може бути:
msg_for_ENOENT=$(LC_ALL=C ls -d -- '/no such file' 2>&1)
msg_for_ENOENT=${msg_for_ENOENT##*:}
(За умови , що повідомлення про помилку закінчується з syserror()
перекладом , ENOENT
і що цей переклад не включати :
) , а потім, замість того [ -e "$file" ]
, зробіть наступне :
err=$(ls -Ld -- "$file" 2>&1)
І перевірте наявність ENOENT помилки за допомогою
case $err in
(*:"$msg_for_ENOENT") ...
esac
Ця $file:P
частина є найскладнішою для досягнення bash
, особливо на FreeBSD.
У FreeBSD є realpath
команда та readlink
команда, яка приймає -f
опцію, але їх не можна використовувати в тих випадках, коли файл є символьним посиланням, яке не вирішується. Це те саме з perl
's Cwd::realpath()
.
python
«И os.path.realpath()
дійсно здається, працюють аналогічно zsh
$file:P
, тому за умови , що принаймні одна версія python
встановлена , і що є python
команда , яка відноситься до одного з них, ви могли б зробити (що не дано на FreeBSD є):
dir=$(python -c '
import os, sys
print(os.path.realpath(sys.argv[1]) + ".")' "$dir") || return
dir=${dir%.}
Але тоді, ви можете також зробити все це в python
.
Або ви могли вирішити не займатися усіма цими кутовими справами.