Compruebe si el argumento pasado es un archivo o directorio en Bash

156

Estoy tratando de escribir un script extremadamente simple en Ubuntu que me permita pasarlo ya sea un nombre de archivo o un directorio, y poder hacer algo específico cuando es un archivo, y algo más cuando es un directorio. El problema que tengo es cuando el nombre del directorio, o probablemente también los archivos, tiene espacios u otros caracteres escapables están en el nombre.

Aquí está mi código básico a continuación, y un par de pruebas.

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

Y aquí está el resultado:

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 esos caminos son válidos y existen.

Andy
fuente
55
if- Las elseconstrucciones en Bash también son compatibles elif. Solo para tu información.
3
@glenn - Curiosamente, las comillas no requieren comillas.
John Kugelman el

Respuestas:

211

Eso debería funcionar. No estoy seguro de por qué está fallando. Estás citando tus variables correctamente. ¿Qué sucede si usa este script con doble [[ ]]?

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

Los corchetes dobles son una extensión de bash para [ ]. No requiere que se citen variables, ni siquiera si contienen espacios.

También vale la pena intentarlo: -epara probar si existe una ruta sin probar qué tipo de archivo es.

John Kugelman
fuente
9

Al menos escribe el código sin el árbol tupido:

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

Cuando pongo eso en un archivo "xx.sh" y creo un archivo "xx sh", y lo ejecuto, obtengo:

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

Dado que tiene problemas, debe depurar el script agregando:

ls -l "${PASSED}"

Esto le mostrará lo que lspiensa sobre los nombres que le pasa al script.

Jonathan Leffler
fuente
4

Usar el comando "archivo" puede ser útil para esto:

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

Ejemplos de salida:

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

FYI: las respuestas anteriores funcionan pero puedes usar -s para ayudar en situaciones extrañas al buscar primero un archivo 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
fuente
Entonces, ¿los archivos vacíos no son válidos (porque -sbusca un archivo no vacío, uno con un tamaño distinto de cero)? ¿Y no imprimirá ningún diagnóstico para un bloque especial, carácter especial, FIFO, etc.? Los enlaces simbólicos probablemente resuelven lo que está en el otro extremo del enlace; los enlaces simbólicos rotos son más problemáticos.
Jonathan Leffler
¿Qué sugieres como edición? No sigo tu comentario
Mike Q
Usa la --briefbandera de file. Simplemente sale directorycuando es.
Berkant Ipek
2

Uso -fy -dencendido /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
fuente
En términos generales, no debería molestarse en agregar una nueva respuesta a una pregunta anterior cuando ya hay respuestas equivalentes disponibles. Si tiene alguna información nueva sorprendente para agregar, pero todos los medios den una respuesta. Pero lo que has dicho ya se ha dicho. ( testnormalmente es un shell incorporado, aunque generalmente también hay un ejecutable como /bin/test, y también /bin/[.)
Jonathan Leffler
1

Una solución más 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
fuente
2
Solicitar entradas en lugar de usar argumentos de línea de comandos no suele ser "más elegante". Usted debe citar la variable en la prueba: if [ -f "$x" ].
Jonathan Leffler
1
function check_file_path(){
    [ -f "$1" ] && return
    [ -d "$1" ] && return
    return 1
}
check_file_path $path_or_file
张馆长
fuente
-1

Esto debería 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
usuario11791326
fuente
2
Por favor, ¿puedes agregar algunas explicaciones cuando respondas una pregunta?
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
fuente
Es mejor hacer eco del nombre del archivo en la salida, como lo hace el código en la pregunta. No está claro que solicitar un nombre de archivo sea una mejora. El código no distingue entre archivos, directorios y 'otros'. Está bien agregar una nueva respuesta a una pregunta anterior si no hay respuesta o si tiene nueva información para impartir. Ese no es el caso aquí.
Jonathan Leffler
-2

Un trazador de líneas

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

el resultado es verdadero (0) (dir) o verdadero (0) (archivo) o falso (1) (ninguno)

noelmcloughlin
fuente
Si reemplazo touch bobcon mkdir bob, obtengo el resultado: diry fileen dos líneas.
Jonathan Leffler