Sprawdź, czy przekazany argument to plik lub katalog w Bash

156

Próbuję napisać niezwykle prosty skrypt w Ubuntu, który pozwoliłby mi przekazać mu nazwę pliku lub katalogu i móc zrobić coś konkretnego, gdy jest to plik, i coś innego, gdy jest to katalog. Problem, który mam, polega na tym, że nazwa katalogu lub prawdopodobnie również pliki zawiera spacje lub inne znaki ucieczki w nazwie.

Oto mój podstawowy kod poniżej i kilka testów.

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

A oto wynik:

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

Wszystkie te ścieżki są prawidłowe i istnieją.

Andy
źródło
5
if- elsekonstrukcje w Bash również obsługują elif. Po prostu FYI.
3
@glenn - Co ciekawe, cudzysłowy nie są wymagane w przypisaniach zmiennych.
John Kugelman,
Możliwy duplikat polecenia Sprawdź, czy katalog istnieje w skrypcie powłoki .
jww

Odpowiedzi:

211

To powinno działać. Nie jestem pewien, dlaczego to zawodzi. Prawidłowo cytujesz swoje zmienne. Co się stanie, jeśli użyjesz tego skryptu z 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

Podwójne nawiasy kwadratowe to rozszerzenie bash do [ ]. Nie wymaga cytowania zmiennych, nawet jeśli zawierają spacje.

Warto również spróbować: -esprawdzić, czy ścieżka istnieje, bez sprawdzania, jakiego typu jest to plik.

John Kugelman
źródło
9

Przynajmniej napisz kod bez krzaczastego drzewa:

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

Kiedy umieszczam to w pliku „xx.sh”, tworzę plik „xx sh” i uruchamiam go, otrzymuję:

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

Biorąc pod uwagę, że masz problemy, powinieneś debugować skrypt, dodając:

ls -l "${PASSED}"

To pokaże ci, co lssądzi o nazwach, które przekazujesz skryptowi.

Jonathan Leffler
źródło
4

Użycie polecenia „plik” może być do tego przydatne:

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

Przykłady wyników:

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

Do Twojej wiadomości: powyższe odpowiedzi działają, ale możesz użyć -s, aby pomóc w dziwnych sytuacjach, sprawdzając najpierw prawidłowy plik:

#!/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
źródło
Czyli puste pliki są niepoprawne (ponieważ -sszuka niepustego pliku, takiego o rozmiarze niezerowym)? I nie wydrukujesz żadnej diagnozy dla bloku specjalnego, specjalnego znaku, FIFO itp.? Dowiązania symboliczne prawdopodobnie prowadzą do tego, co znajduje się na drugim końcu łącza; uszkodzone linki symboliczne są bardziej problematyczne.
Jonathan Leffler
Co sugerujesz jako zmianę, nie śledzę twojego komentarza
Mike Q
Użyj --briefflagi file. Wyprowadza tylko directorywtedy, gdy jest.
Berkant Ipek
2

Używanie -fi -dwłączanie /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
źródło
Ogólnie rzecz biorąc, nie powinieneś zawracać sobie głowy dodawaniem nowej odpowiedzi do starego pytania, gdy są już dostępne równoważne odpowiedzi. Jeśli masz jakieś zaskakujące nowe informacje do dodania, ale wszystkie środki, odpowiedz. Ale to, co powiedziałeś, zostało już powiedziane. ( testZwykle powłoka wbudowany, choć zwykle jest plikiem wykonywalnym, takich jak /bin/test, a także /bin/[.)
Jonathan Leffler
1

Bardziej eleganckie rozwiązanie

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
źródło
2
Pytanie o dane wejściowe zamiast używania argumentów wiersza poleceń zwykle nie jest „bardziej eleganckie”. Należy podać zmienną w teście: if [ -f "$x" ].
Jonathan Leffler
1
function check_file_path(){
    [ -f "$1" ] && return
    [ -d "$1" ] && return
    return 1
}
check_file_path $path_or_file
张馆长
źródło
-1

To powinno działać:

#!/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
źródło
2
Czy możesz dodać wyjaśnienia do odpowiedzi na pytanie?
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
źródło
Najlepiej jest powtórzyć nazwę pliku w danych wyjściowych, tak jak robi to kod w pytaniu. Nie jest jasne, czy monit o podanie nazwy pliku jest poprawą. Kod nie rozróżnia plików, katalogów i „innych”. Możesz dodać nową odpowiedź do starego pytania, jeśli nie ma odpowiedzi lub masz nowe informacje do przekazania. Tak nie jest w tym przypadku.
Jonathan Leffler
-2

Jedna wkładka

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

wynik to prawda (0) (katalog) lub prawda (0) (plik) lub fałsz (1) (żadne)

noelmcloughlin
źródło
Jeśli mogę wymienić touch bobz mkdir bob, mam wyjścia: dira filena dwóch liniach.
Jonathan Leffler