Saturday, August 17, 2013

bash中处理文件名有空格的问题 $(command ls) when file name with space

一个练习。遍历指定的目录,以树状结构显示包括子目录下的所有文件。
在bash中遍历文件用
for file in * ; do
...
done
很多教科书上用: for file in $(commmand ls *) ,但这种方法不能正确处理文件名中包含$IFS指定的分隔符,即便ls 用 --quoting-style=shell 限定也没有用。

for file in * ; 的缺点是不能处理隐藏文件或目录

完整源码如下:
#!/bin/bash

declare -r tab="---"
declare -r lead="|"

function recdir {

declare -i cbfiles=0
declare -a files 

local thistab="$2"$lead$tab
cd "$1"

# get how many files in this directory
for file in * ; do
 files[$(( cbfiles++ ))]="$file"
done

declare -i curr=0
for file in "${files[@]}" ; do
 curr=$((++curr))
 echo "$thistab$file"
 if [ -d "$file" ]; then
  space=${tab//-/' '}
  ll=""
  if [ $(( curr < cbfiles )) = 1 ]; then
   ll=$2$lead"${space}"
  else
   ll="$2 ${space}"
  fi
  recdir "$file" "$ll"
 fi
done

cd ".."
}

rootdir=${1:-.}
rootdir=${rootdir%/}

if [ -d "$rootdir" ]; then
 echo "$rootdir"
 recdir $rootdir
else
 echo "$rootdir is not a directory."
fi

unset rootdir

执行结果如下:
.
|---CRFSC_Manual.pdf
|---foo1
|---foo1~
|---p1_b.jpg
|---p1.jpg
|---p2.jpg
|---p3.jpg
|---studynote
|   |---Match Waveform Theory Notebook.odt
|   |---test.txt
|   |---whh
|       |---a.txt
|---SWIGDocumentation.pdf
|---科学和特异功能.odt

Wednesday, August 14, 2013

bash笔记(三)——条件测试

if 语句之后除了可以跟命令行之外,还可以用[...]结构,[...]结构和内部命令 test 是等效的,即 [ string1 = string2 ]test string1 = string2 的结果是相同的。另外在bash 2.05之后的版本还可以用[[...]] 结构,[[...]] 和[...]之间的不同可参考: http://stackoverflow.com/a/3427931/1036923 .

注意:在 “[” 之后和 “]” 之前空格是必须的。

通常代码形式如下:
if [ -n "$varname" ]; then
    echo "hello"
else
    echo '$varname' is null!
fi
在条件表达式里始终用双引号将变量引起,有以下两个原因:

  1. 将变量解释为一个单词。
  2. 如果变量是null, 没有双引号则上例中条件测试将变为[ -n ] ,它总是返回true; 用了双引号则表达式为[ -n "" ] ;

条件组合

if command && [ condition ] && [ condition ] || [ condition ]; then


if command && [ \( condition \) -a \( condition \) -o \( condition \) ]; then
...


-a 和 -o 只能用在测试条件表达式中

字符串比较

Operator True if...
str1 = str2 str1 matches str2
str1 != str2 str1 does not match str2
str1 < str2 str1 is less than str2
str1 > str2 str1 is greater than str2
-n str1 str1 is not null (has length greater than 0)
-z str1 str1 is null (has length 0)
注:bash 没有 >= 和 <= 比较符。

文件测试

下表列出常用的文件测试表达式。

Operator True if...
-a file file exists
-d file file exists and is a directory
-e file file exists; same as -a
-f file file exists and is a regular file(i.e., not a directory or other special type of file)
-r file You have read permission on file
-s file file exists and is not empty
-w file You have write permission on file
-x file You have execute permission on file, or directory search permission if it is a directroy
-N file file was modified since it was last read
-O file You own file
-G file file's group ID matchs yours (or one of yours, if you are in multiple groups)
file1 -nt file2 file1 is newer than file2
file1 -ot file2 file1 is older than file2


算术比较


Test Comparison
-lt Less than
-le Less than or equal
-eq Equal
-ge Greater than or equal
-gt Greater than
-ne Not equal
注:bash还有针对整数比较的独立语法,效率更高,所以要优先用它而不是上表列出的算术比较运算符。

Monday, August 12, 2013

bash 笔记(二)——字符串操作


一个常用的字符串操作是放在变量名的{}内的一些特定操作符,见下表。

Operator Substitution
${varname:-word} If varname exists and isn't null, return its value; otherwise return word.
Purpose: Returning a default value if the variable is undefined.
Example: ${count:-0} evaluates to 0 if count is undefined.
${varname:=word} If varname exists and isn't null, return its value; otherwise set it to word and then return its value. Positional and special parameters cannot be assigned this way.
Purpose: Setting a variable to a default value if it is undefined.
Example: ${count:=0} set count to 0 if it is undefined.
${varname:?message} If varname exists and isn't null, return its value; otherwise print varname:followed by message, and abort the current command or script(non-interactive shell only). Omitting message produces the default message parameter null or not set.
Purpose: Catching errors that result from variables being undefined.
Example: {count:?”undefined!”} prints “count: undefined!” and exits if count is undefined.
${varname:+word} If varname exists and isn't null, return word; otherwise return null.
Purpose: Testing for existence of a variable.
Example: ${count:+1} return 1 (which could mean “true”) if count is defined.
${varname:offset:length} Performs substring expansion. It returns the substring of $varname starting at offset and up to length characters. The first character in $varname is position 0. if length is omitted, the substring start at offset and continues to the end of $varname. If offset is less than 0 then the position is taken from the end of $varname. If varname is @, the length is number of positional parameters starting at parameter offset.
Purpose: Returning parts of a string(substrings or slices).
Example: if count is set to frogfootman, ${count:4} return footman. ${count:4:4} return foot.
表中除了最后一项,其余各项中的冒号(:)是可以省略的。如果没有冒号,则测试条件由“exists and isn't null" 变为 “exists",即仅仅测试变量是否存在。

模式匹配(Patterns and Pattern Matching)


Operator Substitution
${variable#pattern} If the pattern matches the beginning of the variable's value, delete the shortest part that matches and return the rest.
${variable##pattern} If the pattern matches the begginning of the variable's value, delete the longest part thar matches and return the rest.
${variable%pattern} If the pattern matches the end of the variable's value, delete the shortest part that matches and return the rest.
${variable%%pattern} If the parttern matches the end of the variable's value, delete the longest part the matches and return the rest.
${variable/parttern/string}
${variable//parttern/string}
The longest match to pattern in variable is replaced by string. In the first form, only the first match is replaced. In the second form, all matches are replaced. If the pattern begins with a #, it must match at the start of the variable. If it begins with a %, it must match with the end of the variable. If string is null, the matches are deleted. If variable is @ or *, the operation is applied to each positional parameter in turn and the expansion is the resultant list.
记忆小巧门:#是用在数字开头的,所以对应变量开始部分。%总是用在数字的后面,所以对应变量的结尾部分。

此类模式匹配比较经典的应用是对路径名的解析比如假设:

path=/home/david/document/long.file.name 

则:


${path##/*/}
                     long.file.name
${path#/*/}
      david/document/long.file.name
$path
/home/david/document/long.file.name
${path%.*}
/home/david/document/long.file
${path%%.*}
/home/david/document/long

最长和最短匹配只有在有通配符*存在的情况下才生效,否则他们的输出结果是一样的。比如:filename=alicece ,则${filename%ce} 和 ${filename%%ce} 两个输出的结果都是 alice .

长度操作符

 ${#varname}


*扩展的模式匹配

如果用shopt把extglob设置打开,则可以一次提供多个模式匹配,之间用|分隔。

Operator Meaning
*(patternlist) Matches zero or more occurrences of the given patterns.
+(patternlist) Matches one or more occurrences of the given patterns.
?(patternlist) Matches zero or one occurrences of the given patterns.
@(patternlist) Matches exactly one of the given patterns.
!(patternlist) Matches anything except one of the given patterns.
例子:

  • *(alice|hatter|hare) would match zero or more occurrences of alice, hatter, and hare. So it would match the null string, alice, alicehatter, etc.
  • +(alice|hatter|hare) would do the same except not match the null string.
  • ?(alice|hatter|hare) would only match the null string, alice, hatter, or hare.
  • @(alice|hatter|hare) would only match alice, hatter, and hare.
  • !(alice|hatter|hare) matches everything except alice, hatter and hare.

模式字串里可以包含shell的通配符,比如:+([0-9])可以匹配一位或多位的数字。模式还可以嵌套,比如 rm !(vt+([0-9])) 可以删除所有文件除了文件名由vt开始后跟一串数字的文件。

bash 笔记(一)——变量

1.变量的定义

varname=value
注意:等号两边不能有空格。如果变量(value)超过一个单词,则必须用引号引起。


2.变量和引号

$ fred='Four spaces between these    words.'
$ echo $fred 

Four spaces between these words. 
当使用不带双引号使用变量时,shell会把字符串切分成多个单词,在处理命令行参数时通常就这样做。如果用双引号引起,则shell把整个字符串作为一个单词处理。
$ echo "$fred"
Four spaces between these    words.
双引号和单引号的区别
双引号称为“弱引用”,单引号称为“强引用”。顾名思义,双引号会对引号里的内容进行转意,比如将变量里的内容引出;而单引号则完全拷贝单引号里的内容,对特殊字符不做转意处理。以上面$fred为例:
$ echo '$fred'
$fred

3.位置参数变量

3.1 $0, $1, $2, $3, ...

$1, $2, $3, ... 分别代表命令行中/函数第1, 2, 3, ...个参数。$0 是脚本名字。在函数中$0仍然是脚本名,而不是函数名(如果脚本是用 source 命令执行,则 $0=bash)

3.2 $* 和 $@


  • $* 把所有的参数作为一个单独的字符串,其中用环境变量IFS(internal field separator)中的第一个字符作为参数之间的分隔符。
  • $@ 等价于"$1" "$2" "$3"... "$N" ,就是N个独立的用双引号引起的字符串,它们之间用空格分开。


4.变量名通用写法

完整的变量名书写格式是${varname} 。比如你有超过9个以上的参数,你必须用${10}而不是$10 来引用第10个参数。另外当你想在输出的变量后跟一下划线,你必须用:
echo ${UID}_ 
如果使用 
echo $UID_ ,shell 会把UID_ 看作变量名。

5.变量的类型

用内建命令declare 定义变量的类型,比如 declare -i varname ,表示varname 是个整数变量。 用'-'定义,用'+'取消。常用的设置变量属性的 declare option 见下表。

Option Meaning
-a to make NAMEs indexed arrays (if supported)
-A to make NAMEs associative arrays (if supported)
-i to make NAMEs have the `integer' attribute
-l to convert NAMEs to lower case on assignment
-r to make NAMEs readonly
-t to make NAMEs have the `trace' attribute

当declare 作为命令使用时还有另外一些参数:

Option Meaning
-f restrict action or display to function names and definitions
-F restrict display to function names only (plus line number and source file when debugging)
-g create global variables when used in a shell function; otherwise ignored
-p display the attributes and value of each NAME

Search This Blog