- 论坛徽章:
- 7
|
comp.unix.shell FAQ(转载http://home.comcast.net/~j.p.h/)
4. How can I remove whitespace characters within file names?
File names in unix can contain all kinds of whitespace characters,
not just spaces. The following examples only work with spaces,
adjust accordingly.
a. Use the substitution capabilities of awk, sed, et al.
f=`printf '%s\n' "$filename" | sed 's/ /_/g'`
f=`printf '%s\n' "$filename" | awk '{gsub(" ","_");print $0}'`
f=`printf '%s\n' "$filename" | tr ' ' _`
Add characters to the tr command line as needed (see the man
page for tr to find out the available escape sequences).
Additionally (although not exactly a one-liner)
f=`tr ' ' _ <<EOF
$filename
EOF
`
See section 0a "Notes about using echo" for why echo is not used
here.
b. Use the substitution capabilities of the shell if it has
them. Check the man page for your shell (probably under a
section named something like "Parameter expansion") to see. For
example:
f=${filename// /_}
With zsh:
autoload -U zmv
zmv '* *' '$f:gs/ /_/'
It should be noted that the zmv solution renames the files (call
mv internally and adress several problems that may arise) while
the other solutions only update a variable (and then, renaming
the files may involve a quite complicated script to do it
reliably).
======================================================================
5. How can I automate a telnet session?
This is outside the realm of shell programing, per se. You need
a more special purpose scripting language such as expect. See
http://expect.nist.gov/
Perl scripts can also do this with the Telnet module from CPAN.
======================================================================
6. How do I do date arithmetic?
This depends on exactly what you have in mind.
a. Finding yesterday's date
The GNU version of date has some nice features in this
respect. For example
To find yesterday's date
$ date --date yesterday
To find tomorrow's date
$ date --date tomorrow
See the man page for GNU date for other options. It can also
provide dates more than one day in the past/future.
The FreeBSD version of date also provides extensions that can
do things like this.
$ date
Wed Oct 22 13:48:29 CDT 2003
$ date -v-1d
Tue Oct 21 13:45:16 CDT 2003
Playing with the TZ variable isn't a reliable method. If you
need to do something like this, but don't have GNU or FreeBSD
date available, see section g. "Arbitrary date arithmetic".
b. Determining relative ages of files
If you want to determine whether or not one file is older than
another, you can (with bash, pdksh, ksh93) do
$ [[ file1 -ot file2 ]] && echo file1 is older
or you can use find to search a directory tree for files that
are newer/older than some file:
$ find . -name '*.c' -newer test.c
c. Finding elapsed time
If you want to find elapsed time, perhaps because you want to
know when some operation has timed out, some shells (bash, ksh
zsh [,??]) have a SECONDS variable which tell how many seconds
have elapsed since the invocation of the shell, or since the
last time it was set.
ksh93 has a floating point SECONDS which is locale dependent.
In zsh 4.1 and above one can be made floating point with: float
SECONDS
zsh 4.1 and above also has $EPOCHSECONDS for seconds since
1970-1-1 0:0:0 UTC (see zsh/datetime module).
d. Determining leap year
A leap year in the Gregorian calendar is defined as a year which
is evenly divisible by 4, however, if it's also evenly divisible
by 100 then it's not a leap year unless it's also evenly
divisible by 400. It gets worse than that, actually, but this
is as far as I go :-).
In the Julain calendar which was used before in Europe, only the
years divisible by 4 where leap years.
The standard "cal" utility performed the switch between Julian
and Gregorian calendar in september 1752 (see cal 9 1752) which
corresponds to the date used in England. The Gregorian calendar
(created by Pope Gregory III) was first used in 1582 in many
other countries.
One possibility for a ksh function to do this (after 1600 AD/CE)
is
isleap()
{
y=$1
four=$(( $y % 4 ))
hundred=$(( $y % 100 ))
fourhundred=$(( $y % 400 ))
if [ $four -eq 0 ];then
if [ $hundred -eq 0 ];then
if [ $fourhundred -eq 0 ];then
echo leap year
else
echo not a leap year
fi
else
echo leap year
fi
else
echo not a leap year
fi
}
Or, valid with any date with the same calendar switch day as
POSIX cal's (POSIX syntax):
is_leap_year() # args: year
# NB: year before year 1 is year -1, not 0.
{
[ "$1" -lt 0 ] && set -- "$(($1 + 1))"
[ "$(($1 % 4))" -eq 0 ] && {
[ "$(($1 % 100))" -ne 0 ] || [ "$(($1 % 400))" -eq 0 ] \
|| [ "$1" -le 1752 ]
}
}
Or in any Bourne shell (see COPYING[1]):
is_leap_year() { ## USAGE: is_leap_year [year]
isl_year=${1:-`date +%Y`}
case $isl_year in
*0[48] |\
*[2468][048] |\
*[13579][26] |\
*[13579][26]0|\
*[2468][048]00 |\
*[13579][26]00 ) _IS_LEAP_YEAR=1
return 0 ;;
*) _IS_LEAP_YEAR=0
return 1 ;;
esac
}
On FreeBSD, use the -f option to date(1) to pass in the
(supposed) February 29 in the current year and then print it the
day of the month again to see if there really is such a date
(note that you need -j as well as -f, otherwise date(1) thinks
you want to set the clock):
if [ $(date -jf%Y%m%d $(date +%Y0229) +%d) = 29 ]; then
echo Leap year!
fi
e. Determining the last day of a month.
There are a number of possibilities for doing this which have
been mentioned in the group. The following is a sampling:
In any Bourne-type shell (in conjunction with is_leap_year() as
given above, when month is February) (see COPYING[1]):
days_in_month() { ## USAGE: days_in_month [month [year]]
if [ -n "$1" ]
then
dim_m=$1
dim_y=$2
else
eval `date "+dim_m=%m dim_y=%Y"`
fi
case $dim_m in
9|09|4|04|6|06|11) _DAYS_IN_MONTH=30 ;;
1|01|3|03|5|05|7|07|8|08|10|12) _DAYS_IN_MONTH=31 ;;
2|02) is_leap_year ${dim_y:-`date +%Y`} &&
_DAYS_IN_MONTH=29 || _DAYS_IN_MONTH=28 ;;
esac
[ ${SILENT_FUNCS:-0} -eq 1 ] || echo $_DAYS_IN_MONTH
}
With GNU date:
year=2003
month=9
date -d "$year/$month/1 +1 month -1 day" +%d
With FreeBSD date use the -v-1d option to date(1) to get the day
before the first day of the next month:
$ MONTH=12
$ date -v-1d -jf%Y-%m-%d $(date +%Y-$(((MONTH+1)%12))-01) +%d
31
In the shell using cal (But beware of implementations of cal
which print more than one month at a time):
month=9 ; year=2003 # adjust
##
for lday in `cal $month $year` ; do : ; done
echo $lday
## or
set -- `cal $month $year` ; eval lday=\${$#}
echo $lday
In ksh, bash and zsh:
: $(cal)
days_in_month=$_
In zsh:
days_in_month=${$(cal)[-1]}
f. Determining the day of the week for a given date.
This algorithm is known as Zeller's congruence. An explanation
of it is available from the Dictionary of Algorithms and Data
Structures web page at NIST:
http://www.nist.gov/dads/
Also, a fuller explanation is available at
http://www.merlyn.demon.co.uk/zeller-c.htm#ZC
An example in C, with a short explanation, is given at
http://wwwcdf.pd.infn.it/MLO/Calendars/Notes.html#zeller
A shell (ksh93) implementation of a homework assignment (given
for illustration only - don't turn this in as yours - you might
be sorry if it's wrong :-)
dayofweek()
{
# Implementation of a homework assignment given at
# http://carbon.cudenver.edu/~traup/fa02/lec/hw3.html
#
# call with day: 1 - 31
# month: March = 1, Jan & Feb are months 11 and
# 12 of the previous year.
# year: The year of the century
# c: The previous century
#
# For example, for July 4, 1989,
# m = 5, d = 4, y = 89, and c = 19,
# while for January 25, 1989,
# m = 11, d = 25, y = 88, and c = 19.
#
# The output is the day of the week with Sunday = 0,
# Monday = 1, etc.
d=$1
m=$2
y=$3
c=$4
A=$(( ($m * 13 - 1) / 5 ))
B=$(( $y / 4 ))
C=$(( $c / 4 ))
D=$(( $A + $B + $C + $d + $y - ($c * 2) ))
echo $(( $D % 7 ))
}
On FreeBSD, use the -f option to date(1) to pass in the date of
interest and +%A to print the day of the week:
$ date -jf%Y-%m-%d 2000-01-01 +%A
Saturday
(Use +%u or +%w if you want the weekday as a number. See the
strftime(3) manpage for details.)
g. Arbitrary date arithmetic
To do arbitrary date calculations is more complicated. One
possibility is to call an external utility, or a program in
another scripting language, which has this built in. For
example, perl has wrappers for the unix time functions built in,
so it can provide some relief in this regard. C programs can
also be easily written to do date arithmetic (see the examples
section). One thing to keep in mind, however, is that unix time
functions are, strictly speaking, limited to the range of time
between January 1 1970 at midnight, and January 19, 2038 at
3:14:07. C/Perl programs which calculate dates outside this
range might work, or they might not, that would depend on the
implementation.
To do arbitrary date arithmetic in the shell itself is also
possible. An article provided on the web by SysAdmin magazine
describes one way to do this.
http://www.samag.com/documents/s=8284/sam0307b/0307b.htm
Another possibility is given in the examples section, from
http://groups.google.com/groups? ... f%40ogion.it.jyu.fi
See also
http://groups.google.com/groups? ... las@spam.is.invalid
On FreeBSD, the -f and -v options to date(1) cover most things
you might want to do, with the caveat that only dates within the
range mentioned above are defined. Dates outside that range are
not guaranteed to work.
Also, zsh 4.1 and above has the zsh/datetime module that
provides the $EPOCHSECONDS and the strftime function.
h. Getting the number of seconds since the epoch
- GNU date has the %s format option which returns the epoch
time.
- More portably, use awk.
awk 'BEGIN {srand(); printf("%d\n", srand())}'
This works because srand() sets its seed value with the
current epoch time if not given an argument. It also returns
the previous seed value, so the second call gives the epoch
time.
Note that this doesn't work with older versions of awk. This
requires a version supporting the POSIX spec for srand(). For
example, on Solaris this will not work with /usr/bin/awk, but
will with nawk or /usr/xpg4/bin/awk.
Depending on scheduling, when the call is actually executed,
etc, this might be off by a second.
- Another way is to use perl if you have it.
perl -le 'print time'
- Also, zsh 4.1 and above has the zsh/datetime module that
provides the $EPOCHSECONDS and the strftime function.
======================================================================
[ 本帖最后由 r2007 于 2005-12-3 17:44 编辑 ] |
|