Проверьте, является ли переданный аргумент файлом или каталогом в Bash

156

Я пытаюсь написать чрезвычайно простой скрипт в Ubuntu, который позволил бы мне передать ему имя файла или каталог, и иметь возможность делать что-то конкретное, когда это файл, и что-то еще, когда это каталог. Проблема в том, что в имени каталога или, возможно, в файлах также есть пробелы или другие экранируемые символы в имени.

Ниже приведен мой основной код и пара тестов.

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

И вот вывод:

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

Все эти пути действительны и существуют.

Энди
источник
5
if- elseконструкции в Bash также поддерживают elif. Просто к вашему сведению.
3
@glenn - Любопытно, что кавычки не обязательны при назначении переменных.
Джон Кугельман

Ответы:

211

Это должно работать. Я не уверен, почему это терпит неудачу. Вы правильно цитируете свои переменные. Что произойдет, если вы используете этот скрипт с двойным [[ ]]?

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

Двойные квадратные скобки - это расширение bash для [ ]. Это не требует переменных в кавычках, даже если они содержат пробелы.

Также стоит попробовать: -eпроверить, существует ли путь, не проверяя тип файла.

Джон Кугельман
источник
9

По крайней мере, напишите код без густого дерева:

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

Когда я помещаю это в файл "xx.sh", создаю файл "xx sh" и запускаю его, я получаю:

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

Учитывая, что у вас возникли проблемы, вы должны отладить скрипт, добавив:

ls -l "${PASSED}"

Это покажет вам, что lsдумает об именах, которые вы передаете сценарию.

Джонатан Леффлер
источник
4

Использование команды «file» может быть полезно для этого:

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

Примеры вывода:

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

К вашему сведению: приведенные выше ответы работают, но вы можете использовать -s, чтобы помочь в странных ситуациях, проверив сначала действительный файл:

#!/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}
Майк Q
источник
Итак, пустые файлы недействительны (потому что -sпроверяет непустой файл, файл с ненулевым размером)? И вы не будете печатать какие-либо диагнозы для специальных блоков, специальных символов, FIFO и т. Д.? Симлинки, вероятно, разрешают то, что находится в дальнем конце ссылки; сломанные символические ссылки более проблематичны.
Джонатан Леффлер
Что вы предлагаете в качестве редактирования, я не слежу за вашим комментарием
Mike Q
Используйте --briefфлаг file. Это просто выводит, directoryкогда это так.
Беркант Ипек
2

Использование -fи -dвключает /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
Камран
источник
Вообще говоря, вам не следует добавлять новый ответ на старый вопрос, когда уже есть эквивалентные ответы. Если у вас есть какая-то поразительная новая информация, которую нужно добавить, но все средства дают ответ. Но то, что вы сказали, уже сказано. ( testобычно это встроенная оболочка, хотя обычно есть также исполняемый файл, такой как /bin/test, а также /bin/[.)
Джонатан Леффлер
1

Более элегантное решение

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
Запрос на ввод данных вместо использования аргументов командной строки обычно не является «более элегантным». Вы должны процитировать переменную в тесте: if [ -f "$x" ].
Джонатан Леффлер
1
function check_file_path(){
    [ -f "$1" ] && return
    [ -d "$1" ] && return
    return 1
}
check_file_path $path_or_file
张馆长
источник
-1

Это должно работать:

#!/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
источник
2
Пожалуйста, можете ли вы добавить некоторые объяснения, когда отвечаете на вопрос?
Себастьян Темпрадо
-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
источник
Лучше всего повторить имя файла в выводе, как и код в вопросе. Не ясно, что запрос имени файла является улучшением. Код не различает файлы, каталоги и «другое». Можно добавить новый ответ на старый вопрос, если ответа нет, или у вас есть новая информация для передачи. Это не тот случай, здесь.
Джонатан Леффлер
-2

Один лайнер

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

результат равен true (0) (dir) или true (0) (файл) или false (1) (ни один)

noelmcloughlin
источник
Если я заменю touch bobна mkdir bob, я получаю вывод: dirи fileна две строки.
Джонатан Леффлер