Vérifiez si l'argument passé est un fichier ou un répertoire dans Bash

156

J'essaie d'écrire un script extrêmement simple dans Ubuntu qui me permettrait de lui passer un nom de fichier ou un répertoire, et être capable de faire quelque chose de spécifique quand c'est un fichier, et autre chose quand c'est un répertoire. Le problème que je rencontre est lorsque le nom du répertoire, ou probablement des fichiers aussi, a des espaces ou d'autres caractères échappables sont dans le nom.

Voici mon code de base ci-dessous et quelques tests.

#!/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

Et voici la sortie:

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

Tous ces chemins sont valides et existent.

Andy
la source
5
if- Les elseconstructions dans Bash prennent également en charge elif. Juste FYI.
3
@glenn - Curieusement, les guillemets ne sont pas nécessaires dans les affectations de variables.
John Kugelman

Réponses:

211

Cela devrait fonctionner. Je ne sais pas pourquoi cela échoue. Vous citez correctement vos variables. Que se passe-t-il si vous utilisez ce script avec double [[ ]]?

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

Les doubles crochets sont une extension bash de [ ]. Il n'est pas nécessaire que les variables soient entre guillemets, même si elles contiennent des espaces.

Il vaut également la peine d'essayer: -epour tester si un chemin existe sans tester de quel type de fichier il s'agit.

John Kugelman
la source
9

Au moins écrivez le code sans l'arbre touffu:

#!/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

Lorsque je mets cela dans un fichier "xx.sh" et que je crée un fichier "xx sh" et que je l'exécute, j'obtiens:

$ 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
$

Étant donné que vous rencontrez des problèmes, vous devez déboguer le script en ajoutant:

ls -l "${PASSED}"

Cela vous montrera ce lsque vous pensez des noms que vous passez le script.

Jonathan Leffler
la source
4

L'utilisation de la commande "fichier" peut être utile pour cela:

#!/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}"

Exemples de sortie:

$ ./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) 

Pour info: les réponses ci-dessus fonctionnent, mais vous pouvez utiliser -s pour aider dans des situations étranges en vérifiant d'abord un fichier valide:

#!/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
la source
Donc, les fichiers vides ne sont pas valides (parce que -svérifie un fichier non vide, un avec une taille non nulle)? Et vous n'imprimerez aucun diagnostic pour un bloc spécial, un caractère spécial, un FIFO, etc.? Les liens symboliques se résolvent probablement à ce qui se trouve à l'extrémité du lien; les liens symboliques rompus sont plus problématiques.
Jonathan Leffler
Que proposez-vous comme montage, je ne suis pas votre commentaire
Mike Q
Utilisez le --briefdrapeau de file. Il sort simplement directoryquand c'est le cas.
Berkant Ipek
2

Utilisation -fet mise en -dmarche /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
la source
De manière générale, vous ne devriez pas prendre la peine d'ajouter une nouvelle réponse à une ancienne question alors que des réponses équivalentes sont déjà disponibles. Si vous avez de nouvelles informations surprenantes à ajouter, mais que vous avez tous les moyens de donner une réponse. Mais ce que vous avez dit a déjà été dit. ( testest normalement un shell intégré, bien qu'il y ait généralement aussi un exécutable tel que /bin/test, et aussi /bin/[.)
Jonathan Leffler
1

Une solution plus élégante

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
la source
2
Demander des entrées au lieu d'utiliser des arguments de ligne de commande n'est généralement pas "plus élégant". Vous devez citer la variable dans le test: if [ -f "$x" ].
Jonathan Leffler
1
function check_file_path(){
    [ -f "$1" ] && return
    [ -d "$1" ] && return
    return 1
}
check_file_path $path_or_file
张馆长
la source
-1

Cela devrait fonctionner:

#!/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
la source
2
Pouvez-vous ajouter quelques explications lorsque vous répondez à une question?
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
la source
Il est préférable de faire écho au nom du fichier dans la sortie, comme le fait le code de la question. Il n'est pas clair que demander un nom de fichier soit une amélioration. Le code ne fait pas la distinction entre les fichiers, les répertoires et «autres». Vous pouvez ajouter une nouvelle réponse à une ancienne question s'il n'y a pas de réponse ou si vous avez de nouvelles informations à communiquer. Ce n'est pas le cas ici.
Jonathan Leffler
-2

Bon mot

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

le résultat est vrai (0) (dir) ou vrai (0) (fichier) ou faux (1) (ni l'un ni l'autre)

noelmcloughlin
la source
Si je remplace touch bobpar mkdir bob, j'obtiens la sortie: diret filesur deux lignes.
Jonathan Leffler