gpt4 book ai didi

bash - 为什么我从此bash脚本中得到“缺少`]'来列出文件?

转载 作者:行者123 更新时间:2023-12-03 00:13:37 24 4
gpt4 key购买 nike

我有以下代码

#!/bin/bash
if [ "-f /data/sb3???sh25t.sqfs" && "! -f /data/sb3??????sh25t.sqfs" && "! -f /data/sb3????????sh25t.sqfs" && "! -f /data/sb3???????sh25t.sqfs" ] ;then
select build in `ls -D /data/tftpboot/sb3???sh25t.sqfs` ; do
break
done
build=${build:15}
fi


当我运行此脚本时,它给我一个错误:


缺少“]”


我该如何解决?

最佳答案

好吧,给自己倒一罐Jolt Cola坐下。这需要一段时间...

Unix shell中的if语句接受一个命令(任何命令),对该命令进行处理,如果该命令返回退出代码为零,则将执行if语句的then子句。如果该命令返回非零值,则将在存在一个子句的情况下执行else子句。

例如:

if ls foo 2>&1 > /dev/null
then
echo "File 'foo' exists"
else
echo "No Foo For you!"
fi


上面将运行 ls foo命令。输出将通过 2>&1 > /dev/null删除,因此如果 foo存在,您将看不到列表,如果 foo不存在,则不会收到错误消息。但是,如果 ls存在, 0命令仍将返回 foo,如果 1不存在,则将返回 foo

这使您可以执行各种整洁的工作:

if mv foo foo.backup
then
echo "Renamed `foo` directory to `foo.backup`"
else
echo "BIG HORKING ERROR: Can't rename 'foo' to 'foo.backup'"
exit 2 #I need to stop my program. This is a big problem.
fi


在上面的代码中,在放入新的 foo.backup目录之前,我尝试将目录 foo重命名为 foo。目录 mv可能不存在,所以 if命令失败。也许我没有重命名目录的权限。不管是什么, mv语句都允许我测试 if命令是否成功。

这是 foo命令所做的全部操作:它执行一个命令,并根据返回值是否为零,执行或不执行then子句。



但是,如果我想测试特定条件是否存在怎么办?例如:


是否有一个名为 $value的目录?
我的环境变量 if的值是否大于3?


如果有某种命令可以让我测试这些东西,那不是很好吗?嘿,如果我的测试为true时该命令返回零值,如果我的测试为false则该命令返回非零值,我可以将其合并到 test语句中!

幸运的是,Unix中有一个名为test的命令可以完全做到这一点:

if test -d foo
then
echo "Directory foo exists"
fi

if test $value -gt 3
then
echo "\$value is bigger than 3"
fi


Unix命令 [具有与 [命令的硬链接也恰好发生了:

$ cd /bin
$ ls -li test [
54008404 -rwxr-xr-x 2 root wheel 18576 Jul 25 2012 [
54008404 -rwxr-xr-x 2 root wheel 18576 Jul 25 2012 test


第一列是 inode号。由于两个都是相同的inode编号,因此 test命令只是与 [命令的硬链接。

现在,事情变得更容易理解了。 -gt 1实际上是Unix命令!这就是为什么我需要在方括号和要测试的对象之间留一个空间:

# Invalid Syntax: I need spaces between the test and the braces:
if [$value -gt 3]

# Valid Syntax
if [ $value -gt 3 ]

# Actually the same command as above
if test value -gt 3


这也使得为什么 -fgt带有短划线而不是简单地 f-gt更清楚。这是因为 -fif是测试命令的参数!



现在,您对内置外壳 [的工作方式以及 -f实际上是Unix命令的方式有了更清楚的了解,我们可以查看您的if语句,看看问题出在哪里:

if [ "-f "/data/sb3???sh25t.sqfs" && "! -f /data/sb3??????sh25t.sqfs"  && \
"! -f /data/sb3????????sh25t.sqfs" && "! -f /data/sb3???????sh25t.sqfs" ]


首先,您有:

"-f /data/sb3???sh25t.sqfs"


引号使整个字符串成为测试命令的单个参数,这不是您想要的。您希望 -f参数与要测试的文件分开。因此,您需要从引号中除去 test,因此test命令将其视为参数:

if [ -f "/data/sb3???sh25t.sqfs" && ! -f "/data/sb3??????sh25t.sqfs"  && \
! -f "/data/sb3????????sh25t.sqfs" && ! -f "/data/sb3???????sh25t.sqfs" ]


这仍然行不通。查看 &&命令的(手册页)[http://www.manpagez.com/man/1/test/]。请注意, test对于 -a命令不是有效参数。相反,您假设使用 -f组合两个表达式_您的测试:

if [   -f "/data/sb3???sh25t.sqfs"      -a ! -f "/data/sb3??????sh25t.sqfs"  -a \
! -f "/data/sb3????????sh25t.sqfs" -a ! -f "/data/sb3???????sh25t.sqfs" ]
then


!在引号之外( -a也在引号之外),我正在将 &&和以及所有四个条件一起使用。

我也可以这样做:

if [ -f "/data/sb3???sh25t.sqfs" ]      && [ ! -f "/data/sb3??????sh25t.sqfs" ] && \
! -f "/data/sb3????????sh25t.sqfs" ] && [ ! -f "/data/sb3???????sh25t.sqfs" ]


foo实际上是一个称为列表运算符的bash shell结构。它将两个单独的命令链接在一起,并执行以下操作:


运行第一个命令。
如果第一个命令返回非零值,请运行第二个命令。否则,请返回第一个命令的退出代码,并且不要运行第二个命令。


例如,看到这样的情况很常见:

[ -d foo ] && rm -rf foo


或(同一件事):

test -d foo && rm -rf foo


在以上命令中,我正在测试以查看 [...]是否作为目录存在。如果确实存在,我的测试命令将返回一个真值,然后执行第二个删除目录的命令。和...一样

if [ -d foo ]
then
rm -rf foo
fi


因此,我的if语句由四个单独的命令组成。第一个 /data/sb3???sh25t.sqfs执行,并查看文件 &&是否实际存在。如果是,则该测试为真,并返回零退出代码。然后, /data/sb3??????sh25t.sqfs执行列表中的下一个命令。

下一个测试命令查看 &&是否不存在。如果该文件不存在,那么测试命令将返回零退出代码,并且下一个 test列表运算符将运行下一个测试命令。仅当前三个 if命令为true时,才会执行最后一个测试命令。如果最后一个测试命令也是true语句,那么您的then子句将被执行。



希望您了解 foo.???现在如何工作以及遇到问题的原因。

可悲的是,还有另外一个问题正在发生,那就是Unix shell的工作方式以及 globbing与命令行交互的方式。

与其他操作系统(咳嗽Windows!咳嗽)中的同类操作系统不同,Unix外壳非常智能,也非常实用。

从命令行尝试以下命令:

$ echo "Print an *"   #Use quotes
Print an *


现在尝试这个:

$ echo Print an *    #No quotes
Print an bin foo bar...


这将打印所有“打印一个”,然后打印当前目录中的所有文件。

试试这个:

$ touch foo.out foo.txt
$ echo "Do I have any foo.??? files"
Do I have any foo.??? files
$ echo Do I have any foo.??? files
Do I have any foo.out foo.txt files


同样,没有引号,我要求打印的内容已更改。这是因为Unix shell很麻烦。发生的情况是,Unix外壳会看到 echo浮动模式,并在 xtrace命令有机会回显任何内容之前用所有匹配的文件替换该模式。

您可以通过以下方式看到这种情况:

$ set -x   #Turns on `xtrace`
$ touch foo.out foo.txt
+ touch foo.out foo.txt
$ echo "Do I have any foo.??? files"
+ echo "Do I have any foo.??? files"
Do I have any foo.??? files
$ echo Do I have any foo.??? files
+ echo Do I have any foo.out foo.txt files
Do I have any foo.out foo.txt files
$ set +x #Turns off `xtrace`


+功能向您显示在shell嘲笑命令后您将要执行的命令。以 /data/sb3aaash25t.sqfs开头的行显示了命令将实际执行的操作。

我提出这个问题的原因是可能存在问题。如果碰巧有多个与您的模式匹配的文件,那么所有与您的模式匹配的文件都将被替换到您的测试命令中。让我们再来看一次命令:

if [ -f /data/sb3???sh25t.sqfs ] &&        [ ! -f /data/sb3??????sh25t.sqfs ] && \
[ ! -f /data/sb3????????sh25t.sqfs ] && [ ! -f /data/sb3???????sh25t.sqfs ]


如果您有一个文件 /data/sb3bbbsh25t.sqfs和一个名为 -f的文件,则if语句中的第一个测试命令将如下所示:

if [-f /data/sb3aaash25t.sqfs /data/sb3bbbsh25t.sqfs ] 


这可能不起作用,因为 ls参数仅采用一个文件名。因此,即使您执行了上述所有操作,您仍然可能会遇到无法运行的程序。更糟糕的是,它会间歇性地失败,这更加难以调试。

解决此问题的一种方法是忘记test命令,并使用 ls命令,就像您在此答案的顶部看到的那样:

if ls /data/sb3???25t.sqfs 2>&1 > /dev/null
then
echo "At least one file that matches pattern sb3???25t.sqfs exists"
fi


因此,您可以这样做:

if   ls /data/sb3???sh25t.sqfs       2>&1 > /dev/null && \
! ls /data/sb3??????sh25t.sqfs 2>&1 > /dev/null && \
! ls /data/sb3????????sh25t.sqfs 2>&1 > /dev/null && \
! ls /data/sb3???????sh25t.sqfs 2>&1 > /dev/null
then


因为 if可以占用多个文件。

很抱歉,冗长的解释。 99%的时间,您可以继续前进,而不必了解 [...]语句的工作方式,或者 if确实是与 [完全分开的Unix命令,或者该混杂的Unix shell将如何运行绊倒你。

碰巧您因这个问题而中奖了。如果您今天玩过乐透游戏而不是写剧本,您可能会告诉老板您对他们的真正想法,相当的工作,并搬到文化如此原始的热带岛屿天堂,他们无话可说仿真器。

相反,明天将返回盐矿。



1是,是。我知道 /bin/[内置在BASH shell中,而不是 [命令。但是,内置在 /bin/[中的shell的作用类似于 命令。

关于bash - 为什么我从此bash脚本中得到“缺少`]'来列出文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14880244/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com