读书人

关于shell整型变量解决方案

发布时间: 2012-02-04 15:43:09 作者: rapoo

关于shell整型变量
大家好,我看到一道shell程序:

Perl code
#!/bin/shfoo=1while [ "$foo" -le 20 ]do  echo "Here we go again"  foo=$(($foo + 1)) #问题1.请问这句话改成“foo=$($foo + 1)”为什么不行?最里面的括号起到什么作用?doneexit 0


问题2.shell整型变量还能如何实现自加呢?
请讨论这两个问题,谢谢!

[解决办法]
1,我是这么记忆的,()是数值运算,$()是获取一条命令的输出,所以$((a + b))就是得到a+b的值了。

2,数值计算可以用foo=$(expr 1 + 2) 或者 let foo=1+2 , 可以使用for循环让其自增, 比较灵活的for循环形式如下:
C/C++ code
#!/bin/bashfor ((i = 0; i <= 10; ++ i))do        echo "a"done
[解决办法]
里面的()是新建一个子进程的意思
[解决办法]
探讨

请问2楼的朋友,括号表示新建一个子进程,这类问题如果要在网上搜索,是用什么关键字?谢谢大家!

[解决办法]
我搜了一个,你看下,很不错;
()

命令组.在括号中的命令列表, 将会作为一个子shell来运行.
在括号中的变量,由于是在子shell中,所以对于脚本剩下的部分是不可用的. 父进程, 也就是脚本本身, 将不能够读取在子进程中创建的变量, 也就是在子shell中创建的变量.
(cmd1;cmd2;cmd3)

初始化数组.
Array=(element1 element2 element3)

$(...)

相当于`...`命令,返回括号中命令执行的结果

let命令


(( ))

(( ... ))结构可以用来计算并测试算术表达式的结果. 退出状态将会与[ ... ]结构完全相反!还可应用到c风格的for,while循环语句,(( )) 中,所有的变量(加不加$无所谓)都是数值。

$((...))结构的表达式是C风格的表达式,其返回的结果是表达式值,其中变量引用可不用‘$’(当然也可以)

for((...;...;...))
do
cmd
done

while ((...))
do
cmd
done

比较操作符

<

小于

(("$a" < "$b"))
<=

小于等于

(("$a" <= "$b"))
>

大于

(("$a" > "$b"))
>=

大于等于

(("$a" >= "$b"))


(( 0 ))
echo "Exit status of \"(( 0 ))\" is $?." # 1

(( 1 ))
echo "Exit status of \"(( 1 ))\" is $?." # 0

(( 5 > 4 )) # 真
echo "Exit status of \"(( 5 > 4 ))\" is $?." # 0

(( 5 > 9 )) # 假
echo "Exit status of \"(( 5 > 9 ))\" is $?." # 1

(( 5 - 5 )) # 0
echo "Exit status of \"(( 5 - 5 ))\" is $?." # 1

(( 5 / 4 )) # 除法也可以.
echo "Exit status of \"(( 5 / 4 ))\" is $?." # 0

(( 1 / 2 )) # 除法的计算结果 < 1.
echo "Exit status of \"(( 1 / 2 ))\" is $?." # 截取之后的结果为 0.
# 1

(( 1 / 0 )) 2>/dev/null # 除数为0, 非法计算.
#
echo "Exit status of \"(( 1 / 0 ))\" is $?." # 1


for ((a=1; a <= LIMIT ; a++)) # 双圆括号, 并且"LIMIT"变量前面没有"$".
do
echo -n "$a "
done

while (( a <= LIMIT )) # 双圆括号, 变量前边没有"$".
do
echo -n "$a "
((a += 1)) # let "a+=1"
done

a=2
b=$(($a*4)) #a=2 b=8
c=$((a*3)) #a=2 c=6


[ ]

