检查文件是否存在 [BASH]

3

如何在bash中检查文件是否存在?

当我尝试像这样做时:

FILE1="${@:$OPTIND:1}"
if [ ! -e "$FILE1" ]
then 
    echo "requested file doesn't exist" >&2
    exit 1
elif
<more code follows>

我经常得到以下输出:
 requested file doesn't exist

这个程序的使用方法如下:
script.sh [-g] [-p] [-r FUNCTION_ID|-d FUNCTION_ID] FILE

请问有什么想法吗?非常感谢您的帮助。

顺便说一句,我希望能够展示整个文件,但又担心因此被学校开除。如果有私人通信的方式,我会很乐意配合。

我的错误,我把二进制文件强行放到了错误的位置。感谢大家的帮助。


不,我看到了一个空白的空间。 - user3473750
文件 "" 不存在。 - loentar
当我执行ls命令时,我可以清楚地看到它。当我执行ls命令时,我可以清楚地看到它。 - user3473750
空名称的文件是不存在的。请检查 FILE1="${@:$OPTIND:1} 是否正确。 - loentar
当你执行 ls 命令时,你能清楚地看到什么?问题在于你期望 "${@:$OPTIND:1}" 包含一个有效的文件名,但实际上它包含了一个空字符串(或未设置)。显然,文件名为空字符串的文件是不存在的。你需要弄清楚为什么 "${@:$OPTIND:1}" 没有包含你期望的值,并且你没有给出足够的信息来帮助我们解决这个问题。 - Keith Thompson
显示剩余3条评论
4个回答

6

对于这类问题,有一个小技巧可以帮助调试。请在脚本顶部添加以下代码:

export PS4="\$LINENO: "
set -xv
set -xv会在每行执行之前打印出该行内容,然后在shell插值变量等操作后再次打印该行内容。$PS4是由set -xv使用的提示符。这将打印出shell脚本执行的行号。您将能够跟踪发生了什么以及可能存在的问题。
以下是一个测试脚本示例:
#! /bin/bash

export PS4="\$LINENO: "
set -xv

FILE1="${@:$OPTIND:1}"         # Line 6
if [ ! -e "$FILE1" ]           # Line 7
then 
    echo "requested file doesn't exist" >&2
    exit 1
else
    echo "Found File $FILE1"   # Line 12
fi

当我运行它时,我得到了以下结果:

$ ./test.sh .profile
    FILE1="${@:$OPTIND:1}"
6: FILE1=.profile
    if [ ! -e "$FILE1" ]
    then 
        echo "requested file doesn't exist" >&2
        exit 1
    else
        echo "Found File $FILE1"
    fi
7: [ ! -e .profile ]
12: echo 'Found File .profile'
Found File .profile

在这里,我可以看到我将$FILE1设置为.profile,而我的脚本理解了${@:$OPTIND:1}。最好的是,它适用于所有的Shell,甚至是最初的Bourne Shell。这意味着,如果你认为你正在运行Bash,但实际上不是,你就会看到你的脚本失败的地方,并且可能会修复问题。
我怀疑你可能没有在Bash中运行你的脚本。你是否在顶部放置了#! /bin/bash

script.sh [-g] [-p] [-r FUNCTION_ID|-d FUNCTION_ID] FILE

你可能想使用getopts来解析你的参数:
#! /bin/bash

USAGE=" Usage:
    script.sh [-g] [-p] [-r FUNCTION_ID|-d FUNCTION_ID] FILE
"
while getopts gpr:d: option
do
    case $option in
        g)  g_opt=1;;
        p)  p_opt=1;;
        r)  rfunction_id="$OPTARG";;
        d)  dfunction_id="$OPTARG";;
        [?])
            echo "Invalid Usage" 1>&2
            echo "$USAGE"        1>&2
            exit 2
            ;;
    esac
done

if [[ -n $rfunction_id && -n $dfunction_id ]]
then
    echo "Invalid Usage: You can't specify both -r and -d" 1>&2
    echo "$USAGE" >2&
    exit 2
fi

