精彩文章免费看

linux shell中for循环,如何读取整一行

在处理一个多行数据的过程中,使用for循环去读取每行数据,结果发现输出是

[Sam@localhost 20220927-temp]$ more temp.list
aaa     bbb     ccc
ddd     eee     fff
ggg     hhh     iii
[Sam@localhost 20220927-BA]$ for i in `cat temp.list`;do echo $i;done

shell中for循环的默认分隔符是:空格、tab、\n

一、若shell for 循环以\n作为分隔符,输出整行

#! /bin/bash
IFS=$'\n'
for i in `cat emp.list`
  echo $i

运行时使用./aa.sh 或者bash aa.sh。 不能使用sh aa.sh。

IFS="\n" # 将字符n作为IFS的换行符。
IFS=$"\n" # 这里\n确实通过$转化为了换行符,但仅当被解释时(或被执行时)才被转化为换行符;第一个和第二个是等价的
IFS=$'\n' # 这才是真正的换行符。

#! /bin/bash
while read i
  echo $i
done <temp.list
#! /bin/bash
cat temp.list |while read i
  echo $i
$ bash aa.sh
aaa bbb ccc
ddd eee fff
ggg hhh iii

二、若shell循环,读取每行第二列,列之间以tab分隔

开始时使用read 定义tab键的方式不成功

$ while read i; do a=`echo $i|cut -f2 -d ' '`; echo $a;done <temp.list aaa bbb ccc ddd eee fff ggg hhh iii $ while read i; do a=`echo $i|cut -f2 -d$'\t'`; echo $a;done <temp.list aaa bbb ccc ddd eee fff ggg hhh iii

转换为"$i" 则可成功,是因为echo $i会将默认tab或者空格作为分隔符的默认输出为空格,若管道后的命令的分隔符为\t,则不会按要求分割。

$ while read i; do a=`echo "$i"|cut -f2 -d '       '`; echo $a;done <temp.list
$ while read i; do a=`echo "$i"|cut -f2 -d $'\t'`; echo $a;done <temp.list

cut 的tab分隔符有两种方式:(1)先ctrl+v,然后按tab键,记得用单引号引起来;(2)使用$'\t'。

可以使用将行存入数组的方式调取数组的第二个元素,但要确保每列使用同样的分隔符,若既有空格又有tab键分隔符,则会出现混乱
$ while read line;do a=($line); echo ${a[1]};done <temp.list
while read line; do a=${line#*   };b=${a%%       *};echo $a,$b;done <temp1.list
bbb ccc,bbb
eee fff,eee
hhh iii,hhh
hanna2 hanna3,hanna2
#前两列为a变量,后一列为b变量。此处是按照tab键分隔。

##################################################################################################
环境变量IFS,成为内部字段分隔符(internal field seperator),定义了bash shell 用作字段分隔符的一系列字符。默认情况下,bash shell会将下列字符当作字段分隔符

  • 制表符 tab键
  • 换行符 \n
  • ~~~~可使用以下命令查看当前默认的IFS

    $ echo "$IFS" | od -b  
    0000000 040 011 012 012  
    0000004
    $ set |grep IFS
    IFS=$' \t\n'
    

    ~~~~直接输出IFS是看不到的,把它转化为二进制就可以看到了,"040"是空格,"011"是Tab,"012"是换行符"\n" 。最后一个 012 是因为 echo 默认是会换行的。

    ~~~~若单行数据中有空格时,使用for循环读取每一行,由于空格为默认的字段分隔符,bash shell 遇到空格,就认为读取到一个新的字段,会出现问题。此时需要在shell脚本中临时改变环境变量IFS的值来限制被bash shell当作字符段的字符,如修改为IFS=$'\n'IFS=$'\t'

    但是修改完IFS后,如何恢复IFS默认值?
    答: 方法一:简单粗暴,直接关闭当前终端。(经测试若修改只影响当前终端窗口)
    方法二:可以在修改IFS之前保存原来的IFS值,之后在恢复它,以防忘记,后续出现奇奇怪怪的显示。如下所示:

    IFS_old=$IFS
    IFS=$'\n'
    #在接下来的代码中使用新的IFS
    #code
    #使用完毕后恢复IFS原来的值
    IFS=$IFS_old
    

    答:方式三:在终端输入IFS=$' \t\n',即将是恢复为空格、tab键、和换行符作为分隔符。

    ##################################################################################################

    while read line是一次性将一行的文件信息读入并赋值给变量line。当文件中有多行文字,在while循环中再一次调用read语句,就会读取到下一条记录。而$line中的最后一行读完,无法获取下一行记录,从而退出while循环。

    for是每次读取文件中一个以空格为分隔符的字符串。