Controlla se l'argomento passato è file o directory in Bash


156

Sto cercando di scrivere uno script estremamente semplice in Ubuntu che mi consenta di passargli un nome file o una directory, e di essere in grado di fare qualcosa di specifico quando si tratta di un file e qualcos'altro quando è una directory. Il problema che sto riscontrando è quando il nome della directory, o probabilmente anche i file, contiene spazi o altri caratteri di escape nel nome.

Ecco il mio codice di base in basso e un paio di test.

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

Ed ecco l'output:

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

Tutti questi percorsi sono validi ed esistono.


5
if- elsesupportano anche i costrutti in Bash elif. Cordiali saluti.

3
@glenn - Curiosamente, le assegnazioni delle variabili non richiedono virgolette.
John Kugelman,

Risposte:


211

Dovrebbe funzionare. Non sono sicuro del motivo per cui sta fallendo. Stai citando correttamente le tue variabili. Cosa succede se usi questo script con 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

Le parentesi quadre doppie sono un'estensione bash a [ ]. Non richiede che le variabili siano quotate, anche se contengono spazi.

Vale anche la pena provare: -eper verificare se esiste un percorso senza testare il tipo di file.


9

Almeno scrivi il codice senza l'albero folto:

#!/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 lo inserisco in un file "xx.sh" e creo un file "xx sh", ed eseguo, ottengo:

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

Dato che stai riscontrando problemi, dovresti eseguire il debug dello script aggiungendo:

ls -l "${PASSED}"

Questo ti mostrerà cosa lspensa dei nomi che passi allo script.


4

L'uso del comando "file" può essere utile per questo:

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

Esempi di output:

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

Cordiali saluti: le risposte sopra funzionano ma puoi usare -s per aiutarti in situazioni strane controllando prima un file valido:

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

Quindi, i file vuoti non sono validi (perché -scontrolla un file non vuoto, uno con una dimensione diversa da zero)? E non stamperai alcuna diagnosi per un blocco speciale, un personaggio speciale, FIFO, ecc.? I collegamenti simbolici probabilmente si risolvono in ciò che si trova all'estremità del collegamento; i collegamenti simbolici interrotti sono più problematici.
Jonathan Leffler,

Cosa suggerisci come modifica, non sto seguendo il tuo commento
Mike Q,

Usa la --briefbandiera di file. Emette semplicemente directoryquando lo è.
Berkant Ipek,

2

Utilizzo -fe -daccensione /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

In generale, non dovresti preoccuparti di aggiungere una nuova risposta a una vecchia domanda quando sono già disponibili risposte equivalenti. Se hai nuove sorprendenti informazioni da aggiungere, ma tutti i mezzi danno una risposta. Ma quello che hai detto è già stato detto. ( testè normalmente una shell integrata, anche se di solito esiste anche un eseguibile come /bin/test, e anche /bin/[.)
Jonathan Leffler

1

Una soluzione più 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

2
La richiesta di input invece di utilizzare gli argomenti della riga di comando non è in genere "più elegante". Si dovrebbe citare la variabile nel 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

-1

Questo dovrebbe funzionare:

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

2
Per favore, puoi aggiungere alcune spiegazioni quando rispondi a una domanda?
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 

È meglio fare eco al nome del file nell'output, come fa il codice nella domanda. Non è chiaro che richiedere un nome file sia un miglioramento. Il codice non distingue tra file, directory e "altro". Va bene aggiungere una nuova risposta a una vecchia domanda se non ci sono risposte o se si hanno nuove informazioni da comunicare. Questo non è il caso qui.
Jonathan Leffler,

-2

Una fodera

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

il risultato è true (0) (dir) o true (0) (file) o false (1) (nessuno dei due)


Se lo sostituisco touch bobcon mkdir bob, ottengo l'output: dire filesu due righe.
Jonathan Leffler,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.