编辑 sub-shell 中的全局变量
Edit global variable in sub-shell
在以下脚本中,我使用 bash 检查用户是否拥有自己的主目录作为 CIS CentOS 8 基准测试 (6.2.8) 的一部分。
#!/bin/bash
grep -E -v '^(halt|sync|shutdown)' /etc/passwd | awk -F: '( != "'"$(which nologin)"'" && != "/bin/false") { print " " }' | while read user dir; do
if [ ! -d "$dir" ]; then
echo "The home directory ($dir) of user $user does not exist."
else
owner=$(stat -L -c "%U" "$dir")
if [ "$owner" != "$user" ]; then
echo "The home directory ($dir) of user $user is owned by $owner."
fi
fi
done
如果使用全局变量没有错误,我正在尝试打印一些东西。以下是我的尝试:
correct=true
grep -E -v '^(halt|sync|shutdown)' /etc/passwd | awk -F: '( != "'"$(which nologin)"'" && != "/bin/false") { print " " }' | while read user dir; do
if [ ! -d "$dir" ]; then
echo "The home directory ($dir) of user $user does not exist."
correct=false
else
owner=$(stat -L -c "%U" "$dir")
if [ "$owner" != "$user" ]; then
echo "The home directory ($dir) of user $user is owned by $owner."
correct=false
fi
fi
done
if [ "$correct" = true ]; then
echo "Non-compliance?: No"
echo "Details: All users own their home directories."
echo
fi
但是,全局变量 correct
不会改变,无论 while
循环中发生什么,因为它在多个子 shell 中。
我阅读了相关内容并注意到人们使用“此处字符串”,因此 while
循环不会在子 shell 中。但是,就我而言,我有多个管道(甚至可能为其他脚本添加更多管道),所以我真的不知道如何让它做我想做的事。
如何从循环中获取结果信息
这样我就可以在循环完成后显示摘要信息
当循环在子 shell?
中执行时
这个How is return handled in a function while loop?,一些重写导致了这个脚本,可以return false
:
#!/bin/bash
tmp=/tmp/$$
grep -E -v '^(halt|sync|shutdown)' /etc/passwd |
grep -E -v '/bin/false$|nologin$' |
awk -F: '{ print , }' > $tmp
correct=true
while read user dir; do
#echo $user $dir
if [ ! -d $dir ];
then
echo "Dir ($dir) does not exist"
correct=false
fi
owner=$(stat -L -c "$user" "$dir")
if [ "$owner" != "$user" ];
then
echo "home directory for user ($user) is owned by ($owner)"
correct=false
fi
done < $tmp
rm $tmp
echo "CORRECT: $correct"
- 您的 code/design 在极端情况下失败了:
用户 Horatio Altman 的用户名可能为
haltman
。
您的代码会忽略他,因为他的名字以 halt
开头。
你应该使用正则表达式
'^(停止|同步|关闭)<b>:</b>'
。
请注意末尾的冒号——这将只匹配具有 halt
的行,
sync
或 shutdown
作为 :
分隔文件中的第一个字段。
awk
是一个非常强大的程序。
您几乎不需要将它与 grep
或 sed
之类的东西结合起来;
awk
可以做(几乎?)他们可以做的任何事情。
在您的脚本中,我们可以消除 grep
并将halt
/ sync
/ shutdown
逻辑放入awk
:
awk -F: '( != "halt" && != "sync" && != "shutdown" && != "'"$(which nologin)"'" && != "/bin/false") { print " " }' /etc/passwd |
while read user dir; do
︙
因为 </code> 表示第一个冒号之前的所有内容(因为我们有 <code>-F:
)
我们正在做简单的字符串相等性检查
(而不是正则表达式匹配),
我们不需要开头的 ^
或结尾的 :
。
另请注意,我们可以在 |
之后添加 Enter
不需要反斜杠。
- 三引号乱七八糟;很难阅读,
而且很容易出错,尤其是当你在一年后编辑剧本时。
将信息注入
awk
脚本的一种更简洁的方法是使用变量:
awk -F: -v nl="$(which nologin)" \
'( != "halt" && != "sync" && != "shutdown" && != nl && != "/bin/false") { print " " }' /etc/passwd |
while read user dir; do
︙
在这里,我创建了一个名为 nl
的 awk 变量
值为 $(which nologin)
,
然后在与 </code> 的比较中使用该变量。
(另外,为了便于阅读,我用反斜杠打破了那条很长的线。)</li>
<li>好的,现在我要开始认真回答问题了。
如您所知,脚本的问题在于
它在子 shell 中设置 <code>correct
变量
然后尝试在子外壳之外访问它。
一种解决方案是移动访问变量的语句
进入子外壳。
你可能认为这是不可行的,
因为子外壳是 while
循环,
并且您不想在循环内打印成功消息。
诀窍是强制使用更大的子外壳:
correct=true
awk -F: -v nl="$(which nologin)" \
'( != "halt" && != "sync" && != "shutdown" && != nl && != "/bin/false") { print " " }' /etc/passwd |
(
while read user dir; do
if [ ! -d "$dir" ]; then
echo "The home directory ($dir) of user $user does not exist."
correct=false
else
owner=$(stat -L -c "%U" "$dir")
if [ "$owner" != "$user" ]; then
echo "The home directory ($dir) of user $user is owned by $owner."
correct=false
fi
fi
done
if [ "$correct" = true ]; then
echo "All users own their home directories."
fi
)
我添加了括号以强制包含一个子外壳
while
循环和 if-then
语句。
为了便于阅读,我将括号放在不同的行中;
您可以将它们移到上一行或下一行
如果您想尽量减少行数。
(下一个代码块演示了这一点。)
- 但是如果您想使用
$correct
的值怎么办?
在脚本中的某个时间点?
您不想将大的、不相关的代码块移动到子 shell 中
这样你就可以使用这个技巧了。
好吧,我的下一个技巧是将信息传递出子外壳
通过使用它的退出状态:
#!/bin/bash
correct=true
awk -F: -v nl="$(which nologin)" \
'( != "halt" && != "sync" && != "shutdown" && != nl && != "/bin/false") { print " " }' /etc/passwd |
(while read user dir; do
if [ ! -d "$dir" ]; then
︙
fi
done
if [ "$correct" = true ]; then
exit 0
else
exit 1
fi)
if [ "$?" = 0 ]; then
correct=true
else
correct=false
fi
︙ # Possibly much later in the script.
if [ "$correct" = true ]; then
echo "All users own their home directories."
fi
理论上,退出代码的范围可以从 0 到 255,
尽管最好将自己限制在 0-125 范围内。
请参阅 What is the min and max values of exit codes in Linux?
在以下脚本中,我使用 bash 检查用户是否拥有自己的主目录作为 CIS CentOS 8 基准测试 (6.2.8) 的一部分。
#!/bin/bash
grep -E -v '^(halt|sync|shutdown)' /etc/passwd | awk -F: '( != "'"$(which nologin)"'" && != "/bin/false") { print " " }' | while read user dir; do
if [ ! -d "$dir" ]; then
echo "The home directory ($dir) of user $user does not exist."
else
owner=$(stat -L -c "%U" "$dir")
if [ "$owner" != "$user" ]; then
echo "The home directory ($dir) of user $user is owned by $owner."
fi
fi
done
如果使用全局变量没有错误,我正在尝试打印一些东西。以下是我的尝试:
correct=true
grep -E -v '^(halt|sync|shutdown)' /etc/passwd | awk -F: '( != "'"$(which nologin)"'" && != "/bin/false") { print " " }' | while read user dir; do
if [ ! -d "$dir" ]; then
echo "The home directory ($dir) of user $user does not exist."
correct=false
else
owner=$(stat -L -c "%U" "$dir")
if [ "$owner" != "$user" ]; then
echo "The home directory ($dir) of user $user is owned by $owner."
correct=false
fi
fi
done
if [ "$correct" = true ]; then
echo "Non-compliance?: No"
echo "Details: All users own their home directories."
echo
fi
但是,全局变量 correct
不会改变,无论 while
循环中发生什么,因为它在多个子 shell 中。
我阅读了相关内容并注意到人们使用“此处字符串”,因此 while
循环不会在子 shell 中。但是,就我而言,我有多个管道(甚至可能为其他脚本添加更多管道),所以我真的不知道如何让它做我想做的事。
如何从循环中获取结果信息 这样我就可以在循环完成后显示摘要信息 当循环在子 shell?
中执行时这个How is return handled in a function while loop?,一些重写导致了这个脚本,可以return false
:
#!/bin/bash
tmp=/tmp/$$
grep -E -v '^(halt|sync|shutdown)' /etc/passwd |
grep -E -v '/bin/false$|nologin$' |
awk -F: '{ print , }' > $tmp
correct=true
while read user dir; do
#echo $user $dir
if [ ! -d $dir ];
then
echo "Dir ($dir) does not exist"
correct=false
fi
owner=$(stat -L -c "$user" "$dir")
if [ "$owner" != "$user" ];
then
echo "home directory for user ($user) is owned by ($owner)"
correct=false
fi
done < $tmp
rm $tmp
echo "CORRECT: $correct"
- 您的 code/design 在极端情况下失败了:
用户 Horatio Altman 的用户名可能为
haltman
。 您的代码会忽略他,因为他的名字以halt
开头。 你应该使用正则表达式'^(停止|同步|关闭)<b>:</b>'
。 请注意末尾的冒号——这将只匹配具有halt
的行,sync
或shutdown
作为:
分隔文件中的第一个字段。 awk
是一个非常强大的程序。 您几乎不需要将它与grep
或sed
之类的东西结合起来;awk
可以做(几乎?)他们可以做的任何事情。 在您的脚本中,我们可以消除grep
并将halt
/sync
/shutdown
逻辑放入awk
:
因为awk -F: '( != "halt" && != "sync" && != "shutdown" && != "'"$(which nologin)"'" && != "/bin/false") { print " " }' /etc/passwd | while read user dir; do ︙
</code> 表示第一个冒号之前的所有内容(因为我们有 <code>-F:
) 我们正在做简单的字符串相等性检查 (而不是正则表达式匹配), 我们不需要开头的^
或结尾的:
。 另请注意,我们可以在|
之后添加 Enter 不需要反斜杠。- 三引号乱七八糟;很难阅读,
而且很容易出错,尤其是当你在一年后编辑剧本时。
将信息注入
awk
脚本的一种更简洁的方法是使用变量:
在这里,我创建了一个名为awk -F: -v nl="$(which nologin)" \ '( != "halt" && != "sync" && != "shutdown" && != nl && != "/bin/false") { print " " }' /etc/passwd | while read user dir; do ︙
nl
的 awk 变量 值为$(which nologin)
, 然后在与</code> 的比较中使用该变量。 (另外,为了便于阅读,我用反斜杠打破了那条很长的线。)</li> <li>好的,现在我要开始认真回答问题了。 如您所知,脚本的问题在于 它在子 shell 中设置 <code>correct
变量 然后尝试在子外壳之外访问它。 一种解决方案是移动访问变量的语句 进入子外壳。 你可能认为这是不可行的, 因为子外壳是while
循环, 并且您不想在循环内打印成功消息。 诀窍是强制使用更大的子外壳:
我添加了括号以强制包含一个子外壳correct=true awk -F: -v nl="$(which nologin)" \ '( != "halt" && != "sync" && != "shutdown" && != nl && != "/bin/false") { print " " }' /etc/passwd | ( while read user dir; do if [ ! -d "$dir" ]; then echo "The home directory ($dir) of user $user does not exist." correct=false else owner=$(stat -L -c "%U" "$dir") if [ "$owner" != "$user" ]; then echo "The home directory ($dir) of user $user is owned by $owner." correct=false fi fi done if [ "$correct" = true ]; then echo "All users own their home directories." fi )
while
循环和if-then
语句。 为了便于阅读,我将括号放在不同的行中; 您可以将它们移到上一行或下一行 如果您想尽量减少行数。 (下一个代码块演示了这一点。) - 但是如果您想使用
$correct
的值怎么办? 在脚本中的某个时间点? 您不想将大的、不相关的代码块移动到子 shell 中 这样你就可以使用这个技巧了。 好吧,我的下一个技巧是将信息传递出子外壳 通过使用它的退出状态:
理论上,退出代码的范围可以从 0 到 255, 尽管最好将自己限制在 0-125 范围内。 请参阅 What is the min and max values of exit codes in Linux?#!/bin/bash correct=true awk -F: -v nl="$(which nologin)" \ '( != "halt" && != "sync" && != "shutdown" && != nl && != "/bin/false") { print " " }' /etc/passwd | (while read user dir; do if [ ! -d "$dir" ]; then ︙ fi done if [ "$correct" = true ]; then exit 0 else exit 1 fi) if [ "$?" = 0 ]; then correct=true else correct=false fi ︙ # Possibly much later in the script. if [ "$correct" = true ]; then echo "All users own their home directories." fi