条件测试表达式放在[ ]中. 值得注意的是[是shell内建test命令的一部分, 并不是/usr/bin/test中的外部命令的一个链接.

文件测试操作符(如果下面的条件成立将会返回真)

-e

文件存在(推荐用)
-a

文件存在(不推荐用)

-f

表示这个文件是一个一般文件(并不是目录或者设备文件)
-s

文件大小不为零
-d



表示这是一个目录
-b

表示这是一个块设备(软盘, 光驱, 等等.)
-c

表示这是一个字符设备(键盘, modem, 声卡, 等等.)
-p

这个文件是一个管道
-h

这是一个符号链接
-L

这是一个符号链接
-S

表示这是一个socket
-t

文件(描述符)被关联到一个终端设备上

这个测试选项一般被用来检测脚本中的stdin([ -t 0 ]) 或者stdout([ -t 1 ])是否来自于一个终端.
-r

文件是否具有可读权限(指的是正在运行这个测试命令的用户是否具有读权限)
-w

文件是否具有可写权限(指的是正在运行这个测试命令的用户是否具有写权限)
-x

文件是否具有可执行权限(指的是正在运行这个测试命令的用户是否具有可执行权限)
-g

set-group-id(sgid)标记被设置到文件或目录上

如果目录具有sgid标记的话, 那么在这个目录下所创建的文件将属于拥有这个目录的用户组, 而不必是创建这个文件的用户组. 这个特性对于在一个工作组中共享目录非常有用.

-u

set-user-id (suid)标记被设置到文件上

如果一个root用户所拥有的二进制可执行文件设置了set-user-id标记位的话, 那么普通用户也会以root权限来运行这个文件. [1] 这对于需要访问系统硬件的执行程序(比如pppd和cdrecord)非常有用. 如果没有suid标志的话, 这些二进制执行程序是不能够被非root用户调用的.

-rwsr-xr-t 1 root 178236 Oct 2 2000 /usr/sbin/pppd


对于设置了suid标志的文件, 在它的权限列中将会以s表示.
-k

设置粘贴位

对于"粘贴位"的一般了解, save-text-mode标志是一个文件权限的特殊类型. 如果文件设置了这个标志, 那么这个文件将会被保存到缓存中, 这样可以提高访问速度. [2] 粘贴位如果设置在目录中, 那么它将限制写权限. 对于设置了粘贴位的文件或目录, 在它们的权限标记列中将会显示t.

drwxrwxrwt 7 root 1024 May 19 21:26 tmp/

如果用户并不拥有这个设置了粘贴位的目录, 但是他在这个目录下具有写权限, 那么这个用户只能在这个目录下删除自己所拥有的文件. 这将有效的防止用户在一个公共目录中不慎覆盖或者删除别人的文件. 比如说/tmp目录. (当然, 目录的所有者或者root用户可以随意删除或重命名其中的文件.)

-O

判断你是否是文件的拥有者
-G

文件的group-id是否与你的相同
-N

从文件上一次被读取到现在为止, 文件是否被修改过

f1 -nt f2

文件f1比文件f2新
f1 -ot f2

文件f1比文件f2旧
f1 -ef f2

文件f1和文件f2是相同文件的硬链接
!

"非" -- 反转上边所有测试的结果(如果没给出条件, 那么返回真).


比较操作符


整数比较

-eq

等于

-ne

不等于

-gt

大于

-ge

大于等于

-lt

小于

-le

小于等于


字符串比较

=

等于

==

等于,与=等价.(==比较操作符在双中括号对和单中括号对中的行为是不同的)

[[ $a == z* ]] # 如果$a以"z"开头(模式匹配)那么结果将为真
[[ $a == "z*" ]] # 如果$a与z*相等(就是字面意思完全一样), 那么结果为真.

[ $a == z* ] # 文件扩展匹配(file globbing)和单词分割有效.
[ "$a" == "z*" ] # 如果$a与z*相等(就是字面意思完全一样), 那么结果为真.

!=

不等号(这个操作符将在[[ ... ]]结构中使用模式匹配)

<

小于, 按照ASCII字符进行排序(注意"<"使用在[ ]结构中的时候需要被转义)
>

大于, 按照ASCII字符进行排序(注意">"使用在[ ]结构中的时候需要被转义)

-z

字符串为"null", 意思就是字符串长度为零
-n

字符串不为"null".

-a

逻辑与

-o

逻辑或


{xxx,yyy,zzz,...}

大括号扩展.
echo {1..20}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
cat {file1,file2,file3} > combined_file
# 把file1, file2, file3连接在一起, 并且重定向到combined_file中.
cp file22.{txt,backup}
# 拷贝"file22.txt"到"file22.backup"中

在大括号中, 不允许有空白, 除非这个空白被引用或转义.

echo {file1,file2}\ :{\ A," B",' C'}

file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C

代码块


这个结构事实上创建了一个匿名函数(一个没有名字的函数). 然而, 与"标准"函数不同的是, 在其中声明的变量,对于脚本其他部分的代码来说还是可见的(除了用declare,typeset命令声明的变量)


()会开启一个新的子shell,{}不会开启一个新的子shell

(())常用于算术运算比较,[[]]常用于字符串的比较.

$()返回括号中命令执行的结果

$(())返回的结果是括号中表达式值

${ }参数替换与扩展

参数替换

${var}
${var}=$var

${var:-default} ${var-default}
如果var未set,那么就是用default.两者之间不同只有当var为空变量时,前者为default,后者为空.

${var:=default} ${var=default}
如果var未set,那么就设置default.两者之间不同只有当var为空变量时,前者设置为default,后者设置为空.

${var:+default} ${var+default}
如果var被set,就是用default.未set,就使用null字符串.两者之间不同只有当var为空变量时,前者为null字符串,后者为default.



上面三种参数替换中,第二种使用后变量的值被改变.

参数替换扩展

${#var} ${#array}
字符串长度或数组第一个元素的字符串长度
例外:
${#*}、${#@}指位置参数的个数.
${#array
},$[#array[@]}指数组元素的个数

${var#pattern} ${var##pattern}
从var开头删除最近或最远匹配pattern的子串.

${var%pattern} ${var%%pattern}
从var结尾删除最近或最远匹配pattern的子串.

${var:pos}
变量var从位置pos开始扩展.

${var:pos:len}
从位置pos开始,并扩展len长度个字符

${var/pattern/replacement} ${var//pattern/replacement}
使用replacement来替换var中的第一个或所有pattern的匹配.

${var/#pattern/replacement}
如果var的前缀匹配到了pattern,那么就用replacement来替换pattern.

${var/%pattern/replacement}
如果var的后缀匹配到了pattern,那么就用replacement来替换pattern.

${!varprefix*} ${!varprefix@}
前边所有声明过的,以varprefix为前缀的变量名.


[[]]就是条件表达式,在bash中,字符串比较用 > < != == <= >= 只是在[]中 < >需要转义;对于数值比较.用 -lt -le -eq -ge -gt 来比较,与[[]]中表达不太一样,在[ ] 中的 < > 需要用转义 \< \>,如果有多个表达式,在[[ ]] 中用 && || 来组合,而[] 中是用 -a -o 来组合
[解决办法]

C/C++ code
ret=$(grep -q break *)
[解决办法]
探讨
既然“在括号中的变量,由于是在子shell中,所以对于脚本剩下的部分是不可用的”,那么为什么这样执行“foo=$(($foo + 1)) ”没有问题呢?

[解决办法]
探讨
请问各位,如果我想在当前目录的文本中搜索break这个词,使用这个语句:
grep -q break *

如何保存这个值的返回值呢?

我试过:
ret=$((grep -q break *))
然后echo $ret

不行。请问如何解决?

[解决办法]
命令的返回值在C语言源码中定义的 比如 在C语言中 用 exit -1 那么执行这个命令后 用ech $?可以看到 255-1=254 这个值.
不会将命令的返回内容赋值给shell变量.
用()子进程的方式计算结果后,返回值是结果的值.

还有,没明白你要那个 grep -q break*命令执行后的什么结果
[解决办法]
探讨

请问1楼的朋友,我试过用 let foo=$foo + 1不行啊,请问这是什么原因?

[解决办法]
探讨
谢谢各位。12楼的朋友,我想用“grep -q break *”尝试在当前目录下的文本搜索字符串“break”,把是否搜索到这个字符串的结果保存下来。

[解决办法]
grep -q break * break和*号之间有空格? 如果有的话 有问题的吧


还有$()是``的另一种表现方式。不用去纠结这个问题。用。$()这个取代``的一个理由是``和''比较像,容易引起程序不易差错.$()表示执行括号内的命令。返回命令结果
$(()) 这个是开另一个子进程执行括号内的命令,返回命令返回值,也不用纠结,规定而已. 两者没有任何关系的.你可以把下面的$(())分解成()和$(),前一个()是开启子进程,返回命令的结果(数字)然后$()是执行这个子进程而已
[解决办法]
可以很简单的理解为 $()里面必须是shell command 也就是shell命令. 而$(())这种形式就是用来做一些计算的 而 1+1 不是shell命令. 有些时候不要钻牛角尖. 只要记住这些用法就行.

如果你觉得晕 那就不要用$() 而用 `` 取代之.

读书人网 >UNIXLINUX

热点推荐