我只编写了几周的脚本,但我已经成功地在这篇文章的底部编写了脚本并使其正常工作。然而,我知道我需要做什么真的是将下面的 if 语句条件移到上面的 if 语句中。所以我需要做这样的条件:
如果文件存在(并且不是空字符串)并且是以下 2 之一:(“交换机端口模式访问”或“允许交换机端口中继的 VLAN”)。
我觉得如果我做了类似下面的事情(不确定语法是否正确,但没关系,因为我只是在解释):
###### 1 ##### ################ 2 #################### ######## 3 ########
if ([ ! -z "${prev}" ] && [ "$line" = "switchport access vlan $i" ] || [[ $line =~ $regex ]]);then
然后我担心我不明白它是如何分组的。因此,如果我们将每个条件称为 1,2,3。那么这是否意味着条件 1&2 OR 3。或者这个人的条件是 1 & (2-OR-3)?比如,有没有一种方法可以像数学中那样通过在要分组的位周围加上括号来消除任何歧义。正确的做法是什么?
tmpfiles="$PWD/configs/*.tmp"
prev=
for basefile in $tmpfiles
do
while read line
do
## get old vlans and interfaces
for i in "${oldvlans[@]}"
do
if ([ ! -z "${prev}" ] && [ "$line" = "switchport access vlan $i" ]);then
line1="${prev}"
line2="${line}"
echo "${line1}"
echo "${line2}"
fi
done
### check for lines with "trunk allowed vlans" on and do a no trunk allowed vlan on them
regex="switchport trunk allowed vlan*"
if [ ! -z "${prev}" ] && [[ $line =~ $regex ]];then
line1="${prev}"
line2="${line}"
echo "${line1}"
echo "no ${line2}"
fi
prev="${line}"
done < "$basefile" > "$basefile.done"
编辑:我在下面做了一些测试来尝试找出逻辑,因为斯蒂芬说 () 不适用于对测试条件进行分组,我相信我证明了他/她是错的):
$ var1=sumit ; [ -z "var1" ] && ([ "$var1" = "sxxt" ] || [ "$var1" = "sumit" ]); echo $?
1
$ var1=sumit ; [ -n "var1" ] && ([ "$var1" = "sxxt" ] || [ "$var1" = "sumit" ]); echo $?
0
所以我认为这是在我想要的地方进行测试的正确方法:
If [condition1] AND if ([condition 2] OR [condition 3] are true).
但我只是在努力解决它,并且需要尝试澄清它。
答案1
在 bash 中,执行您想要的操作的最简洁方法是使用构造[[ ]]
而不是更有限的构造[ ]
并组合条件:
if [ ! -z "${prev}" ] && [[ "$line" = "switchport access vlan $i" || "$line" =~ $regex ]];then
其效果符合您的预期:
$ var1=sumit; [ -z "var1" ] && [[ "$var1" = "sxxt" || "$var1" = "sumit" ]]; echo $?
1
$ var1=sumit; [ -n "var1" ] && [[ "$var1" = "sxxt" || "$var1" = "sumit" ]]; echo $?
0
现在,您确实可以滥用( )
在子 shell 中运行测试并检查结果,但它很丑陋、复杂且不必要。如果您坚持走这条路线,至少避免使用子 shell 并使用{ }
对命令进行分组:
$ var1=sumit; [ -z "var1" ] && { [ "$var1" = "sxxt" ] || [ "$var1" = "sumit" ] ;}; echo $?
1
$ var1=sumit; [ -n "var1" ] && { [ "$var1" = "sxxt" ] || [ "$var1" = "sumit" ] ;}; echo $?
0
但实际上,如果您使用 bash,就选择[[ ]]
.如果使用 POSIX shell,您可以使用-o
:
$ var1=sumit; [ -z "var1" ] && [ "$var1" = "sxxt" -o "$var1" = "sumit" ] ; echo $?
1
$ var1=sumit; [ -n "var1" ] && [ "$var1" = "sxxt" -o "$var1" = "sumit" ] ; echo $?
0
答案2
关于这部分:
###### 1 ##### ################ 2 #################### ######## 3 ######## if ([ ! -z "${prev}" ] && [ "$line" = "switchport access vlan $i" ] || [[ $line =~ $regex ]]);then
然后我担心我不明白它是如何分组的。因此,如果我们将每个条件称为 1,2,3。那么这是否意味着条件 1&2 OR 3。或者这个人的条件是 1 & (2-OR-3)?
shell 没有&&
and的优先级||
在命令列表中(如上),但从左到右解析它们。因此,如果有A && B || C
,它会被有效地分组为(A && B) || C
.同样,A || B && C
是(A || B) && C
.
这与大多数其他编程语言不同,其中&&
优先级高于||
.更糟糕的是,在[[ .. ]]
条件语句和算术表达式(例如$(( .. ))
)内,Bash 也遵循该优先级。
所以,这两者是不同的:
if [[ a = a ]] || [[ a = a ]] && [[ a = b ]]; then
echo one
fi
if [[ a = a || a = a && a = b ]]; then
echo two
fi
(a = a
当然a = b
,这很愚蠢,但对于这个例子来说就足够了。)
最好避免使用命令列表作为条件,而是执行其他人建议的操作并使用[[ ... ]]
.
也就是说,只要您运行的是 Bash 或其他具有该功能的 shell,因为[[ ... ]]
这不是标准功能。在标准 POSIX shell 中,您只能使用命令列表。
至于您的代码,我不确定通过将这三个条件组合为一个条件是否会取得很大成果,即使正确分组也是如此:
if [ ! -z "${prev}" ] && ( [ "$line" = "switchport access vlan $i" ] || [[ $line =~ $regex ]] ); then
(无论您是否使用子外壳(foo)
或复合组{ foo;}
进行分组)
主要是因为按照您的方式,您必须在循环内执行后两个测试${oldvlans[@]}
,但实际上只有其中之一取决于循环变量。此外,该[ ! -z "$prev" ]
条件似乎是while read
循环执行任何操作的先决条件,所以为什么不单独使用它。
我宁愿将这三者分开而不是将它们结合起来:
while read line
do
if [ -z "$prev" ]; then
continue
fi
## we know "$prev" is non-empty now
## get old vlans and interfaces
for i in "${oldvlans[@]}"
do
if [ "$line" = "switchport access vlan $i" ]; then
...
fi
done
### check for "trunk allowed vlans" etc.
regex="switchport trunk allowed vlan"
if [[ $line =~ $regex ]]; then
...
fi
prev="${line}"
done < ...
(或者也许首先检查该行是否匹配switchport access vlan
,switchport trunk allowed vlan
然后决定要做什么。)
请注意,在正则表达式中,n*
表示“零个或多个字母n
”,不是“单个n
后接任何内容”,就像 shell 模式中的那样。但在[[ str =~ regex ]]
测试中,正则表达式无论如何都不会锚定到字符串的开头/结尾(例如[[ foobar =~ ob ]]
为 true),因此您无论如何都不需要尾随的“匹配任何内容”。
其他一些编程语言也可能提供比 shell 更好的字符串处理工具。