Überprüfen Sie, ob das übergebene Argument eine Datei oder ein Verzeichnis in Bash ist

156

Ich versuche, ein extrem einfaches Skript in Ubuntu zu schreiben, das es mir ermöglicht, entweder einen Dateinamen oder ein Verzeichnis zu übergeben und etwas Bestimmtes zu tun, wenn es sich um eine Datei handelt, und etwas anderes, wenn es sich um ein Verzeichnis handelt. Das Problem, das ich habe, ist, wenn der Verzeichnisname oder wahrscheinlich auch Dateien Leerzeichen oder andere Zeichen enthalten.

Hier ist mein Basiscode unten und ein paar 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

Und hier ist die Ausgabe:

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

Alle diese Pfade sind gültig und existieren.

Andy
quelle
5
if- elseKonstrukte in Bash unterstützen ebenfalls elif. Nur zur Info.
3
@glenn - Seltsamerweise sind bei Variablenzuweisungen keine Anführungszeichen erforderlich.
John Kugelman

Antworten:

211

Das sollte funktionieren. Ich bin mir nicht sicher, warum es fehlschlägt. Sie zitieren Ihre Variablen richtig. Was passiert, wenn Sie dieses Skript mit double verwenden [[ ]]?

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

Doppelte eckige Klammern sind eine Bash-Erweiterung für [ ]. Variablen müssen nicht in Anführungszeichen gesetzt werden, auch wenn sie Leerzeichen enthalten.

Es lohnt sich auch zu versuchen: -ezu testen, ob ein Pfad vorhanden ist, ohne zu testen, um welchen Dateityp es sich handelt.

John Kugelman
quelle
9

Schreiben Sie zumindest den Code ohne den buschigen Baum:

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

Wenn ich das in eine Datei "xx.sh" lege und eine Datei "xx sh" erstelle und ausführe, erhalte ich:

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

Wenn Sie Probleme haben, sollten Sie das Skript debuggen, indem Sie Folgendes hinzufügen:

ls -l "${PASSED}"

Dies zeigt Ihnen, was Sie lsüber die Namen denken, an die Sie das Skript übergeben.

Jonathan Leffler
quelle
4

Die Verwendung des Befehls "Datei" kann hierfür hilfreich sein:

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

Ausgabebeispiele:

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

Zu Ihrer Information: Die obigen Antworten funktionieren, aber Sie können -s verwenden, um in seltsamen Situationen zu helfen, indem Sie zuerst nach einer gültigen Datei suchen:

#!/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.
quelle
Leere Dateien sind also nicht gültig (weil -snach einer nicht leeren Datei mit einer Größe ungleich Null gesucht wird)? Und Sie werden keine Diagnose für ein Block-Special, ein Character-Special, ein FIFO usw. drucken? Symlinks lösen sich wahrscheinlich auf, was sich am anderen Ende des Links befindet. defekte Symlinks sind problematischer.
Jonathan Leffler
Was schlagen Sie als Bearbeitung vor, ich folge Ihrem Kommentar nicht
Mike Q
Verwenden Sie die --briefFlagge von file. Es wird nur ausgegeben, directorywenn es ist.
Berkant
2

Verwenden -fund -dEinschalten /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
quelle
Im Allgemeinen sollten Sie sich nicht die Mühe machen, einer alten Frage eine neue Antwort hinzuzufügen, wenn bereits gleichwertige Antworten verfügbar sind. Wenn Sie einige erstaunliche neue Informationen hinzufügen möchten, aber alle Mittel eine Antwort geben. Aber was Sie gesagt haben, wurde bereits gesagt. ( testist normalerweise eine Shell eingebaut, obwohl es normalerweise auch eine ausführbare Datei wie /bin/testund gibt /bin/[.)
Jonathan Leffler
1

Eine elegantere Lösung

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
quelle
2
Das Auffordern von Eingaben anstelle von Befehlszeilenargumenten ist normalerweise nicht "eleganter". Sie sollten die Variable im Test angeben : if [ -f "$x" ].
Jonathan Leffler
1
function check_file_path(){
    [ -f "$1" ] && return
    [ -d "$1" ] && return
    return 1
}
check_file_path $path_or_file
张馆长
quelle
-1

Das sollte funktionieren:

#!/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
quelle
2
Können Sie bitte einige Erklärungen hinzufügen, wenn Sie eine Frage beantworten?
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
quelle
Es ist am besten, den Dateinamen in der Ausgabe wiederzugeben, wie es der Code in der Frage tut. Es ist nicht klar, dass die Aufforderung zur Eingabe eines Dateinamens eine Verbesserung darstellt. Der Code unterscheidet nicht zwischen Dateien, Verzeichnissen und "anderen". Es ist in Ordnung, eine neue Antwort auf eine alte Frage hinzuzufügen, wenn keine Antwort vorliegt oder Sie neue Informationen zu vermitteln haben. Das ist hier nicht der Fall.
Jonathan Leffler
-2

Einzeiler

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

Ergebnis ist wahr (0) (dir) oder wahr (0) (Datei) oder falsch (1) (weder)

Noelmcloughlin
quelle
Wenn ich ersetzen touch bobmit mkdir bob, erhalte ich die Ausgabe: dirund fileauf zwei Linien.
Jonathan Leffler