x=$?
if [ ${x} -eq '0' ]; then
echo "something missing"
exit 1
else
echo "all present"
fi
为什么我-eq: unary operator expected
执行上面的时候会出现这样的情况?
我正在做的事情的更完整版本:
对于 `cat ${2}` 中的文件 #$2 是 file.txt 做 如果 [ ! -f "${1}/${文件}"]; # $1 是一个路径 然后 echo "$文件丢失" 诺托克=$? 菲 完毕 如果 [ ${notok} -eq 0 ]; 然后 echo“需要检查” 1号出口 别的 echo“所有文件都存在” 菲
答案1
看起来变量x
未定义。
尝试一下
if [ ${x-1} -eq 0 ]
如果定义或未定义,则将${x-1}
计算为$x
的值。1
或者更好,我假设之前有一个命令x=$?
,为什么不使用
if cmd arg1 ... argn
then
# true
else
# false
fi
答案2
好吧,现在你已经编辑了你的问题,我想我知道到底发生了什么。
假设我有一个目录 ( ~/dir
),其中包含一些文件和一个文本文件 ( ~/file.txt
),其中包含一些文件名:
$ ls ~/dir
1.txt 2.txt 3.txt 4.txt 5.txt
$ cat ~/file.txt
1.txt
2.txt
3.txt
4.txt
5.txt
据我所知,您的脚本读取 的内容~/file.txt
并检查这些文件( 、 等)是否1.txt
存在2.txt
于~/dir
.
但是当脚本运行时:
$ script.sh ~/dir ~/file.txt
/home/user/bin/script.sh: line 11: [: -eq: unary operator expected
All files present
那么,发生了什么事?看一下这段代码摘录:
if [ ! -f "${1}/${file}" ]; # $1 is a path
then
echo "$file is missing"
notok=$?
fi
~/dir
这里的主要缺陷是,如果和的内容~/file.txt
匹配,则echo "$file is missing"; notok=$?
永远不会被执行。然后,如果notok
变量没有值,[ ${notok} -eq 0 ]
则会失败,因为${notok}
扩展为空(正如我们许多人所怀疑的那样)。
该缺陷不是唯一的缺陷,但它是您收到错误的原因[: -eq: unary operator expected
。
你如何解决它?
如果您不想重写脚本,只需初始化notok
变量即可与任意数字不同0
在你的主代码之前。例如:
notok='1'
for file in `cat ${2}` #$2 is file.txt
do
if [ ! -f "${1}/${file}" ]; # $1 is a path
then
echo "$file is missing"
notok=$?
fi
done
if [ ${notok} -eq 0 ];
then
echo "need to check"
exit 1
else
echo "All files present"
fi
有用:
# When the content of ~/dir and ~/file.txt matches
$ ls ~/dir
1.txt 2.txt 3.txt 4.txt 5.txt
$ cat ~/file.txt
1.txt
2.txt
3.txt
4.txt
5.txt
$ script.sh ~/dir ~/file.txt
All files present
# When the content of ~/dir and ~/file.txt does not match
$ ls ~/dir
1.txt 2.txt 3.txt 4.txt 5.txt
$ cat ~/file.txt
foo
1.txt
2.txt
3.txt
4.txt
5.txt
$ script.sh ~/dir ~/file.txt
foo is missing
need to check
但如果我要重写脚本,我会这样做:
# Initialize variables
# It's possible to parse arguments like `-d DIR -f FILE` but let's keep simple
dir="${1}"
txt="${2}"
missing='0'
# Don't read lines with for!
while IFS='' read -r 'file'; do
if [ ! -f "${dir}/${file}" ]; then
echo "Missing file: ${file}"
# Use a simple counter instead of catch the exit code of `echo`
missing=$(( missing + 1 ))
fi
done < "${txt}"
if [ "${missing}" -gt '0' ]; then
echo "Total: ${missing}"
exit '1'
else
echo 'All files are present'
fi
答案3
我认为你真正想要的是rsync
:
rsync --list-only --files-from="$2" "$1" "$1/.."
最后一个参数是一个虚拟参数,但是是必要的;它可以是任何目录,只要它与其$1
本身不是同一目录即可。
或者,如果您想要的只是退出状态和更少的冗长:
rsync --dry-run --files-from="$2" "$1" "$1/.."
(2>/dev/null
如果您愿意,可以选择在末尾添加真的只需要退出状态。)
现在,回顾一下您发布的损坏的脚本:
for file in `cat ${2}` #$2 is file.txt
反引号已被弃用;看cmd
*sh shell 中的反引号(即 )是否已被弃用?
您假设传入的文件名$2
名称中没有任何空格或特殊字符;看为什么我的 shell 脚本会因为空格或其他特殊字符而卡住?
你还假设列出的所有文件名称中$2
没有任何特殊字符或空格。
并且您假设该列表足够短,不会超过 shell 参数列表的最大长度。如果列表包含通配符(例如/*/*/*/*/../../../../*/*/*/*
),那么您将遇到很多很多问题。
do
if [ ! -f "${1}/${file}" ]; # $1 is a path
你真的仅有的想要允许常规文件吗?不是任何类型的特殊文件,甚至不是目录?如果是这样,那么您可能需要修改rsync
之前给出的命令。
另外,风格要点:!
前as[
不太if ! [ -f "$1/$file" ]
可能让你误入歧途。然而,在这个具体案例中,这并没有什么问题。 (如果你[ ! ... ]
与缺少的双引号结合起来,你就是非常很可能会被引入歧途。)
then
echo "$file is missing"
notok=$?
除非命令失败,否则在任何情况下都echo
将此处设置为 0。notok
当您意识到您已经检查了退出状态时,这甚至更愚蠢。您无需再次这样做。如果您在此处编写notok=1
(并添加notok=0
到脚本的顶部),您将获得更简单、更好的形式。
fi
done
if [ ${notok} -eq 0 ];
缺少双引号字符。再次,参见为什么我的 shell 脚本会因为空格或其他特殊字符而卡住?(并注意其副标题“稳健的文件名处理和 shell 脚本中其他字符串传递的介绍性指南”)。
then
echo "need to check"
这个消息完全没有意义。需要检查什么吗?谁需要检查什么?啊?
exit 1
else
echo "All files present"
fi
其余的都很好。是的。
替换脚本
if rsync --dry-run --files-from="$2" "$1" "$1/.." 2>/dev/null; then
echo "All files present"
else
echo "need to check"
exit 1
fi
不允许列表中的目录的替换脚本(将对其引发错误)
if ! rsync --dry-run --files-from="$2" "$1" "$1/.." --no-dirs 2>&1 | grep -q .; then
echo "All files present"
else
echo "need to check"
exit 1
fi
答案4
我非常喜欢冗长的脚本,但是
if [ X”$x” = X”0” ] ; then doThis ; else doThat ;fi
即使不完全正确,也是足够的。别跟我说话,我在 FreeBSD 库的一些脚本中看到了这一点。 OP 的真正问题是无法可靠地获取返回代码,但这似乎已被其他人修复。