shift $(($OPTIND - 1))
[[ -n $g_opt ]] && echo "-g was set"
[[ -n $p_opt ]] && echo "-p was set"
[[ -n $rfunction_id ]] && echo "-r was set to $rfunction_id"
[[ -n $dfunction_id ]] && echo "-d was set to $dfunction_id"
[[ -n $1 ]] && echo "File is $1"

我在顶部放置了 #!/usr/bin/env bash - user3473750
@user3473750 - 很明显,您没有将$FILE1设置为行号69。我怀疑FILE1="${@:$OPTIND:1}"可能不像您想象的那样工作。此时$@的值是多少?也许没有将文件名传递给您的脚本。也许您已经提取了比您想象的更多。请参考我的脚本,了解如何使用getopt以及我如何在之后使用shift调整$@ - David W.
$@ 包含“-r timeout.o”。 - user3473750
@user3473750:为了可视化检查传递给脚本的所有参数,请添加 for a; do echo "[$a]"; done。([...] 只是为了查看您值的确切边界。) - mklement0
脚本开头或在分配$FILE1之前,$@是否包含-r timeout.o?我怀疑您正在使用getopts,因为您正在使用$OPTIND。在这种情况下,-r timeout.o $OPTIND将包括-rtimeout.o。它将设置为3,这意味着您没有指向文件名。使用shift = $((OPTIND - 1))并验证$1仍然具有值。 - David W.
PS4这个东西很可爱,但是在使用“函数”这样高级的东西时会出现问题。我喜欢:PS4='>${FUNCNAME:-script}> ' == 如果你喜欢,可以用'$LINE'来增强它,但通常不需要。 - Bruce K

1

为了重申并补充@DavidW.的出色回答:

  • 检查你的脚本的shebang行(第一行),确保它由bash执行:是#!/bin/bash还是#!/usr/bin/env bash

  • 检查你的脚本文件中是否有隐藏的控制字符(如\r),这可能导致意外行为;运行cat -v scriptFile | fgrep ^ - 它不应该产生输出;如果文件包含\r字符,它们将显示为^M

    • 要删除\r实例(更准确地说,将Windows风格的\r\n换行序列转换为Unix \n-only序列),可以使用dos2unix file进行就地转换;如果你没有此实用程序,可以使用sed 's/'$'\r''$//' file > outfile(注意:使用一个不同的输出文件,否则会破坏你的输入文件);要删除所有\r实例(即使没有后跟\n),请使用tr -d '\r' < file > outfile(注意:使用一个不同的输出文件,否则会破坏你的输入文件)。
除了@DavidW.的出色调试技巧外,您还可以添加以下内容来可视化检查传递给脚本的所有参数:
i=0; for a; do echo "\$$((i+=1))=[$a]"; done 

(将值放在[...]中的目的是为了看到值的确切边界。)
这将产生类似于:
$1=[-g]
$2=[input.txt]
...

请注意,如果没有传递参数,则不会打印任何内容。

我正在使用 #!/usr/bin/env bash 并且我有大量的 ^M。 - user3473750
@user3473750:(a) 你应该从你的脚本中删除\r(^M)字符,可以参考我的更新答案;(b) 尽管看起来你的参数本身传递得很好,但是通过${@:$OPTIND:1}访问timeout.o存在问题——为了缩小问题范围,请使用$2代替${@:$OPTIND:1},看看是否有效。 - mklement0
如何删除 \r 字符?Geany 不让我这样做。:( - user3473750
tr -d '\r' < depcg.sh > depcg.sh 完全清空了文件......谢谢吧... - user3473750
很抱歉听到这个。请注意我的示例使用“outfile”,这是一个与输入文件不同的文件,以防止出现刚才遇到的情况-你遇到的是一种不幸但非常基本的shell陷阱(执行命令之前会截断输出文件)。我已经在我的答案中添加了一个警告。 - mklement0
显示剩余2条评论

0

我总是得到未找到。 - user3473750

0

不要用一种棘手的方式从"$@"中取出一个项目,为什么不使用getopts处理过的参数进行shift

while getopts ...
done

shift $(( OPTIND - 1 ))

FILE1=$1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接