也可以从文件重定向过来
read [-options] [variable...]
echo -n "Please enter an integer -> "
read int
-n使得输出后不换行
read可以给多个变量赋值
#!/bin/bash
echo -n "enter one or more values ->"
read var1 var2 var3 var4 var5
echo "var1= '$var1'"
echo "var2= '$var2'"
echo "var3= '$var3'"
echo "var4= '$var4'"
echo "var5= '$var5'"
enter one or more values ->asb jskdj kja e
var1= 'asb'
var2= 'jskdj'
var3= 'kja'
var4= 'e'
var5= ''
[me@linuxbox ~]$ read-multiple
Enter one or more values > a b c d e f g
var1 = 'a'
var2 = 'b'
var3 = 'c'
var4 = 'd'
var5 = 'e f g'
如果read命令接受到变量值数目少于期望的数字,那么额外的变量值为空,多余的输入数据则会 被包含到最后一个变量中
enter one or more values -> abd djkj dj
REPLY= ' abd djkj dj'
read -p "enter one or more values >"
if read -t 10 -sp "enter secret pass phrase >" secret_pass; then
echo -e "\n secret pass phrase = '$secret_pass'"
else
echo -e "\n time out" >&2
exit 1
fi
将输入分隔,分发给多个变量
shell variable named IFS (for Internal Field Separator)
The default value of IFS contains a space, a tab, and a newline character, each of which will separate items from one another.
file=/etc/passwd
read -p "please input a user name -> " user_name
file_info=$(grep ^$user_name $file)
if [[ -n $file_info ]]; then
IFS=":" read user pw uid gid name home shell <<< $file_info
echo "User='$user'"
echo "pw='$pw'"
else
echo "no such user" >&2
exit 1
#statements
fi
IFS=":" read user pw uid gid name home shell <<< "$file_info"
Shell 允许在一个命令之前给一个或多个变量赋值。这些赋值会暂时改变之后的命令的环境变量, 有效时间是命令持续时间[[:digit:]]* *代表必须匹配0或多次(要区分正则表达式和shell通配符)
‘.5656’ is a floating point number.
+ 必须匹配1次或多次
function invalid_input(){
echo "invalid input '$REPLY'" >&2
exit 1
}
read -p "enter a single item -> "
[[ -z $REPLY ]] && invalid_input #empty
[[ $(echo $REPLY | wc -w) -gt 1 ]] && invalid_input #more than one
# is input a valid filename?
if [[ $REPLY =~ ^[-[:alnum:]/\._]+$ ]]; then
echo "$REPLY is a valid filename."
if [[ -e "$REPLY" ]]; then
echo "and file $REPLY exsits."
else
echo "however it doesnot exsit."
fi
else
echo "$REPLY is not a valid filename."
fi
#!/bin/bash
count=1
while [[ $count -le 5 ]]; do
echo $count
count=$((count+1))
done
echo "finished"
delay=3
while [[ $REPLY != 0 ]]; do
clear
echo "
please select:
1. display system information
2. display disk space
3. display home space utilization
0. exit
"
read -p "enter selection [0..3] ->"
if [[ $REPLY =~ ^[0-3]$ ]]; then
if [[ $REPLY == 0 ]]; then
echo "programme terminates"
exit
fi
if [[ $REPLY == 1 ]]; then
echo "hostname is $HOSTNAME"
uptime
sleep $delay
fi
if [[ $REPLY == 2 ]]; then
df -h
sleep $delay
fi
if [[ $REPLY == 3 ]]; then
du -sh $HOME
sleep $delay
fi
else
echo "invalid entry"
sleep $delay
fi
done
echo "programme terminates"
delay=3
while true; do
clear
echo "
please select:
1. display system information
2. display disk space
3. display home space utilization
0. exit
"
read -p "enter selection [0..3] ->"
if [[ $REPLY =~ ^[0-3]$ ]]; then
if [[ $REPLY == 0 ]]; then
break
fi
if [[ $REPLY == 1 ]]; then
echo "hostname is $HOSTNAME"
uptime
sleep $delay
continue
fi
if [[ $REPLY == 2 ]]; then
df -h
sleep $delay
continue
fi
if [[ $REPLY == 3 ]]; then
du -sh $HOME
sleep $delay
continue
fi
else
echo "invalid entry"
sleep $delay
continue
fi
done
echo "programme terminates"
count=1
until [[ $count -gt 5 ]]; do
echo $count
count=$((count+1))
done
echo "finished"
1 2 3 4 5
while IFS=":" read user pw uid gid name home shell; do
printf "user: %s\t pw: %s\t uid: %s\n" $user $pw $uid
done < /etc/passwd
oot@ubuntu:/code# ./file.sh
user: root pw: x uid: 0
user: daemon pw: x uid: 1
user: bin pw: x uid: 2
user: sys pw: x uid: 3
user: sync pw: x uid: 4
user: games pw: x uid: 5
user: man pw: x uid: 6
user: lp pw: x uid: 7
user: mail pw: x uid: 8
user: news pw: x uid: 9
user: uucp pw: x uid: 10
user: proxy pw: x uid: 13
#!/bin/bash
# trouble: script to demonstrate common errors
number=1
if [ $number = 1 ]; then
echo "Number is equal to 1.
else
echo "Number is not equal to 1."
fi
script.sh: line 7: unexpected EOF while looking for matching `"'
script.sh: line 9: syntax error: unexpected end of file
#!/bin/bash
# trouble: script to demonstrate common errors
number=1
if [ $number = 1 ] then
echo "Number is equal to 1."
echo "Number is not equal to 1."; then
echo what
else echo hh
fi
Number is equal to 1.
Number is not equal to 1.
what
script.sh: line 4: [: missing `]'
#!/bin/bash
# trouble: script to demonstrate common errors
number=1
if [ $number = 1 ] then
echo "Number is equal to 1."
false; then
echo what
else echo hh
fi
Number is equal to 1.
hh
script.sh: line 4: [: missing `]'
#!/bin/bash
# trouble: script to demonstrate common errors
number=
if [ $number = 1 ]; then
echo "Number is equal to 1."
else
echo "Number is not equal to 1."
fi
Number is not equal to 1.
script.sh: line 4: [: =: unary operator expected
Incorrect conditional expressions. It’s easy to incorrectly code an if/then/else and have the wrong logic carried out. Sometimes the logic will be reversed or it will be incomplete. 条件表达式错误
“Off by one” errors. When coding loops that employ counters, it is possible to overlook that the loop may require the counting start with zero, rather than one, for the count to conclude at the correct point. These kinds of errors result in either a loop “going off the end” by counting too far, or else missing the last iteration of the loop by terminating one iteration too soon. 循环终止条件错误
Unanticipated situations. Most logic errors result from a program encountering data or situations that were unforeseen by the programmer. This can also include unanticipated expansions, such as a filename that contains embedded spaces that expands into multiple command arguments rather than a single filename. 展开错误
cd $dir_name
rm *
如果dir_name不存在,就会在当前的目录执行删除操作。
make the execution of rm contingent on the success of cd:
cd $dir_name && rm *
but still leaves open the possibility that the variable, dir_name, is unset or empty, which would result in the files in the user’s home directory being deleted.
[[ -d $dir_name ]] && cd $dir_name && rm *
[[ $REPLY =~ ^[0-3]$ ]]
root@ubuntu:/code/test# touch 123
root@ubuntu:/code/test# ls
123
root@ubuntu:/code/test# touch 345
root@ubuntu:/code/test# echo $(rm *)
root@ubuntu:/code/test# ls
root@ubuntu:/code/test# touch 234
root@ubuntu:/code/test# touch 345
root@ubuntu:/code/test# echo rm *
rm 234 345
root@ubuntu:/code/test# echo *
234 345
root@ubun
if [[ -d $dir_name ]]; then
if cd $dir_name; then
echo rm * # TESTING
else
echo "cannot cd to '$dir_name'" >&2
exit 1
fi
else
echo "no such directory: '$dir_name'" >&2
exit 1
fi
exit # TESTING
#!/bin/bash -x
# trouble: script to demonstrate common errors
number=1
if [ $number = 1 ]; then
echo "Number is equal to 1."
else
echo "Number is not equal to 1."
fi
root@ubuntu:/code# ./trouble.sh
+ number=1
+ '[' 1 = 1 ']'
+ echo 'Number is equal to 1.'
Number is equal to 1.
#The leading plus signs indicate the display of the trace to distinguish them from lines of regular output.
如果想在tracing时显示行号 export PS4=$LINENO +
(不work)
通过set -x set +x部分跟踪
#!/bin/bash
# trouble: script to demonstrate common errors
number=1
set -x # Turn on tracing
if [ $number = 1 ]; then
echo "Number is equal to 1."
else
echo "Number is not equal to 1."
fi
set +x # Turn off tracing
+ '[' 1 = 1 ']'
+ echo 'Number is equal to 1.'
Number is equal to 1.
+ set +x
echo "number=$number" # DEBUG
case word in
[pattern [| pattern]...) commands ;;]...
esac
delay=3
while true; do
clear
echo "
please select:
1. display system information
2. display disk space
3. display home space utilization
0. exit
"
read -p "enter selection [0..3] ->"
case $REPLY in
0 ) echo "programme terminates"
exit
;;
1 ) echo "hostname is $HOSTNAME"
uptime
sleep $delay
continue
;;
2 ) df -h
sleep $delay
continue
;;
3 ) du -sh $HOME
sleep $delay
continue
;;
* ) echo "invalid entry"
sleep $delay
continue
;;
esac
done
The patterns used by case are the same as those used by pathname expansion. Patterns are terminated with a “)” character. Here are some valid patterns: pattern的规则与路径展开一样
#!/bin/bash
read -p "enter word > "
case $REPLY in
[[:alpha:]]) echo "is a single alphabetic character." ;;
[ABC][0-9]) echo "is A, B, or C followed by a digit." ;;
???) echo "is three characters long." ;;
*.txt) echo "is a word ending in '.txt'" ;;
*) echo "is something else." ;;
esac
It is also possible to combine multiple patterns using the vertical bar character as a separator. This creates an “or” conditional pattern. This is useful for such things as handling both upper- and lowercase characters.
#!/bin/bash
# case-menu: a menu driven system information program
clear
echo "
Please Select:
A. Display System Information
B. Display Disk Space
C. Display Home Space Utilization
Q. Quit
"
read -p "Enter selection [A, B, C or Q] > "
case $REPLY in
q|Q) echo "Program terminated."
exit
;;
a|A) echo "Hostname: $HOSTNAME"
uptime
;;
b|B) df -h
;;
c|C) if [[ $(id -u) -eq 0 ]]; then
echo "Home Space Utilization (All Users)"
du -sh /home/*
else
echo "Home Space Utilization ($USER)"
du -sh $HOME
fi
;;
*) echo "Invalid entry" >&2
exit 1
;;
esac
-n nchars return after reading NCHARS characters rather than waiting
for a newline, but honor a delimiter if fewer than
NCHARS characters are read before the delimiter
键入一个字符后立刻停止,不需要回车
#!/bin/bash
# case4-2: test a character
read -n 1 -p "Type a character > "
echo
case $REPLY in
[[:upper:]]) echo "'$REPLY' is upper case." ;;&
[[:lower:]]) echo "'$REPLY' is lower case." ;;&
[[:alpha:]]) echo "'$REPLY' is alphabetic." ;;&
[[:digit:]]) echo "'$REPLY' is a digit." ;;&
[[:graph:]]) echo "'$REPLY' is a visible character." ;;&
[[:punct:]]) echo "'$REPLY' is a punctuation symbol." ;;&
[[:space:]]) echo "'$REPLY' is a whitespace character." ;;&
[[:xdigit:]]) echo "'$REPLY' is a hexadecimal digit." ;;&
esac
Type a character > a
'a' is lower case.
'a' is alphabetic.
'a' is a visible character.
'a' is a hexadecimal digit.
accept and process command line options and arguments
The shell provides a set of variables called positional parameters that contain the individ- ual words on the command line. The variables are named 0 through 9. They can be demonstrated this way:
root@ubuntu:/code# ./trouble.sh a v c d e f tg f as ef g we r gfgdfg ad
$0 = ./trouble.sh #$0 will always contain the first item appearing on the command line, which is the pathname of the program being executed.
$1 = a
$2 = v
$3 = c
$4 = d
$5 = e
$6 = f
$7 = tg
$8 = f
$9 = as
$10_wo= $10 如果没有{},那么$1不读取0
root@ubuntu:/code# ./trouble.sh a v c d e f tg f as ef g we r gfgdfg ad
$0 = ./trouble.sh
$1 = a
$2 = v
$3 = c
$4 = d
$5 = e
$6 = f
$7 = tg
$8 = f
$9 = as
$10 = ef #${10}
$10_wo= a0
yields the number of arguments on the com-mand line:
Number of arguments: $#
#!/bin/bash
# posit-param2: script to display all arguments
count=1
while [[ $# -gt 0 ]]; do
echo "Argument $count = $1"
count=$((count + 1))
shift
done
root@ubuntu:/code# ./trouble.sh a v c d e f tg f as ef g we r gfgdfg ad
arguments 1=a
arguments 2=v
arguments 3=c
arguments 4=d
arguments 5=e
arguments 6=f
arguments 7=tg
arguments 8=f
arguments 9=as
arguments 10=ef
arguments 11=g
arguments 12=we
arguments 13=r
arguments 14=gfgdfg
arguments 15=ad
#!/bin/bash
# file_info: simple file information program
PROGNAME=$(basename $0)
if [[ -e $1 ]]; then
echo -e "\nFile Type:"
file $1
echo -e "\nFile Status:"
stat $1
else
echo "$PROGNAME: usage: $PROGNAME file" >&2
exit 1
fi
file_info () {
# file_info: function to display file information
if [[ -e $1 ]]; then
echo -e "\nFile Type:"
file $1
echo -e "\nFile Status:"
stat $1
else
echo "$FUNCNAME: usage: $FUNCNAME file" >&2
return 1
fi
}
#!/bin/bash
# posit-params3 : script to demonstrate $* and $@
print_params () {
echo "\$1 = $1"
echo "\$2 = $2"
echo "\$3 = $3"
echo "\$4 = $4"
}
pass_params () {
echo -e "\n" '$* :'; print_params $*
echo -e "\n" '"$*" :'; print_params "$*"
echo -e "\n" '$@ :'; print_params $@
echo -e "\n" '"$@" :'; print_params "$@"
}
pass_params "word" "words with spaces"
$* :
$1 = word
$2 = words
$3 = with
$4 = spaces
"$*" :
$1 = word words with spaces
$2 =
$3 =
$4 =
$@ :
$1 = word
$2 = words
$3 = with
$4 = spaces
"$@" :
$1 = word
$2 = words with spaces
$3 =
$4 =
With our arguments, both $* and @ p r o d u c e a f o u r w o r d r e s u l t : w h i c h m a t c h e s o u r a c t u a l i n t e n t . T h e l e s s o n t o t a k e f r o m t h i s i s t h a t e v e n t h o u g h t h e s h e l l p r o v i d e s f o u r d i f f e r e n t w a y s o f g e t t i n g t h e l i s t o f p o s i t i o n a l p a r a m e t e r s , “ @ produce a four word result: which matches our actual intent. The lesson to take from this is that even though the shell provides four different ways of getting the list of positional parameters, “ @produceafourwordresult:whichmatchesouractualintent.Thelessontotakefromthisisthateventhoughtheshellprovidesfourdifferentwaysofgettingthelistofpositionalparameters,“@” is by far the most useful for most situations, because it preserves the integrity of each positional parameter.
#!/bin/bash
# Program to output a system information page
PROGNAME=$(basename $0)
function write_html_page()
{
cat << _EOF_
$title
$title
$time_stamp
$(report_uptime)
$(report_disk_space)
$(report_home_space)
_EOF_
return
}
function usage()
{
echo "$PROGNAME: usage: $PROGNAME [-f file | -i]"
return
}
interactive=
filename=
#while the length of the first parameter > 0
while [[ -n "$1" ]]; do
case $1 in
-f | --file )
shift
filename=$1
;;
-i | --interactive)
interactive=1
;;
-h | --help)
usage
exit
;;
* )
usage >&2
exit 1
;;
esac
shift
done
# interactive mode
if [[ -n $interactive ]]; then
while true; do
read -p "enter a filename for output" filename
if [[ -e "$filename" ]]; then
read -p "file exist. overwrite(y/n/q) > "
case $REPLY in
y | Y)
break;
;;
n | N)
continue
;;
q|Q)
echo "Program terminates"
exit
;;
*)
continue
;;
esac
elif [[ -z filename ]]; then #字符串的长度是零
continue
else
break
fi
done
fi
if [[ -n $filename ]]; then
if touch $filename && [[ -f $filename ]]; then
write_html_page > $filename
else
echo "$PROGNAME: Cannot write file '$filename'" >&2
fi
else
write_html_page
fi