Verifique se o argumento passado é um arquivo ou diretório no Bash

156

Eu estou tentando escrever um script extremamente simples no Ubuntu que me permita passar um nome de arquivo ou um diretório e ser capaz de fazer algo específico quando é um arquivo e outra coisa quando é um diretório. O problema que estou tendo é quando o nome do diretório, ou provavelmente arquivos também tem espaços ou outros caracteres escapable estão no nome.

Aqui está o meu código básico abaixo e alguns testes.

#!/bin/bash

PASSED=$1

if [ -d "${PASSED}" ] ; then
    echo "$PASSED is a directory";
else
    if [ -f "${PASSED}" ]; then
        echo "${PASSED} is a file";
    else
        echo "${PASSED} is not valid";
        exit 1
    fi
fi

E aqui está a saída:

andy@server~ $ ./scripts/testmove.sh /home/andy/
/home/andy/ is a directory

andy@server~ $ ./scripts/testmove.sh /home/andy/blah.txt
/home/andy/blah.txt is a file

andy@server~ $ ./scripts/testmove.sh /home/andy/blah\ with\ a\ space.txt
/home/andy/blah with a space.txt is not valid

andy@server~ $ ./scripts/testmove.sh /home/andy\ with\ a\ space/
/home/andy with a space/ is not valid

Todos esses caminhos são válidos e existem.

Andy
fonte
5
if- elseconstruções em Bash também suportam elif. Apenas para sua informação.
3
@glenn - Curiosamente, aspas não são necessárias em atribuições de variáveis.
John Kugelman

Respostas:

211

Isso deve funcionar. Não sei por que está falhando. Você está citando suas variáveis ​​corretamente. O que acontece se você usar esse script com o dobro [[ ]]?

if [[ -d $PASSED ]]; then
    echo "$PASSED is a directory"
elif [[ -f $PASSED ]]; then
    echo "$PASSED is a file"
else
    echo "$PASSED is not valid"
    exit 1
fi

Os colchetes duplos são uma extensão do bash para [ ]. Não requer que as variáveis ​​sejam citadas, nem mesmo se elas contiverem espaços.

Também vale a pena tentar: -etestar se existe um caminho sem testar que tipo de arquivo é.

John Kugelman
fonte
9

Pelo menos escreva o código sem a árvore espessa:

#!/bin/bash

PASSED=$1

if   [ -d "${PASSED}" ]
then echo "${PASSED} is a directory";
elif [ -f "${PASSED}" ]
then echo "${PASSED} is a file";
else echo "${PASSED} is not valid";
     exit 1
fi

Quando coloco isso em um arquivo "xx.sh" e crio um arquivo "xx sh" e o executo, recebo:

$ cp /dev/null "xx sh"
$ for file in . xx*; do sh "$file"; done
. is a directory
xx sh is a file
xx.sh is a file
$

Como você está tendo problemas, você deve depurar o script adicionando:

ls -l "${PASSED}"

Isso mostrará o que você lspensa sobre os nomes que você passa no script.

Jonathan Leffler
fonte
4

O uso do comando "arquivo" pode ser útil para isso:

#!/bin/bash
check_file(){

if [ -z "${1}" ] ;then
 echo "Please input something"
 return;
fi

f="${1}"
result="$(file $f)"
if [[ $result == *"cannot open"* ]] ;then
        echo "NO FILE FOUND ($result) ";
elif [[ $result == *"directory"* ]] ;then
        echo "DIRECTORY FOUND ($result) ";
else
        echo "FILE FOUND ($result) ";
fi

}

check_file "${1}"

Exemplos de saída:

$ ./f.bash login
DIRECTORY FOUND (login: directory) 
$ ./f.bash ldasdas
NO FILE FOUND (ldasdas: cannot open `ldasdas' (No such file or  directory)) 
$ ./f.bash evil.php 
FILE FOUND (evil.php: PHP script, ASCII text) 

Para sua informação: as respostas acima funcionam, mas você pode usar -s para ajudar em situações estranhas, verificando primeiro um arquivo válido:

#!/bin/bash

check_file(){
    local file="${1}"
    [[ -s "${file}" ]] || { echo "is not valid"; return; } 
    [[ -d "${file}" ]] && { echo "is a directory"; return; }
    [[ -f "${file}" ]] && { echo "is a file"; return; }
}

check_file ${1}
Mike Q
fonte
Portanto, os arquivos vazios não são válidos (porque -sverifica um arquivo não vazio, um com tamanho diferente de zero)? E você não imprimirá nenhum diagnóstico para um bloco especial, caractere especial, FIFO, etc? Os links simbólicos provavelmente resolvem o que está no outro extremo do link; links simbólicos quebrados são mais problemáticos.
Jonathan Leffler
O que você sugere como uma edição, eu não estou seguindo o seu comentário
Mike Q
Use a --briefbandeira de file. Apenas produz directoryquando é.
Berkant Ipek 13/07/19
2

Usando -fe -dativando /bin/test:

F_NAME="${1}"

if test -f "${F_NAME}"
then                                   
   echo "${F_NAME} is a file"
elif test -d "${F_NAME}"
then
   echo "${F_NAME} is a directory"
else                                   
   echo "${F_NAME} is not valid"
fi
Kamran
fonte
De um modo geral, você não deve adicionar uma nova resposta a uma pergunta antiga quando já houver respostas equivalentes disponíveis. Se você tem algumas novas informações surpreendentes para adicionar, mas todos os meios dão uma resposta. Mas o que você disse já foi dito. ( testÉ normalmente um invólucro interno, embora haja geralmente também é um executável, tais como /bin/test, e também /bin/[.)
Jonathan Leffler
1

Uma solução mais elegante

echo "Enter the file name"
read x
if [ -f $x ]
then
    echo "This is a regular file"
else
    echo "This is a directory"
fi
Jaison John
fonte
2
Solicitar entradas em vez de usar argumentos de linha de comando geralmente não é "mais elegante". Você deve citar a variável no teste: if [ -f "$x" ].
Jonathan Leffler
1
function check_file_path(){
    [ -f "$1" ] && return
    [ -d "$1" ] && return
    return 1
}
check_file_path $path_or_file
张馆长
fonte
-1

Isso deve funcionar:

#!/bin/bash

echo "Enter your Path:"
read a

if [[ -d $a ]]; then 
    echo "$a is a Dir" 
elif [[ -f $a ]]; then 
    echo "$a is the File" 
else 
    echo "Invalid path" 
fi
user11791326
fonte
2
Por favor, você pode adicionar algumas explicações quando responder a uma pergunta?
Sébastien Temprado
-2
#!/bin/bash                                                                                               
echo "Please Enter a file name :"                                                                          
read filename                                                                                             
if test -f $filename                                                                                      
then                                                                                                      
        echo "this is a file"                                                                             
else                                                                                                      
        echo "this is not a file"                                                                         
fi 
muj
fonte
É melhor repetir o nome do arquivo na saída, como o código da pergunta. Não está claro que solicitar um nome de arquivo seja uma melhoria. O código não faz distinção entre arquivos, diretórios e 'outros'. Não há problema em adicionar uma nova resposta a uma pergunta antiga, se não houver resposta, ou se você tiver novas informações a transmitir. Esse não é o caso aqui.
Jonathan Leffler
-2

Um forro

touch bob; test -d bob && echo 'dir' || (test -f bob && echo 'file')

o resultado é verdadeiro (0) (dir) ou verdadeiro (0) (arquivo) ou falso (1) (nenhum)

noelmcloughlin
fonte
Se substituir touch bobpor mkdir bob, recebo a saída: dire fileem duas linhas.
Jonathan Leffler