Pelo que estou coletando, você quer verificar isso ao usar
tee -- "$OUT_FILE"
(observe o --
ou não funcionaria para nomes de arquivos que começam com -), tee
teria sucesso em abrir o arquivo para gravação.
É isso:
- o comprimento do caminho do arquivo não excede o limite de PATH_MAX
- o arquivo existe (depois da resolução do symlink) e não é do tipo diretório e você tem permissão de escrita para ele.
- se o arquivo não existir, o dirname do arquivo existe (após a resolução do symlink) como um diretório e você tem permissão de gravação e pesquisa nele e o tamanho do nome do arquivo não excede o limite NAME_MAX do sistema de arquivos desse diretório reside em.
- ou o arquivo é um link simbólico que aponta para um arquivo que não existe e não é um loop de link simbólico, mas atende aos critérios acima
Ignoraremos por enquanto sistemas de arquivos como vfat, ntfs ou hfsplus que têm limitações sobre quais valores de bytes podem conter, cota de disco, limite de processo, selinux, apparmor ou outro mecanismo de segurança no caminho, sistema de arquivos completo, sem inode à esquerda, arquivos de dispositivo que não podem ser abertos dessa maneira por um motivo ou outro, arquivos executáveis atualmente mapeados em algum espaço de endereço de processo, e que também podem afetar a capacidade de abrir ou criar o arquivo.
com 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
}
Em bash
ou qualquer shell parecido com o Bourne, basta substituir o
zmodload zsh/system
tee_would_likely_succeed() {
<zsh-code>
}
com:
tee_would_likely_succeed() {
zsh -s -- "$@" << 'EOF'
zmodload zsh/system
<zsh-code>
EOF
}
Os recursos zsh
-específicos aqui são $ERRNO
(que expõe o código de erro da última chamada de sistema) e $errnos[]
matriz associativa para traduzir para os nomes de macro C padrão correspondentes. E o $var:h
(do csh) e $var:P
(precisa do zsh 5.3 ou superior).
O
bash ainda não possui recursos equivalentes.
$file:h
pode ser substituído por dir=$(dirname -- "$file"; echo .); dir=${dir%??}
ou com GNU dirname
: IFS= read -rd '' dir < <(dirname -z -- "$file")
.
Para $errnos[ERRNO] == ENOENT
, uma abordagem poderia ser executar ls -Ld
no arquivo e verificar se a mensagem de erro corresponde ao erro ENOENT. Fazer isso de maneira confiável e portável é complicado.
Uma abordagem poderia ser:
msg_for_ENOENT=$(LC_ALL=C ls -d -- '/no such file' 2>&1)
msg_for_ENOENT=${msg_for_ENOENT##*:}
(supondo que a mensagem de erro termine com a syserror()
tradução de ENOENT
e que essa tradução não inclua :
) e, em vez de [ -e "$file" ]
, faça :
err=$(ls -Ld -- "$file" 2>&1)
E verifique se há um erro ENOENT com
case $err in
(*:"$msg_for_ENOENT") ...
esac
A parte $file:P
é a mais difícil de conseguir em bash
, especialmente no FreeBSD.
O FreeBSD tem um comando realpath
e um comando readlink
que aceita uma opção -f
, mas eles não podem ser usados nos casos em que o arquivo é um link simbólico que não resolve. É o mesmo com perl
' Cwd::realpath()
.
python
os.path.realpath()
parece funcionar de forma semelhante a zsh
$file:P
, portanto, supondo que pelo menos uma versão de python
esteja instalada e que exista um comando python
que se refere a um deles (o que não é um dado no FreeBSD), você poderia fazer:
dir=$(python -c '
import os, sys
print(os.path.realpath(sys.argv[1]) + ".")' "$dir") || return
dir=${dir%.}
Mas você pode fazer tudo em python
.
Ou você pode decidir não lidar com todos esses casos.