寻觅生命中的那一片浅草......

文章带标签 shell

当while遇到重定向—-sh的陷阱

以下是我在编程时的亲身体会:
先看如下的程序:
#!/bin/sh

count=0
while read LINE
do
count=`expr $count + 1`
done < file
echo $count
问题是这个程序能得到什么样的结果?仿佛很明显,能够统计文件file的行数,然而你若执行一下便知,无论file的行数是多少,该程序的输出始终是0!为什么?
原来如果使用for,while,until,if,case这些命令时用到了重定向,那么sh会产生一个子shell来运行它们。可以想象count在循环体中能够正确的计数,一旦while执行完毕,子shell完成,其内部的变量count就消失了。因而得到的结果是循环体外开始赋的值0!
可以避免吗?试试这样:
cat file|while read LINE
do
count=`expr $count + 1`
done
echo $count
依然如此!怎么办?可以先关闭标准输入,然后以你要读取得文件重新打开它,这样就不需要对while命令的输入进行改向,也就不会用子shell的方式来运行它了。如下:
exec<file
count=0
while read LINE
do
count=`expr $count + 1`
done
exec < /dev/tty
echo $count
也可以
exec 4<&amp;0 0< file
count=0
while read LINE
do
count=`expr $count + 1`
done
exec 0<&amp;4
echo $count
如果还有怀疑的话可以试一下if:
#!/bin/sh

echo &quot;abcd&quot;|if [ &quot;abcd&quot; = &quot;abcd&quot; ]; then
var=7
fi
echo $var
输出当然是空。去掉echo &quot;abcd&quot;|就好了。
所以不了解这一点在编程时就会非常危险,试想while,for,case,if,until是多么的常用,与管道或重定向连着用也是很常见的,可是一旦在其内部使用了什么变量,就是有进无出,有去无回了。
以上的说法仅限于sh编程,对于ksh,bash,据我所知都不会有这个问题,它们处理的时候不会当作子shell来做。其它种类的shell可以自己试一试。

当while read 遇上ssh

http://hi.baidu.com/test/blog/item/16ecf01f6f46e4f7e0fe0b10.html

当while read 遇上ssh
2007-08-15 21:13先看一段简化过的BASH SHELL代码

TODAY=`date +%Y%m%d`
SUFFIX=”tar”
CONF=the_config_file
i=0

