Bash 脚本具有 运行 绝对路径的意外行为
Bash Script has unexpected behavior if run with absolut path
所以我们有这个脚本应该根据用户输入更改 linux 机器的 IP。必须验证此用户输入。
如果脚本 运行 在它所在的目录中,一切都按预期工作,但一旦它 运行 具有绝对路径,它似乎在某些地方中断了。
我已经尝试使用调试选项 set -x 但输出几乎保持不变。
read -p "Please enter the netmask (CIDR format): " netmask
if [ ! $(echo "$netmask" | egrep "^([12]?[0-9]?)$") ];
then
subnetok=0
fi
if [ "$subnetok" == "0" ];
then
echo -e "3[41m3[39m Subnetmask is invalid!3[0m"
sleep 1
return 1
fi
如果脚本在 运行 目录中,这是调试输出:
++ echo 24
++ egrep '^([12]?[0-9]?)$'
+ '[' '!' 24 ']'
+ '[' '' == 0 ']'
如果脚本 运行 具有绝对路径
,则这是调试输出
+++ echo 24
+++ egrep --color=auto '^([12]?[0-9]?)$'
++ '[' '!' 24 ']'
++ '[' 0 == 0 ']'
++ echo -e 'Subnetmask is invalid'
我希望相同数字的输出相同
:
By running with an absolute path I mean . /usr/local/script/script.sh instead of cd into /usr/local/script/ and then ./script.sh
不同之处在于,在一种情况下您执行 脚本,而在另一种情况下您采购 脚本。有关详细信息,请参阅 What is the difference between executing a Bash script vs sourcing it?。
当您 运行 ./script.sh
在点和斜杠之间没有 space 时,您正在 执行 新脚本中的脚本 shell。当您是 运行 . /usr/local/script/script.sh
时,您正在获取当前 shell 中的脚本。例如,如果您在当前 shell 中设置了别名,而新 shell 中不会出现该别名,例如 alias egrep='egrep --color=auto'
,这可能会产生影响。这就是为什么会有差异。
来自链接的问题:
Both sourcing and executing the script will run the commands in the script line by line, as if you typed those commands by hand line by line.
The differences are:
When you execute the script you are opening a new shell, type the commands in the new shell, copy the output back to your current shell, then close the new shell. Any changes to environment will take effect only in the new shell and will be lost once the new shell is closed.
When you source the script you are typing the commands in your current shell. Any changes to the environment will take effect and stay in your current shell.
Use source if you want the script to change the environment in your currently running shell. use execute otherwise.
当您 运行 脚本时:
. /usr/local/script/script.sh
这使用 .
命令,运行 是当前 shell 中的脚本 (相当于 source
).也就是说,它 运行 在您的交互式 shell 中添加它,而不是将新的 shell 分叉到 运行 它。参见:What is the difference between ./somescript.sh
and . ./somescript.sh
这有(至少)两个作用:
当前的 shell 是交互式的,并且显然为 egrep
定义了一个别名,这让事情变得有点奇怪。不是真正的问题,只是很奇怪。
现在的shell显然已经有了变量subnetok
的定义,而且是“0”。它可能是您上次 运行 以这种方式编写脚本时遗留下来的。这就是导致问题的原因。
主要解决方案是脚本需要显式初始化 subnetok
而不是仅仅假设它是未定义的:
subnetok=1
if ...
或者,如果您不需要该变量用于其他用途,您可以跳过它并立即处理该条件:
if [ ! $(echo "$netmask" | egrep "^([12]?[0-9]?)$") ]; # See below for alternatives
then
echo -e "3[41m3[39m Subnetmask is invalid!3[0m"
...
其他推荐:
运行 没有 .
:
的脚本
/usr/local/script/script.sh
为脚本指定 bash shell(即 #!/bin/bash
或 #!/usr/bin/env bash
).
使用更好的方法检查子网的有效性,例如:
if ! echo "$netmask" | grep -Eq "^[12]?[0-9]?$"
或(仅限bash):
if ! [[ "$netmask" =~ ^[12]?[0-9]?$ ]]
不要使用 echo -e
,因为它不可移植(即使在相同 OS 的不同版本、shell 的模式等之间也是如此)。请改用 printf
(对于包含反斜杠的字符串,我建议使用 single-quotes,因为在某些情况下,在 double-quotes 中它们会得到 pre-parsed):
printf '3[41m3[39m Subnetmask is invalid!3[0m'
请注意 printf
而不是 drop-in echo -e
的替代品,当您使用变量 and/or 多个参数。阅读手册页。
所以我们有这个脚本应该根据用户输入更改 linux 机器的 IP。必须验证此用户输入。
如果脚本 运行 在它所在的目录中,一切都按预期工作,但一旦它 运行 具有绝对路径,它似乎在某些地方中断了。
我已经尝试使用调试选项 set -x 但输出几乎保持不变。
read -p "Please enter the netmask (CIDR format): " netmask
if [ ! $(echo "$netmask" | egrep "^([12]?[0-9]?)$") ];
then
subnetok=0
fi
if [ "$subnetok" == "0" ];
then
echo -e "3[41m3[39m Subnetmask is invalid!3[0m"
sleep 1
return 1
fi
如果脚本在 运行 目录中,这是调试输出:
++ echo 24
++ egrep '^([12]?[0-9]?)$'
+ '[' '!' 24 ']'
+ '[' '' == 0 ']'
如果脚本 运行 具有绝对路径
,则这是调试输出+++ echo 24
+++ egrep --color=auto '^([12]?[0-9]?)$'
++ '[' '!' 24 ']'
++ '[' 0 == 0 ']'
++ echo -e 'Subnetmask is invalid'
我希望相同数字的输出相同
By running with an absolute path I mean . /usr/local/script/script.sh instead of cd into /usr/local/script/ and then ./script.sh
不同之处在于,在一种情况下您执行 脚本,而在另一种情况下您采购 脚本。有关详细信息,请参阅 What is the difference between executing a Bash script vs sourcing it?。
当您 运行 ./script.sh
在点和斜杠之间没有 space 时,您正在 执行 新脚本中的脚本 shell。当您是 运行 . /usr/local/script/script.sh
时,您正在获取当前 shell 中的脚本。例如,如果您在当前 shell 中设置了别名,而新 shell 中不会出现该别名,例如 alias egrep='egrep --color=auto'
,这可能会产生影响。这就是为什么会有差异。
来自链接的问题:
Both sourcing and executing the script will run the commands in the script line by line, as if you typed those commands by hand line by line.
The differences are:
When you execute the script you are opening a new shell, type the commands in the new shell, copy the output back to your current shell, then close the new shell. Any changes to environment will take effect only in the new shell and will be lost once the new shell is closed.
When you source the script you are typing the commands in your current shell. Any changes to the environment will take effect and stay in your current shell.
Use source if you want the script to change the environment in your currently running shell. use execute otherwise.
当您 运行 脚本时:
. /usr/local/script/script.sh
这使用 .
命令,运行 是当前 shell 中的脚本 (相当于 source
).也就是说,它 运行 在您的交互式 shell 中添加它,而不是将新的 shell 分叉到 运行 它。参见:What is the difference between ./somescript.sh
and . ./somescript.sh
这有(至少)两个作用:
当前的 shell 是交互式的,并且显然为
egrep
定义了一个别名,这让事情变得有点奇怪。不是真正的问题,只是很奇怪。现在的shell显然已经有了变量
subnetok
的定义,而且是“0”。它可能是您上次 运行 以这种方式编写脚本时遗留下来的。这就是导致问题的原因。
主要解决方案是脚本需要显式初始化 subnetok
而不是仅仅假设它是未定义的:
subnetok=1
if ...
或者,如果您不需要该变量用于其他用途,您可以跳过它并立即处理该条件:
if [ ! $(echo "$netmask" | egrep "^([12]?[0-9]?)$") ]; # See below for alternatives
then
echo -e "3[41m3[39m Subnetmask is invalid!3[0m"
...
其他推荐:
运行 没有
的脚本.
:/usr/local/script/script.sh
为脚本指定 bash shell(即
#!/bin/bash
或#!/usr/bin/env bash
).使用更好的方法检查子网的有效性,例如:
if ! echo "$netmask" | grep -Eq "^[12]?[0-9]?$"
或(仅限bash):
if ! [[ "$netmask" =~ ^[12]?[0-9]?$ ]]
不要使用
echo -e
,因为它不可移植(即使在相同 OS 的不同版本、shell 的模式等之间也是如此)。请改用printf
(对于包含反斜杠的字符串,我建议使用 single-quotes,因为在某些情况下,在 double-quotes 中它们会得到 pre-parsed):printf '3[41m3[39m Subnetmask is invalid!3[0m'
请注意
printf
而不是 drop-inecho -e
的替代品,当您使用变量 and/or 多个参数。阅读手册页。