请教1个shell 问题

请教各位老师1个shell 问题,为什么我在while循环中,对变量进行了相加,但是循环结束后,这个变量还是0,我猜是因为管道|的问题,但是不知道怎么避免这个问题
[tidb@tidb01 tmp]$ cat test.sh
count=0
df -PT|while read line
do
echo $line
count=expr $count + 1

done
echo $count
#还是0

你是要读取df -PT结果的行数?df -PT |wc -l 这样就行了

写这段shell想计算行数?还是某列的count?

count=0
df -PT | while read line
do
echo $line
((count++))
done
echo $count

count=df -PT|wc -l
就行,用管道符进子shell了,改变不了父shell的变量。

#!/bin/bash
declare -x count=0
while IFS= read -r line
do
echo $line
count=$(expr $count + 1)
done <<< “$(df -PT)”
echo “Count: $count”

你没说自己用的是什么 shell, 但是在大部分的 shell 里, 管道的命令经常是在 subshell 里执行的,在 subshell 里对变量做的更改,是没办法在 subshell 外部获取的。

不同的 shell 表现不一样,BASH 是这样的:仅当 loop 是管道的最后一部分,才会创建一个 subshell (也就是你写的脚本的场景)

解决思路是去掉管道。以 BASH 为例:

1. 用文件当作输入

df -PT 的输出写到一个临时文件,把临时文件的内容用 < 重定向一下

2. Command grouping

df -PT 以外的处理都放一起

df -PT |
  {
    while read line
    do
      linecount=$((linecount + 1))
    done
    echo "$linecount"
  }

3. ProcessSubstitution

while read line
do
  linecount=$((linecount + 1))
done < <(df -PT)
echo "$linecount"

4. HereString

while read line
do
  linecount=$((linecount + 1))
done <<< $(df -PT)
echo "$linecount"

5. named pipe

mkfifo p
df -PT > p &
while read line
do
  linecount=$((linecount + 1))
done < p
echo "$linecount"

不止上面这些,肯定还有不少方法可以绕过

这种方式也不行的,while 循环结束后,count的值还是0

我的需求是,循环df -PT 里面的每一行,做判断,如果文件系统满足某1个条件就+1,但是这样直接把df -PT当成1个整体,并没有循环,没有达到效果

我的需求是,循环df -PT 里面的每一行,做判断,如果文件系统满足某1个条件就+1,并不是直接统计df -PT 有多少行

感谢大佬
df -PT |
{
while read line
do
linecount=$((linecount + 1))
done
echo “$linecount”
}
这种办法是OK的

但是下面就不行了
while read line
do
linecount=$((linecount + 1))
done <<< $(df -PT)
echo “$linecount”
应为把$(df -PT) 当成了1整行,并没有进行循环,所以结果是1

df -PT|awk ‘{if ($4>0) {print$4}}’|wc -l
这个意思?

你用的是 bash 吧?
可以在命令行这样测一下 HereString 的表现,应该不会是1行的

cat <<< $(df)

首先管道属于非内建指令,linux执行shell时,会创建“子shell”运行shell中的命令,当运行到非内建指令时,会创建“shell”运行非内建指令,变量的作用于在每个shell中有效,所以,非内建指令中定义的这些变量就只能在shell运行,而在子shell中不生效,所以,即便在while中给count赋值了,子shell中也不会获取到这个值。这类属于shell变量作用域问题,与其他语言不同。

解决这个问题的办法有两种

如果不是必须使用管道符的方式写while循环,可以用重定向的写法,这种写法循环内的变量在子shell中是生效的,比较简便

如果非要使用管道符的方式,可以创建临时文件,用于存放shell中的输出。

感谢老师的回复

[root@tidb01 ~]# cat <<< $(df)
Filesystem 1K-blocks Used Available Use% Mounted on /dev/mapper/rhel-root 49250820 23256508 25994312 48% / devtmpfs 2150452 0 2150452 0% /dev tmpfs 2162688 0 2162688 0% /dev/shm tmpfs 2162688 11996 2150692 1% /run tmpfs 2162688 0 2162688 0% /sys/fs/cgroup /dev/sda1 1038336 132920 905416 13% /boot /dev/mapper/datavg-lv_deploy 30832548 796296 28447004 3% /tidb-deploy /dev/mapper/datavg-lv_data 51474912 1377720 47459368 3% /tidb-data tmpfs 432540 0 432540 0% /run/user/0

不知道你的环境和版本,应该是 IFS 的配置问题。

为了让 shell 正确识别到特殊符号 \n, 你可以加上双引号。

cat <<< "$(df)"

可以了,谢谢大佬,谢谢老师

此话题已在最后回复的 60 天后被自动关闭。不再允许新回复。