while read HOST SRCPATH DSTPATH
do
(( i++ ))
if [ “X${HOST:0:1}” = “X#” -o ${#DSTPATH} -eq 0 ]
then
#忽略注释行及少于3个项的记录
else
ssh ${HOST} “tar c ${SRCPATH}” >${DSTPATH}.${TODAY}.${SUFFIX} 2>/dev/null
fi
done < $CONF

作者的本意是通过脚本读取配置文件$CONF,然后打包备份$CONF中指定的文件,但执行后发现程序在成功备份$CONF中指定的第一个文件后即退出……

跟踪后发现read在备份完第一个文件后就再读不到任何数据,所以while循环结束--也就是说,
通过重定向$CONF文件传给标准输入的数据被

ssh ${HOST} “tar c ${SRCPATH}” >${DSTPATH}.${TODAY}.${SUFFIX} 2>/dev/null

这个命令提前吃掉了。

将其改为
ssh ${HOST} “tar c ${SRCPATH}” >${DSTPATH}.${TODAY}.${SUFFIX} 2>/dev/null </dev/null

将ssh的标准输入重定向到/dev/null,程序正常备份所有$CONF中指定的文件。

while read line的一些问题

while read line的一些问题

Q1. 曾经面试的时候被问到一个问题, 说
while read line ; do
echo $line ;
done < ./a.txt
失败, 会是什么原因, 当时觉得不太可能, 就回答了“我用的时候没有碰到过这种情况”。 前段在写一个脚本的时候, 确实遇到了所谓的while read 失败, 原因是我读入的文件是在win下用dos格式保存的文本, 所以每行结束都带有一个\r字符, 这个字符在linux/unix环境下的作用是“回到一行的开始”, 如果再对$line做相关的字符串操作的话(比如字符串连接), 得到的结果可能会让人迷惑… 所以当把win下编译的文本用在linux/unix环境时, 最好做一下格式转换

Q2. 2009-08-01更新:
# cat ur_file
1234 abcd
5678 efgh

# vi t3.sh
#!/bin/bash

while read t1 t2 ; do
echo $t1 $t2 ; awk ‘{ print $0 }’
done < ./ur_file

#./t3.sh
1234 abcd
5678 efgh

# sh -x ./t3.sh 发现只read 了一次

Q3.

# seq 10 > file
# while read line ; do
> echo $line
> dd &>/dev/null
> done < ./file
1

为什么这里只打印第一行呢?
问题的关键在于:
read从输入读取了一行内容后,
其它的程序(比如 rsh或sed)从同样的地方把其它的输入读走了,
read当然读不到其它内容了,
while也就结束了

摘自:http://bbs.chinaunix.net/viewthread.php?tid=769040&extra=&page=2

可以打开set -x, 看看, 确实只read了两次, 第一次read出第一行, 第二次read不到就退出while循环
read 和 dd都从文件描述符3中读取数据

shell里非交互式修改用户密码

echo “newpasswd” | passwd user –stdin

comp.unix.shell FAQ 阅读笔记

最近在阅读一个比较经典的关于shell的faq,全文链接在这里http://home.comcast.net/~j.p.h/

作者将comp.unix.shell新闻组上的常见问题总结了一下,做成faq的形式,对学习很有帮助。

不打算全文翻译了,仅把认为比较重要的部分总结一下,做个备案吧

1.UUOC(useless use of cat)

避免 cat file | tr -d ‘xyz’ ,因为这样实际上跑了两个进程cat 和 tr

应该采用输入重定向 tr -d ‘xyz’ <file

2.shebang

脚本第一行是告诉操作系统解释器路径的,如果用解释器显式调用脚本文件,

那么第一行只是注释而已,可有可无。

3.echo 在不同的shell里的解释方式可能不同,要熟悉你的shell是如何做的。

优先采用cat输出变量

cat<<EOF

$a

EOF

4.随机数生成方法

最portable的方法是利用awk内置的srand和rand函数

awk ‘BEGIN {srand();print rand()}’

5.dos和unix文本文件之间转换

dos格式的文本文件,行末尾是
(CR LF)

unix格式的文本文件,行末尾是

所以dos格式文本在unix下显式的时候,尾部会多一个^M

二者转换方式是

dos—unix : sed ’s/^M$//’ dos.txt > unix.txt或者tr -d ‘
‘ <dosfile >unixfile

unix—dos: sed ’s/$/^M/’ unix.txt >dos.txt

另外,也可以用dos2unix unix2dos等现成工具

6.改变term窗口的标题

两种方法

$ echo -en “33]2;XXX07″

$ printf ‘%b’ ‘e]2;XXXa’

7.如何设定用户输入超时

bash里面可以用read的参数-t 设置超时

也可以利用终端属性完成

{

s=$(stty -g)

stty -icanon min 0 time 100

var=$(head -n 1)

stty “$s”

}

8. bash脚本一定要注意空格的使用

$ [ -f xxx ] isn’t the same as

$ [-f xxx ]

9.如何从路径中分离目录和文件

pathname=’/path/to/some/file’

dir=`dirname “$pathname”`

file=`basename “$pathname”`

缺点是,fork出了新进程

可以使用shell内置的字符串处理机制

pathname=/path/to/some/file

file=${pathname##*/}

case $pathname in

*/*) dir=${pathname%/*};;

*) dir=”

esac

10.如何删除特殊字符开头的文件

例如删除 -foo,有三种方法

rm -foo

rm — -foo

rm -i — * (进入交互模式,提示动作)

转载自:http://www.oolec.com/compunixshell-faq-study-note/

2024年四月
« 5月    
1234567
891011121314
15161718192021
22232425262728
2930