-
Notifications
You must be signed in to change notification settings - Fork 3
/
bashref.tex
3451 lines (2802 loc) · 345 KB
/
bashref.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
% vim:fmr=[[[,]]]:fdm=marker
% by Jerry Fleming <jerryfleming2006@gmail.com>
% on 2013-03-25.
% All rights reserved.
% coding: utf8
% preamble [[[
\documentclass[openany,notitlepage]{book}
% [[[ packages
\usepackage{makeidx}
\usepackage{CJKutf8}
%\usepackage[utf8]{inputenc}
\usepackage{CJKnumb}
\usepackage{CJKpunct}
\usepackage{nameref}
\usepackage{color}
\usepackage{float} % for [H], must comes before hyperref
\usepackage{hyperref}
\usepackage{fancyhdr}
\usepackage{fancybox}
\usepackage{titlesec}
\usepackage{titletoc}
\usepackage[titles]{tocloft} % for List of Notes
\usepackage[chapter]{tocbibind} % to include to[clf] in toc
\usepackage{appendix}
\usepackage{multicol} % for \theindex
\usepackage{fullpage}
\usepackage{graphicx}
\usepackage{eso-pic} % for fullpage covers
\usepackage{tikz}
\usepackage{pifont} % for \ding
\usepackage{datetime} % to renew \today
\usepackage{indentfirst}
\usepackage{keystroke}
% packages ]]]
\makeindex
% pdf styles [[[
\hypersetup{
bookmarksopen=true,
backref=true,
unicode=true, % non-Latin characters in Acrobats bookmarks
pdftoolbar=true, % show Acrobat's toolbar?
pdfmenubar=true, % show Acrobat's menu?
pdffitwindow=false, % page fit to window when opened
pdftitle={Bash 参考手册}, % title
pdfauthor={邵加超 Jerry Fleming}, % author
pdfsubject={company}, % subject of the document
pdfnewwindow=true, % links in new window
pdfkeywords={Bash,邵加超,Jerry Fleming,参考手册,中文文档,Linux,命令}, % list of keywords
colorlinks=true, % false: boxed links; true: colored links
linkcolor=red, % color of internal links
citecolor=green, % color of links to bibliography
filecolor=magenta, % color of file links
urlcolor=cyan, % color of external links
plainpages=true % use formatted page number
}
% this is to fix a bug in hyperref, which hornors linktocpage option as a boolean wrongly
\makeatletter
\let\Hy@linktoc\Hy@linktoc@all
\makeatother
% pdf styles ]]]
% [[[ colors
\definecolor{row}{rgb}{.9,.9,.9}
\definecolor{skyblue}{RGB}{97,203,222}
% colors ]]]
% [[[ headers
\addtolength{\voffset}{-.6in}
\setlength{\headsep}{10pt}
\setlength{\footskip}{45pt}
\setlength{\headheight}{40pt}
\renewcommand{\headrulewidth}{.2pt}
\renewcommand{\footrulewidth}{.2pt}
\renewcommand{\footruleskip}{2pt}
\setlength{\arrayrulewidth}{.2pt}
\fancypagestyle{myfancy}{
\fancyhead[C]{
\setlength{\unitlength}{1in}
\begin{picture}(0,0)
\color[gray]{.95}
\put(-1,-5){\makebox(2, 2){\rotatebox{45}{\scalebox{10}{\Large\CJKfamily{gkai}{草稿}}}}}
\end{picture}
}
\fancyhead[RO,LE]{\leftmark}
\fancyhead[RE,LO]{\rightmark}
\fancyfoot[LE]{\textcolor{gray}{\raisebox{-.35\height}{\includegraphics{geofuture_h.png}} BASH 中文文档}}
\fancyfoot[RO]{\textcolor{gray}{BASH 中文文档 \raisebox{-.35\height}{\includegraphics{geofuture_h.png}}}}
\fancyfoot[C]{\nameref{content}}
\fancyfoot[RE,LO]{
\begin{picture}(30,5)
\color{skyblue}
\put(0,0){\scalebox{2.5}[1]{\circle*{15}}}
\color{black}
\put(0,0){\makebox(0,0){\textit{p\thepage}}}
\end{picture}
}
\renewcommand{\headrule}{
\begin{picture}(430,10)(10,-5)
\color{skyblue}
\put(0,0){\circle*{10}}
\multiput(13,0)(10,0){49}{\circle*{6}}
\end{picture}
}
\renewcommand{\footrule}{
\begin{picture}(430,10)(10,0)
\color{skyblue}
\put(492,0){\circle*{10}}
\multiput(0,0)(10,0){49}{\circle*{6}}
\end{picture}
\vskip 3pt
}
}
\fancypagestyle{myplain}{
\fancyhead[C]{
\setlength{\unitlength}{1in}
\begin{picture}(0,0)
\color[gray]{.95}
\put(-1,-5){\makebox(2, 2){\rotatebox{45}{\scalebox{10}{\Large\CJKfamily{gkai}{草稿}}}}}
\end{picture}
}
\fancyhead[RO,LE]{\leftmark}
\fancyhead[RE,LO]{\rightmark}
\fancyfoot[LE]{\textcolor{gray}{\raisebox{-.35\height}{\includegraphics{geofuture_h.png}} BASH 中文文档}}
\fancyfoot[RO]{\textcolor{gray}{BASH 中文文档 \raisebox{-.35\height}{\includegraphics{geofuture_h.png}}}}
\fancyfoot[C]{}
\fancyfoot[RE,LO]{
\begin{picture}(30,5)
\color{skyblue}
\put(0,0){\scalebox{2.5}[1]{\circle*{15}}}
\color{black}
\put(0,0){\makebox(0,0){\textit{p\thepage}}}
\end{picture}
}
\renewcommand{\headrule}{
\begin{picture}(430,10)(10,-5)
\color{skyblue}
\put(0,0){\circle*{10}}
\multiput(13,0)(10,0){49}{\circle*{6}}
\end{picture}
}
\renewcommand{\footrule}{
\begin{picture}(430,10)(10,0)
\color{skyblue}
\put(492,0){\circle*{10}}
\multiput(0,0)(10,0){49}{\circle*{6}}
\end{picture}
\vskip 3pt
}
}
\fancypagestyle{plain}{
\fancyhead[C]{
\setlength{\unitlength}{1in}
\begin{picture}(0,0)
\color[gray]{.95}
\put(-1,-5){\makebox(2, 2){\rotatebox{45}{\scalebox{10}{\Large\CJKfamily{gkai}{草稿}}}}}
\end{picture}
}
\fancyhead[RO,LE]{\leftmark}
\fancyhead[RE,LO]{\rightmark}
\fancyfoot[LE]{\textcolor{gray}{\raisebox{-.35\height}{\includegraphics{geofuture_h.png}} BASH 中文文档}}
\fancyfoot[RO]{\textcolor{gray}{BASH 中文文档 \raisebox{-.35\height}{\includegraphics{geofuture_h.png}}}}
\fancyfoot[C]{}
\fancyfoot[RE,LO]{
\begin{picture}(30,5)
\color{skyblue}
\put(0,0){\scalebox{2.5}[1]{\circle*{15}}}
\color{black}
\put(0,0){\makebox(0,0){\textit{p\thepage}}}
\end{picture}
}
\renewcommand{\headrule}{
\begin{picture}(430,10)(10,-5)
\color{skyblue}
\put(0,0){\circle*{10}}
\multiput(13,0)(10,0){49}{\circle*{6}}
\end{picture}
}
\renewcommand{\footrule}{
\begin{picture}(430,10)(10,0)
\color{skyblue}
\put(492,0){\circle*{10}}
\multiput(0,0)(10,0){49}{\circle*{6}}
\end{picture}
\vskip 3pt
}
}
% headers ]]]
% [[[ sections and titles
\renewcommand{\contentsname}{目录}
\renewcommand{\indexname}{索引}
\renewcommand{\appendixname}{附录}
\renewcommand{\appendixpagename}{\appendixname}
\renewcommand\appendixtocname{\appendixname}
\titleformat{\chapter}{\Huge\center}{第\CJKnumber{\thechapter}章}{20pt}{}[\rule{.6\textwidth}{4pt}]
\pagestyle{fancy}
\renewcommand{\theparagraph}{\Alph{paragraph}}
\titleformat{\paragraph}{\large}{\raisebox{-2pt}{\rule{10pt}{12pt}} \theparagraph .}{10pt}{}[\vspace{\parskip}]
\setlength{\cftparanumwidth}{15pt}
\newcommand{\xquad}{\hspace{1em plus.4em minus.4em}}
%\titlecontents*{paragraph}[.3\textwidth]{\small}{\P\thecontentslabel\hspace{4pt}}{}{,\hspace{4pt}\textit{p\thecontentspage}}[][\xquad ---\xquad ][.]
\setcounter{secnumdepth}{5} % include \paragraph as well
\setcounter{tocdepth}{5} % include \paragraph as well
\renewcommand{\cftchappresnum}{第}
\renewcommand{\cftchapaftersnum}{章}
\renewcommand{\cftsecpresnum}{\S}
\setlength{\cftsecnumwidth}{3.1em}
\renewcommand{\cftsubsecpresnum}{\S}
\renewcommand{\cftsubsubsecpresnum}{\S}
\setlength{\cftchapnumwidth}{40pt}
\titlelabel{\S\,\thetitle\quad}
\renewcommand{\cftaftertoctitle}{\vspace{2pt}\rule{.6\textwidth}{4pt}}
\renewcommand{\dateseparator}{-}
\renewcommand{\today}{\the\year \dateseparator \twodigit\month \dateseparator \twodigit\day}
\setlength{\parindent}{2em}
% sections and titles ]]]
% faq [[[
\newlistof{faqs}{faq}{常见问题}
\newcommand{\question}[1]{%
\ifnum\value{faqs} > 0
\label{faq:\thefaqs}%
\addcontentsline{faq}{faqs}{\protect\numberline{\thefaqs}#1}%
\fi
\noindent\begin{tikzpicture}[remember picture] \node [xshift=1cm,yshift=1cm] [fill=red!20,rounded corners] {
\raisebox{-1.1em}{%
{\Huge\CJKfamily{gkai}{\color{red}问}}%
\raisebox{1em}{\color{red}\large\thefaqs .}%
}
\begin{minipage}[t]{.9\textwidth}#1\end{minipage}%
};
\end{tikzpicture}%
\par
\refstepcounter{faqs}
}
\newenvironment{answer}[1]{%
%\par\noindent\hangindent=3em \hangafter=1
%\leavevmode{%
% \hbox{\raisebox{-.7\height}[0pt][0pt]{\CJKfamily{gkai}{\Huge\color{red}答}}}}
\par#1
}
{\vskip 5pt}
% faq ]]]
% misc [[[
% item label
\renewcommand{\thefootnote}{\textcolor{red}{[\arabic{footnote}]}}
\renewcommand{\descriptionlabel}[1]{\hspace{\labelsep}\thinlines\setlength{\fboxsep}{0pt}\fbox{\,\raisebox{-1ex}{\textcolor{white}{\rule{1px}{3.5ex}}}\textbf{#1}\,}\hspace{10pt}}
\renewcommand{\labelitemi}{\ding{43}}
\renewcommand{\labelenumi}{
\begin{picture}(10,10)(-5,-2)
\color{gray}
\put(0,0){\circle*{15}}
\color{white}
\put(0,0){\makebox(0,0){\textit{\theenumi}}}
\end{picture}
}
% inline pic
\newcommand{\inlinepic}[1]{%
\lower -.5ex\hbox{%
\raisebox{.5\totalheight}{\includegraphics{#1}}
}
}
\makeatletter
\def\flushboth{%
\let\\\@normalcr
\@rightskip\z@skip \rightskip\@rightskip
\leftskip\z@skip
\parindent 2em\relax}
\makeatother
\renewcommand{\textasciitilde}{\path+~+}
% misc ]]]
% note [[[
\newcommand{\note}[3][info]{%
\vskip 5pt
\begin{tikzpicture}[remember picture] \node [xshift=1cm,yshift=1cm] [draw=red,thick,rounded corners] {
\begin{minipage}{.9\textwidth}
\raisebox{-5pt}{\includegraphics[width=20pt]{box_#1.jpg}} {\large\CJKfamily{gkai}{\color{red}#2}}
\vskip 5pt
#3
\end{minipage}
};
\end{tikzpicture}
}
% note ]]]
% [[[ codeline
\newcommand{\codelinenum}[1]{\par\rule{.1\textwidth}{.3pt} \textit{p\pageref{codeline:#1}} \rule{.1\textwidth}{.3pt}\par\vskip 20pt}
\newcommand{\codelinetext}[1]{\par\noindent#1}
\makeatletter
\newcounter{codeline}
\newcommand{\codeline}[2]{
\begin{center}
\begin{tikzpicture}[remember picture] \node [xshift=1cm,yshift=1cm] [text width=.8\textwidth,fill=green!20,rounded corners] {
\begin{minipage}{.97\textwidth}#1\end{minipage}
};
\ifnum\value{csts} > 0
\label{codeline:\thecodeline}
\protected@write\@auxout{}{%
\string\@writefile{line}{\string\codelinetext{#1}}
\string\@writefile{line}{\string\codelinenum{\thecodeline}}
}%
\fi
\end{tikzpicture}
\end{center}
\refstepcounter{codeline}
}
\makeatother
% ]]]
% [[[ codelist
\newcommand{\listcstsname}{代码列表}
\newlistof{csts}{cst}{\listcstsname}
\newenvironment{codelist}[1]
{
\ifnum\value{csts} > 0
\label{cst:\thecsts}
\addcontentsline{cst}{csts}{\protect\numberline{\thecsts}#1}
\fi
\begin{quotation}\noindent%
\begin{tikzpicture}[remember picture] \node [xshift=1cm,yshift=1cm] [text width=.9\textwidth,fill=row] {
\textcolor{red}{\ding{107} 代码清单 \thecsts :#1}
};
\end{tikzpicture}\par\noindent\tt{}%
}
{
\par\noindent\normalfont%
\begin{tikzpicture}[remember picture] \node [xshift=1cm,yshift=1cm] [text width=.9\textwidth,fill=row] {};
\end{tikzpicture}%
\end{quotation}
\refstepcounter{csts}
}
\newcommand{\codeblock}[1]{\begin{center}\begin{minipage}{.7\textwidth}{\tt #1}\end{minipage}\end{center}}
\newcommand{\codeConstant}[1]{\textcolor[RGB]{0,205,0}{\texttt{#1}}}
\newcommand{\codeComment}[1]{\textcolor[RGB]{255,96,96}{\texttt{#1}}}
\newcommand{\codeIdentifier}[1]{\textcolor[RGB]{0,255,255}{\texttt{#1}}}
\newcommand{\codeSpecial}[1]{\textcolor[RGB]{255,20,147}{\texttt{#1}}}
\newcommand{\codeOperator}[1]{\textcolor[RGB]{128,128,255}{\texttt{#1}}}
\newcommand{\codePreProc}[1]{\textcolor[RGB]{205,0,205}{\texttt{#1}}}
\newcommand{\codeRepeat}[1]{\textcolor[RGB]{255,255,255}{\texttt{#1}}}
\newcommand{\codeStatement}[1]{\textcolor[RGB]{0,0,255}{\texttt{#1}}}
\newcommand{\codeType}[1]{\textcolor[RGB]{20,0,255}{\texttt{#1}}}
\newcommand{\codelnr}[1]{\textcolor[RGB]{165,42,42}{\scriptsize\texttt{#1}}}
% ]]]
% [[[ context commands
\newcommand{\mnemonic}[1]{\textcolor{gray}{{\scriptsize\tt 助记词: #1}}}
\newcommand{\code}[1]{\textcolor{blue}{{\tt #1}}}
\newcommand{\hl}[1]{{\sl #1}}
\newcommand{\hs}[1]{{\textcolor{violet}{#1}}}
% seperate the paragraph end from the rest of upper layer section
\newcommand{\stopparagraph}{\par\vspace{5pt}\noindent\raisebox{-2pt}{\rule{10pt}{12pt}} {\bf [分节结束]}\vspace{5pt}}
\newcommand{\tab}{\mbox{\ \ \ \ }}
% context commands ]]]
% [[[ refs and labels
% reference to subparts of \lebelsection will display the section title/label instead of the ones for various subparts
\def\currentlabeltitle{}
\def\currentlabelnum{}
\newcommand{\labelsection}[1]{\def\currentlabeltitle{#1}\section{#1}\def\currentlabelnum{\thesection}}
\newcommand{\labelsubsubsection}[1]{\def\currentlabeltitle{#1}\subsubsection{#1}\def\currentlabelnum{\thesubsubsection}}
\makeatletter
\def\ulabel#1{%
\@bsphack
\begingroup
\protected@write\@auxout{}{%
\string\newlabel{#1}{%
{\currentlabelnum}%
{\thepage}%
{\currentlabeltitle\,\, \@currentlabelname}%
{\@currentHref}{}%
}%
}%
\endgroup
\@esphack
}%
\makeatother
\newcommand{\fullref}[1]{\,\S\,\ref{#1}[\nameref{#1}], \textit{p\pageref{#1}}\,}
% ]]]
% preamble ]]]
\begin{document}
\begin{CJK}{UTF8}{song}
% front issues [[[
\frontmatter
\pagenumbering{alph}
\setcounter{page}{1}
\pagestyle{empty}
\begin{titlepage}
\AddToShipoutPicture*{\put(0,-43.4){\includegraphics[width=\paperwidth,height=\paperheight]{frontcover.png}}}
\
\end{titlepage}
%\begin{titlepage}
\thispagestyle{empty}
\begin{center}\ \end{center}
\begin{center}
\scalebox{2}{\textcolor{red}{\Huge\CJKfamily{gkai}{BASH 中文文档}}}
\vskip 10pt
\rule{.7\textwidth}{4pt}
\vskip 30pt
{\Large Chet Ramey, Brian Fox} {\Large\textsl{著}}
{\Large 邵加超(Jerry Fleming)} {\Large\textsl{译注}}
\vskip 10pt
{\Large 最后更新于 \today}
\vfill
\includegraphics[width=100px]{geofuture.png}
\vskip 15pt
{\Large\code{Bash} 4.0 参考文档}\\
\vskip 5pt
\href{mailto:jerryfleming2006@gmail.com}{\Large jerryfleming2006@gmail.com}
\end{center}
%\end{titlepage}
\newpage
\begin{center}\ \end{center}
\par
\vskip 80pt
\begin{center}
{\Large 献给}\\
我的妻子和孩子们\\
感谢他们的谅解,使我有足够的业余时间翻译完这本书
\end{center}
\newpage
\pagenumbering{roman}
\setcounter{page}{1}
\pagestyle{myplain}
\renewcommand{\chaptermark}[1]{\markboth{#1}{}}
\renewcommand{\tocetcmark}[1]{\markboth{#1}{}}
\chapter{译者序}\label{test}
人们在学习新事物时往往急于看到它的全貌,而看到全貌时却又失之于细节。学习和使用 Bash 尤其如此。尽管它是 Linux 的首选 shell,但是因为它与我们所熟知的``高级语言''是如此不一致,以至于即使是从业多年的老程序员,写起 shell 程序来也会觉得力不从心。而 Bash 最权威的参考资料,即其程序作者所著的《Bash Reference Manual》,又因为语言的问题不能尽为中国程序员和 IT 从业人士所知悉。所以在很多时候,我们对刚刚崛起的 Linux 只能望洋兴叹。我正是试图改变这种现状才翻译这本书的。事实上,最初促使我翻译的动机并不是来自 Bash 本身,而是来自 \LaTeX{}。在学习 \LaTeX{} 多年以后,我很想做出一些东西来,于是就选择了翻译这本书,因为我使用 Bash 有七八年了,颇有一些心得。这些心得会以脚注或或者备注等形式体现出来。
为了准确的呈现原文的内容,本文在很多地方都做了特殊处理。首先是某些地方中文和英语的表达习惯不一样,为了使中文更具可读性,在不改变原意的情况下,我对原来的语句做了适当的调整。有些地方还加了脚注。原文是用 \code{texinfo} 来排版的;这便于在计算机上阅读,而排版并翻译成中文却有诸多不便。所以我对原稿的显示风格做了大量的改动,使得翻译出来的中文版更方便阅读和打印。此外,本书原文中有很多地方都对命令进行交叉引用,而命令又很多,只找到章节标题并不能很快的定位到想要查看的命令。所以在交叉引用命令时,我大都做一些改动,以方便读者。
每个页面的页脚中都中指向目录的链接;这样如果在电脑上阅读本书而不是打印,将会非常方便。\hl{1234567890} 是一个数字。\Return\ 是键盘上的一个键;有的键,例如 \keystroke{C-a} 需要和 \keystroke{Control} 等其它键一起输入。\code{abc} 是代码行中的一个或多个单词,可以在命令行中输入或写入到文件中。而 \mnemonic{Mnemonic, 助记词} 是用来帮助记忆命令或选项的;这些助记词大多数情况下都是来自它所解释对象的英文表达,有些时候则纯粹是为了记忆写臆造的。另外,本书中的交叉引用都是采用``章节号 标题 页码''的完整格式,例如\fullref{test}。
\codeblock{\textsl{\small{这是应该在命令行中输入的文本。}}}
\codeline{这段是命令格式的说明。其中的\hs{高亮字体}部分将在后面的详细讲解中用到。所有的命令格式都在附录中列出。}
\noindent\note[info]{你知道吗?}{这是和上下文相关的作息。}
\note[tip]{使用小窍门}{这是一些小窍门。}
\note[forbid]{注意事项}{这是一些典型的错误用法。}
\begin{codelist}{代码清单说明}%
{\scriptsize 这是一些可以直接使用的代码片段。它采用了 Vim 形式的高亮显示,并且加上了行号。}
\end{codelist}
\question{这是``常见问题''中的问题与答案。}
本书是在 Gentoo Linux 下的 TexLive 环境中使用 Vim 编辑的。成书以后,用 CTexLive2008 最后编译了一次,因为那里的字体好看~(感谢 CTexLive2008 的制作者们)。
本书尚未译完时就有很多读者朋友要求印刷,因为在电脑上看书很伤眼睛。我也正在考虑这件事。如果您愿意购买纸质版本,请尽早\href{mailto:jerryfleming2006@gmail.com}{发邮件}和我联系。
\vskip 30pt
\hfill\begin{minipage}{.3\textwidth}
\begin{center}
译者\\
2009年秋
\end{center}
\end{minipage}
\begin{center}
\tableofcontents
%\addcontentsline{toc}{chapter}{\contentsname}
\label{content}
\chapter{\listcstsname}
\makeatletter
\@starttoc{cst}
\makeatother
\end{center}
\mainmatter
\pagenumbering{arabic}
\setcounter{page}{1}
\pagestyle{myfancy} % this dummy line is required by \chaptermark and \sectionmark
% front issues ]]]
\renewcommand{\chaptermark}[1]{\markboth{第\CJKnumber{\thechapter}章 \MakeUppercase{#1}}{}}
\renewcommand{\sectionmark}[1]{\markright{\S\,\thesection\ \MakeUppercase{#1}}}
\chapter{总体介绍} % [[[
\section{什么是 Bash?} % [[[
Bash 是一个用于 GNU\footnote{即 Linux。} 操作系统的 shell,也就是命令解释器。这个名字是``Borune-Again SHell''\footnote{意为``波恩 shell 的再生''。}\index{shell!波恩 shell}的缩略词,意在调侃斯蒂芬\textbullet 波恩;他写的 \code{sh} 是目前 Unix 命令行解释器的前身,最初出现于被贝尔实验室研究用 Unix 的第七版。
Bash 整体上保持与 \code{sh} 兼容,并且从科恩 shell \code{ksh}\index{shell!ksh} 和 C shell \code{csh}\index{shell!csh} 引进了一些有用的功能。它的设计力求遵循 IEEE POSIX 规范中的《Shell 和实用工具》一节~( IEEE 标准第 1003.1 号)~的规范,并且在交互和编程运行两方面对 \code{sh} 做了功能上的改进。
虽然 GNU 操作系统还提供了其它 shell,包括 \code{csh} 的一个版本,但 Bash 是默认的 shell。此外,Bash 和其它 GNU 程序一样,具有很好的移植性。它目前几乎能在任何版本的 Unix 和一些其它操作系统上运行;并且在 MS-DOS,OS/2 和 Windows 等平台上还有独立维护的移植版本。
% ]]]
\section{什么是 Shell?} % [[[
从本质上来说,shell 是一个能执行各种命令的宏处理器。这里,宏处理是指扩展文本和符号以创建更大的表达式的功能。
Unix shell 不仅是一个命令解释器,还是一种编程语言。作为命令解释器,shell 提供了包含众多 GNU 实用工具的用户界面。可编程的特性使得这些实用工具能够被组织起来。可以创建包含若干命令的文件,而这些文件本身又可以作为命令。这些命令和 \code{/bin} 等目录下的系统命令具有同等的地位,从而使得用户和用户组能定制运行环境并自动完成他们的常规任务。
Shell 还提供了少量的内部命令~(称为``builtin''),它们实现的功能是外部工具不方便或者不可能完成的。例如,\code{cd}、\code{break}、\code{continue} 和 \code{exec},它们不能通过 shell 以外的方式实现,因为它们要直接操纵 shell 本身。而诸如 \code{history}、\code{getopts}、\code{kill} 或 \code{pwd} 等内部命令,虽然可以在外部单独实现,但是作为内部命令会更便于使用。所有这些内部命令都将在后续章节中介绍。
虽然执行命令是其关键任务,shell 的强大~(和灵活)~之处却在于其中内置了编程语言。和其它高级语言一样,shell 提供了变量、流程控制结构、引用\footnote{这里是指字符串周围的引号~(quotes),而不是如 C++ 中那样的对象地址的引用 --- 后者叫 reference。}和函数。
Shell 提供了一些专为交互式使用而设计的功能,它们不是为了增强 shell 的编程特性。这些交互式的功能包括作业控制、命令行编辑、命令行历史以及~(命令)~别名。所有这些功能都将在本手册中一一介绍。
% ]]]
% chapter ]]]
\chapter{术语定义} % [[[
在本手册的全文中使用了下面的定义:
\begin{description}
\item[POSIX]\index{POSIX} 基于 Unix 的一系列操作系统可移植性\footnote{原文``open system''主要是指面向可移植性的``开放性'',故这里采取意译。}的标准。Bash 主要和 POSIX 标准第1003.1号中的《Shell 和实用工具》部分有关。
\item[空白符]\index{空白符} 一个空格或者制表符。
\item[内部命令]\index{命令!内部命令} 在 shell 内部而不是文件系统中由某个可执行文件实现的一些命令。
\item[控制运算符]\index{运算符!控制运算符} 实现控制功能的一些符号,包括换行符\footnote{即 newline。在 Windows 上面,它是 \code{\textbackslash n\textbackslash r};在 Linux 上是 \code{\textbackslash n};在 Mac 上是 \code{\textbackslash r}。} 和下面的任意一个符号:\code{||}、\code{\&\&}、\code{\&}、\code{;}、\code{;;}、\code{|}、\code{|\&}、\code{(} 或 \code{)}。
\item[退出状态]\index{退出状态|see{返回状态}} 命令返回给调用者的一个值。这个值不得超过八位\footnote{这里指的是二进位数,尽管实际返回的是十进制数。},所以其最大值是 255。
\item[字段]\index{字段} 执行某个 shell 扩展后所得到的文本的一个部分。执行一个命令时,经过 Shell 扩展后得到的各字段分别作为命令的名称和参数。
\item[文件名]\index{文件名} 用以标志一个文件的字符串。
\item[作业]\index{作业} 组成一个管道的一系列进程,以及其衍生出的进程;这些进程都属于同一个进程组。
\item[作业控制]\index{作业!作业控制} 用户可以有选择的终止~(挂起)~和重启~(恢复)~进程执行的一种机制。
\item[元字符]\index{元字符} 当没有引用时能够分隔开单词的字符。包括空白符和下面的字符之一:\code{|}、\code{\&}、\code{;}、\code{(}、\code{)}、\code{<} 以及 \code{>}。
\item[名称]\index{名称|see{标志符}} 只包括数字、字母、下划线,并且以字母或下划线开头的单词。这些名称用作变量和函数的名称,又叫做标志符。
\item[运算符]\index{运算符} 包括控制运算符和重定向运算符。重定向运算符列表请参见\fullref{redirection}。它至少包括一个未被引用的元字符。
\item[进程组]\index{进程组} 一系列拥有相同进程组号 的相关的进程。
\item[进程组号]\index{进程组号} 在进程组的生命周期内,能唯一代表该组的一个标志符。
\item[保留字]\index{保留字} 对 shell 来说具有特殊意义的一些单词;它们大部分是用来构建 shell 的控制结构的,例如 \code{for} 和 \code{while}。
\item[返回状态]\index{返回状态|see{退出状态}} 退出状态的同义词。
\item[信号]\index{信号} 当系统中发生某个事件时,内核用以通知~(用户)~进程的一种机制。
\item[特殊内部命令]\index{命令!内部命令!特殊内部命令} 被POSIX标准认为具有特殊作用的命令。
\item[符号]\index{符号} 被 shell 当成一个单独单位的一串字符。它要么是一个单词,要么是一个运算符。
\item[单词]\index{单词} 被 shell 当成一个单位处理的一串字符;它不能包含未被引用的元字符。
\end{description}
% chapter ]]]
\chapter{Shell 的基本功能} % [[[
Bash 是``Bourne-Again SHell''的缩略词,而Bourne~(波恩)~shell是原来由斯蒂芬\textbullet 波恩所作的传统 Unix shell。所有波恩 shell 的内部命令在 Bash 中同样可用,而求值和引用的规则却是来自 POSIX 规范中定义的``标准'' Unix shell。
本间简要介绍了 Bash 的结构:命令、控制结构、shell 函数、shell 变量、shell 扩展、重定向 --- 即把输入和输出定向到~(自)~文件,以及 shell 是怎么执行命令的。
\section{Shell 语法} % [[[
Shell 在读取输入时,要经过一系列的操作。如果在输入中开始了一个注释,shell 会把注释符~(\code{\#})~以及后面的一整行都忽略掉。否则,概括的说,shell 会读取输入并将之分解为一个个单词和运算符,并使用引用规则来决定每个单词和字符的不同含义。然后 shell 会把这些解析为命令和其它结构,去除一些特定单词的特殊含义,对另外一些进行扩展,根据需要进行重定向,执行指定的命令,等待其退出状态,并让这个状态能用于后续检查或处理。
\subsection{Shell 操作} % [[[
下面简要说明了 shell 读取和执行命令时所进行的操作。简单的说,shell 执行了下面的操作:
\begin{enumerate}
\item 从文件~(参见\fullref{shell-script}),或启动``-c''选项的字符串参数~(参见\fullref{invoking-bash})~中,或者用户的终端上读取输入。
\item 按照\fullref{quoting}中所述规则把输入分解为单词和运算符。这些符号用元字符分隔。该步骤还进行别名扩展~(参见\fullref{alias})。
\item 把符号解析为简单和复杂命令~(参见\fullref{shell-commands})。
\item 进行各种 shell 扩展~(参见\fullref{shell-expansion}),并把扩展后的符号分解为文件名~(参见\fullref{filename-expansion})、命令和参数的列表。
\item 进行必要的重定向~(参见\fullref{redirection}),并把重定向运算符及其参数从参数列表中去掉。
\item 执行得到的命令~(参见\fullref{executing-commands})。
\item (可选的)~等待命令结束并收集其退出状态~(\fullref{exit-status})。
\end{enumerate}
\subsubsection{引用}\label{quoting}\index{引用} % [[[
引用在 shell 中用以去除某些字符或单词的特殊含义。它可以用来禁止对特殊字符的特殊处理,使得保留字不再被认为是保留字,或者禁止参数扩展。
Shell 的每个元字符在 shell 中都有特殊的含义,必须引用后才能代表其自身。如果使用了命令历史扩展的功能~(\fullref{history-expansion}),则历史扩展字符~(通常是 \code{!})~也引用起来以取消历史扩展。有关历史扩展的更多细节,请参见\fullref{history-facility}。
Bash 中有三种引用机制:转义字符,单引用和双引用。
% subsubsection: quoting ]]]
\subsubsection{转义字符}\index{引用!转义字符} % [[[
在 Bash 中,没有转义的反斜杠 \code{\textbackslash} 是转义字符,它能保留其下一个字符的字面含义,除非这个字符是换行符。如果出现 \code{\textbackslash newline}\footnote{即\code{\textbackslash}是某行的最后一个字符。}这样的序列,并且反斜杠本身没有被引用,则 \code{\textbackslash newline} 就是行连续符;也就是说,它们将会从输入流中被删除并被完全忽略掉。
% subsubsection ]]]
\subsubsection{单引用}\index{引用!单引用} % [[[
把字符串用单引号~(\code{\char13})~引用能保留引号内各个字符的字面含义。在单引号中不允许再出现单引号,即使它已经由反斜杠转义。
% subsubsection ]]]
\subsubsection{双引用}\index{引用!双引用} % [[[
把字符串用双引号~(\code{"})~引用能保留引号内各个字符的字面含义,除非这些字符是 \code{\$}、\code{\char13}、\code{\textbackslash\textbackslash}、以及 \code{!}~(如果已经打开历史扩展)。在双引号中,\code{\$} 和 \code{\char13} 继续保留其特殊的功能~(参见\fullref{shell-expansion})。而反斜杠,只有当其后面的字符是 \code{\$}、\code{\char13}、\code{"}、\code{\textbackslash\textbackslash} 或者换行符时才保留其特殊的含义。在双引号中,如果反斜杠后面是这些字符中的任意一个,则这个反斜杠就会被删除。而它后面字符如果没有特殊的含义,则它将被保留。在双引号中可以出现另外一个双引号,只要它在反斜杠后面。如果打开了历史扩展,\code{!} 将导致历史扩展,除非它由反斜杠转义。在 \code{!} 前的反斜杠不会被删除。
特殊变量 \code{*} 和 \code{@} 在双引号中有特殊的含义;请参见\fullref{parameter-expansion}。
% subsubsection ]]]
\subsection{ANSI 标准 C 引用}\label{ansi-c}\index{引用!ANSI 引用} % [[[
形如 \code{\$\char13{}string\char13} 的单词会被特殊处理。这个词将会扩展成一个字符串,其中的转义字符会按照 ANSI C 标准被替换。如果其中出现转义字符序列,则按照下面的规则解释:
\begin{description}
\item[\code{\textbackslash a}] 警告~(响铃)~
\item[\code{\textbackslash b}] 退格删除
\item[\code{\textbackslash e}] 转义字符~(不属于 ANSI C)~
\item[\code{\textbackslash f}] 走纸换页
\item[\code{\textbackslash n}] 新行
\item[\code{\textbackslash r}] 换行
\item[\code{\textbackslash t}] (水平)~制表符
\item[\code{\textbackslash v}] 垂直制表符
\item[\code{\textbackslash\textbackslash}] 反斜杠
\item[\code{\textbackslash \char13}] 单引号
\item[\code{\textbackslash \hl{nn}}] 由八进制数 \hl{nnn}~(一个到三个数字)~代表的一个八位字符。
\item[\code{\textbackslash x\hl{HH}}] 由十六进制数 \hl{HH}~(一个到两个数字)~代表的一个八位字符。
\item[\code{\textbackslash cx}] 一个控制字符 CTRL-X
\end{description}
扩展的结果是一个单引用,就好像美元符号原本就不存在一样。
\note[forbid]{不能用双引号转义}{在很多命令中都需要指定单个字符,例如 \code{tr} 或 \code{awk} 中的 \code{IFS} 等变量。这时就应该使用 ANSI 标准 C 引用,而不能用双引号转义。例如
\codeblock{\codeStatement{tr} \codeStatement{\$\char13{}}\codeSpecial{\textbackslash{}n}\codeStatement{\char13{}} \codeStatement{\char13{}}\codeConstant{ }\codeStatement{\char13{}} \codeStatement{file}}
可以把文件 \code{file} 的所有行用空格连在一起。
}
% subsection ]]]
\subsubsection{Locale 专用的翻译}\label{locale-translation}\index{引用!国际化}\index{引用!本地化} % [[[
双引用的字符串在美元符号~(\code{\$})~后面将使得该字符串根据当前的 locale 被翻译过来。如果当前 locale 是 \code{C} 或者 \code{POSIX},则美元符号将被忽略。如果字符串被翻译或者替换,则替换后的字符串是双引用的。
有些系统使用 \code{LC\_MESSAGES}\index{LC\_MESSAGES} 这个 shell 变量来选择消息目录。也有些系统根据 \code{TEXTDOMAIN}\index{TEXTDOMAIN} 这个 shell 变量来决定消息目录的名称,有可能还要加上 \code{.mo} 后缀。如果使用了 \code{TEXTDOMAIN} 变量,可能还需要把 \code{TEXTDOMAINDIR}\index{TEXTDOMAINDIR} 设为存放消息目录文件的路径。更有一些系统这样使用这两个变量:
\codeblock{\code{TEXDOMAINDIR}\hl{/}\code{LC\_MESSAGES}\hl{/LC\_MESSAGES/}\code{TEXTDOMAIN}\hl{.mo}}
% subsubsection ]]]
% shell operation ]]]
\subsection{注释}\index{注释!Shell 注释} % [[[
在非交互运行的 shell 中,或者交互运行的 shell 如果打开了内部命令 \code{shopt} 的 \code{interactive\_comments} 选项~(参见\fullref{shopt:interactive-comments}),以 \code{\#} 开头的单词将使得该单词及本行中所有其它单词都被忽略。如果交互运行的 shell 没有打开 \code{interactive\_comments} 选项则不允许注释。在默认情况下,交互运行的 shell 已经打开了 \code{interactive\_comments} 选项。至于如何让 shell 变成交互式的,请参见\fullref{interactive-shell}。
% subsection ]]]
% section: shell syntax ]]]
\section{Shell 命令}\label{shell-commands}\index{命令|(} % [[[
一个简单的 shell 命令,例如 \code{echo a b c},包含该命令本身,其后还有一些参数;它们都用空格分隔。
复杂一些的命令由有简单的命令通过各种方式组合而成的:通过管道命令,这时一个命令的输出成为另一个命令的输入;或者通过循环或条件命令;或者通过其它组合方式。
\subsection{简单命令}\label{simple-command}\index{命令!简单命令} % [[[
简单命令使用得最频繁。它仅仅包括空白符\index{空白符}分隔的多个单词,其结尾是一个 shell 控制运算符\index{控制运算符}。其中的第一个单词通过指定要执行的命令,而后续单词都是这个命令的参数。
简单命令的返回状态~(参见\fullref{exit-status})~是 POSIX 1003.1 中的 \code{waitpid}\index{waitpid@\code{waitpid}} 函数规定的退出状态;如果该命令由一个信号 \hl{n} 终止,则其退出状态是 \hl{128+n}。
% ]]]
\subsection{管道}\label{pipeline}\index{命令!管道} % [[[
\index{保留字!\code{\char33}}
\index{保留字!\code{time}}
管道是由控制字符\index{控制字符|see{控制运算符}} \code{|} 或 \code{|\&} 分隔开的一系列简单命令。
管道的其格式为:
\codeline{[\code{time} [\code{-p}]] [\code{!}] \hs{命令一} [ [\code{|}或\code{|\&}] \hs{命令二} $\cdots$ ] }
管道里面每个命令的输出都经由管道与下一个命令的输入相连接;也就是说,每个命令都去读取上一个命令的输出。这种连接早在命令中指定的任何重定向之前就已经进行了。
如果使用了 \code{|\&},则\hs{命令一}的标准错误输出将会和\hs{命令二}的标准输出相连,这是 \code{2>\&1 |} 的简写形式。这种对标准错误输出的隐含重定向\index{重定向!隐含重定向}是在命令中指定的任何重定向之后进行的。
保留字 \code{time}\label{time} 能够在管道执行完毕后输出其执行时间的统计信息。这个统计目前包括执行该命令所花费的总时间~(钟表时间)~以及用户和系统时间。\code{-p} 选项\mnemonic{POSIX}使得输出的形式和 POSIX 中的规定相同。可以设置 \code{TIMEFORMAT} 变量为一个格式化字符串,以指定时间输出的形式。至于所有可用的格式,请参见\fullref{TIMEFORMAT}。把 \code{time} 作为保留字允许我们统计内部命令,shell 函数,以及管道的执行时间;而如果 \code{time} 是外部命令就不能很容易的做到这一点。
\note{\code{time} 和 \code{times}}{在 Bash 中,\code{time} 是用于管道的保留字,而 \code{times} 是一个内部命令。它们的作用相同,使用的场合却不一样。参见\fullref{builtin:times}。}
如果该管道不是异步\index{异步|see{同步}}~(参见\fullref{list})~执行的,则 shell 会等待管道中所有命令执行的结束。
管道里面的每个命令都是在自己的子 shell (参见\fullref{execution-environment})~里面执行的。管道的退出状态是其中的最后一个命令的退出状态,除非打开了 \code{pipefail} 选项~(参见\fullref{shopt:pipefail})。如果打开了 \code{pipefail} 选项,则管道的返回状态是其中最后一个~(最靠右的)~返回非零的那个命令的状态;如果所有命令都成功执行,则返回零。如果管道的前端有保留字 \code{!},则其返回状态是按照如上据说再进行逻辑取反。Shell 等待管道里面的所有命令的结束,然后才返回一个值。
% ]]]
\subsection{命令队列}\label{list}\index{命令!命令队列} % [[[
命令队列是由一个或者管道通过运算符 \code{;}、\code{\&}、\code{\&\&}、\code{||} 连接而成,最后还可以~(可选的)~由 \code{;}、\code{\&}、或换行符结束。
在之些队列运算符中,\code{\&\&} 和 \code{||} 具有同样的优先级;其次是 \code{;} 和 \code{\&},这两个也有同样的优先级。
在命令队列中可以使用一个或多个换行符分隔命令,这与分号是等价的。
如果一个命令是由控制字符 \code{\&} 结束,则 shell 会不同步的在子 shell 中执行该命令。我们通常称之为在``后台''\index{后台|see{异步,同步,前台}}运行该命令。这时 shell 并不等待命令的结束,而其返回状态 \hl{0}~(即逻辑真)。如果没有启用作业控制~(参见\fullref{job-control}),并且也没有显式指定重定向,则在异步执行的命令的标准输入将被重定向到 \code{/dev/null}。
由 \code{;} 分隔的命令将相继执行。Shell 依次等待每个命令的结束。整个返回状态是最后一个要执行命令的返回状态。
``与''和``或''命令队列是分别由控制运算符 \code{\&\&} 和 \code{||} 分隔的一个或多个管道。``与''和``或''按照左结合的方法执行。
``与''队列\index{命令!命令队列!``与''队列}具有如下形式:
\codeline{\hs{命令一} \code{\&\&} \hs{命令二}}
其中,当且仅当\hs{命令一}返回值为零时才执行。
``或''队列\index{命令!命令队列!``或''队列}具有如下形式:
\codeline{\hs{命令一} \code{||} \hs{命令二}}
其中,当且仅当\hs{命令一}返回值为非零时才执行。
``与''和``或''队列的返回值是其中最后一个被执行的命令的返回值。
% ]]]
\subsection{复合命令}\label{compound-command}\index{命令!复合命令} % [[[
复合命令是 shell 的编程结构体。每个结构体都是以保留字或者控制运算符开头,然后以与之对应的保留字或控制运算符结束。任何与复合命令相关的重定向~(参见\fullref{redirection})~都作用于该复合命令里面的所有命令,除非显式覆盖。
Bash 提供了循环结构,条件结构,以及将命令分组并将之整体执行的机制。
\labelsubsubsection{循环结构}\index{命令!循环命令} % [[[
Bash 支持以下的循环结构。
请注意:在介绍命令的语法时不管在哪里使用了 \code{;},都可以用一个或多个换行符来代替。
\paragraph{\code{until}} % [[[
\index{保留字!\code{until}}
\index{保留字!\code{do}}
\index{保留字!\code{done}}
\code{until} 命令的语法格式是
\codeline{\code{until} \hs{测试命令}\code{;} \code{do} \hs{命令块}\code{;} \code{done} }
只要\hs{测试命令}返回非零值就执行\hs{命令块}。其返回值是\hs{命令块}中最后一个被执行的命令的返回值。如果\hs{命令块}没有被执行则返回零。
% ]]]
\paragraph{\code{while}} % [[[
\index{保留字!\code{while}}
\index{保留字!\code{do}}
\index{保留字!\code{done}}
\code{while} 命令的语法格式是
\codeline{\code{while} \hs{测试命令}\code{;} \code{do} \hs{命令块}\code{;} \code{done} }
只要\hs{测试命令}返回零就执行\hs{命令块}。其返回值是\hs{命令块}中最后一个被执行的命令的返回值。如果\hs{命令块}没有被执行则返回零。
% ]]]
\paragraph{\code{for}}\ulabel{for} % [[[
\index{保留字!\code{for}}
\index{保留字!\code{in}}
\index{保留字!\code{do}}
\index{保留字!\code{done}}
\code{for} 命令的语法格式是
\codeline{\code{for} \hs{变量} [\code{in} \hs{单词} ]\code{;} \code{do} \hs{命令块}\code{;} \code{done} }
将\hs{单词}扩展成一个列表,然后把结果中列表的每个元素都赋值给\hs{变量}并执行一次\hs{命令块}。如果没有``\code{in} \hs{单词}''这部分,则\code{for} 依次对每个位置参数都执行一次\hs{命令块},就好像指定了``\code{in} \code{\$@}''一样~(参见\fullref{special-parameter})。其返回值是\hs{命令块}中最后一个被执行的命令的返回值。如果对\hs{单词}的扩展没有得到任何元素,则不执行任何命令,并返回零。
\code{for} 命令还支持另外一种格式:
\codeline{\code{for} \code{((} \hs{表达式一} \code{;} \hs{表达式二} \code{;} \hs{表达式三} \code{));} \code{do} \hs{命令块} \code{;} \code{done}}
首先,按照下面将要介绍~(参见\fullref{shell-arithmetic})~规则对算术\hs{表达式一}进行求值。然后不断的对算术\hs{表达式二}进行求值,直到其结果为零。每次求值时,如果\hs{表达式二}的值不是零,则执行一次\hs{命令块},并且计算算术\hs{表达式三}的值。如果省略了任意一个表达式,则效果就好像该表达式总是返回一。其返回值是\hs{命令块}中最后一个被执行的命令的返回值。如果表达式的值都是假的,则返回假。
\stopparagraph
可以使用内置命令\code{break}和\code{continue}~(参见\fullref{builtin:break}和\fullref{builtin:continue})~来控制循环命令的执行。
% ]]]
% looping ]]]
\labelsubsubsection{条件结构}\label{conditional-construct}\index{命令!条件命令} % [[[
\paragraph{\code{if}} % [[[
\index{保留字!\code{if}}
\index{保留字!\code{then}}
\index{保留字!\code{else}}
\index{保留字!\code{elif}}
\index{保留字!\code{fi}}
\code{if} 命令的语法格式是
\codeline{%
\code{if} \hs{测试命令一} \code{;} \code{then}\newline%
\tab\hs{命令块一} \code{;}\newline%
[\code{elif} \hs{测试命令二} \code{;} \code{then}\newline%
\tab\hs{命令块二} \code{;}]\newline%
\tab $\vdots$ \newline%
[\code{else}\newline%
\tab\hs{其它命令块} \code{;}]\newline%
\code{fi}%
}
首先执行\hs{测试命令一}这个命令列表,如果其返回值为零,则执行命令列表\hs{命令块一}。如果\hs{测试命令一}返回非零值,则依次执行每个\code{elif}列表,如果其返回值为零,则执行其对应的命令块,并结束整个命令。如果有``\code{else} \hs{其它命令块}'',并且 \code{if} 或者 \code{elif} 子句的最后一个命令返回值为非零,则执行\hs{其它命令块}。整个命令的返回值是最后一个被执行的命令的返回值。如果没有一个条件为真,则返回零。
% ]]]
\paragraph{\code{case}} % [[[
\index{保留字!\code{case}}
\index{保留字!\code{esac}}
\index{保留字!\code{in}}
\code{case} 命令的语法格式是
\codeline{%
\code{case} \hs{单词} \code{in}\newline%
\tab [ [\code{(}] \hs{模式一} [\code{|} \hs{模式二} ] $\cdots$ \code{)}\newline%
\tab\tab\hs{命令块}\newline%
\tab\tab\code{;;}]\newline%
\tab$\vdots$ \newline%
\code{esac}%
}
\code{case} 命令会选择性的执行与\hs{单词}所匹配的第一个模式对应的\hs{命令块}。如果打开了 shell 的 \code{nocasematch} 选项~(参见\fullref{shopt:nocasematch}的描述),则匹配时将忽略字母的大小写。``\code{|}''用来分隔多个模式,而``\code{)}''用来结束模式列表。\footnote{模式列表前面可以加一个与``\code{)}''匹配的``\code{(}'',但不是必须的。}模式列表和其对应的命令块叫做一个``分句''。每个分句都必须由``\code{;;}'',``\code{,\&}''或者``\code{;;\&}''结束。这里的\hs{单词}在匹配之前要经过波浪号扩展、参数扩展、命令替换、算术扩展以及引用去除,而每个\hs{模式}也要经过波浪号扩展、参数扩展、命令替换、算术扩展等步骤。
\code{case} 分句的数量量不限的,但是每个分句都以``\code{;;}'',``\code{,\&}''或者``\code{;;\&}''结束。最先匹配的\hs{模式}决定了哪个\hs{命令块}被执行。下面是 \code{case} 在脚本中使用的一个例子,它描述一些动物的有趣特征:
\begin{codelist}{\code{case} 的例子}%
\input{case.tex}%
\end{codelist}
如果使用了``\code{;;}''~(来结束分句)~,则匹配第一个\hs{模式}以后就不会再匹配其它\hs{模式}。如果用``\code{;\&}''来代替``\code{;;}'',则执行\hs{命令块}后,如果下面还有其它分句,就继续执行该分句。如果用``\code{;;\&}''来代替``\code{;;}'',则执行\hs{命令块}后,如果下面还有其它分句,就检查其\hs{模式}~(如果有的话)~;如果其模式为真,继续执行其对应的命令块。
如果任何\hs{模式}都不匹配,该命令的返回状态是零;否则,返回最后一个被执行的命令的返回值。
% ]]]
\paragraph{\code{select}}\ulabel{select} % [[[
\index{保留字!\code{select}}
\index{保留字!\code{in}}
\index{保留字!\code{do}}
\index{保留字!\code{done}}
\code{select} 结构使得菜单的生成变得简单。它的语法格式几乎和 \code{for} 命令一样:
\codeline{\code{select} \hs{名称} [\code{in} \hs{单词表} $\cdots$ ] \code{;} \code{do} \hs{命令块}\code{;} \code{done}}
\code{in} 后面的\hs{单词表}被扩展并生成一个项目列表;这个扩展后的列表将会打印到标准错误输出流中,并且每个项目前面都会加上一个序数。如果省略了``\code{in} \hs{单词表}''部分,则打印位置参数,就好像使用了``\code{in} \code{\$@}''一样。之后,将显示 \code{PS3} 提示符,并且从标准输入读取一行的输入。如果输入的行含有一个与所打印出的词项对应的数字,则 \code{name} 的值就被置为该单词。如果输入的为空,则重新显示\hs{单词表}和提示符。如果输入了 \code{EOF} 字符,\code{select} 命令将结束。输入任何其它值都会使\hs{名称}被置为空值。读取到地值被存放在变量 \code{REPLY} 中。每次选择之后都会执行\hs{命令块},直到遇到一个 \code{break} 命令为止---这时 \code{select} 命令将结束。
下面是 \code{select} 的一个例子。该例允许用户从当前目录中选择一个文件,并显示用户选择的文件名及其序号。
\begin{codelist}{\code{select} 的例子}%
\input{select.tex}%
\end{codelist}
% ]]]
\paragraph[\code{((\texorpdfstring{$\cdots$}\ ))}]{\code{(($\cdots$))}}\ulabel{cond-((} % [[[
\codeline{\code{((} \hs{算术表达式} \code{))}}
根据后面将要介绍的规则~(参见\fullref{shell-arithmetic})~对\hs{算术表达式}求值。如果这个值不是零,则返回状态是零,否则返回\hl{1}。这和下面的命令完全等价:
\codeline{\code{let} \code{"}\hs{表达式}\code{"}}
关于内部命令 \code{let} 的完整介绍,请参见\fullref{builtin:let}。
% ]]]
\paragraph[\code{[[\texorpdfstring{$\cdots$}\ ]]}]{\code{[[$\cdots$]]}}\ulabel{cond-[[} % [[[
\index{保留字!\code{[[}}
\index{保留字!\code{]]}}
\codeline{\code{[[} \hs{条件表达式} \code{]]}}
对\hs{条件表达式}求值,并根据其结果返回 \hl{0} 或者 \hl{1}。\hs{条件表达式}是由\fullref{conditional-expression}中介绍的原子成分组成的。在 ``\code{[[}'' 和 ``\code{]]}'' 中间的单词不会进行单词和文件名扩展,但却进行波浪号扩展、参数和变量扩展、算术扩展、命令替换、进程替换以及引用去除。诸如``\code{-f}''等条件运算符不能被引用,否则它们就不是原子算术表达式了。
如果使用了``\code{==}''和``\code{!=}''运算符,则运算符的右边会被看作是一个模式,并且按照\fullref{pattern-matching} 中所介绍的匹配规则进行匹配。如果打开了 shell 的 \code{nocasematch} 选项~(参见\fullref{shopt:nocasematch}),则匹配时不考虑字母的大小写。如果使用了``\code{==}''并且字符串匹配,或者使用了``\code{!=}''并且字符串不匹配,则其返回值是 0,否则返回 1。模式的任何部分都可以被引用以强制把其当作字符串来匹配。
还可以使用另外一个双目运算符``\code{=\textasciitilde}'';它和``\code{==}''以及``\code{!=}''具有同样的优先级。如果使用了它,则其右边的字符串就被认为是一个扩展的正则表达式来匹配~(如 \code{regex} \hl{3} 一样\footnote{指 \code{manpage}。})。如果字符串和模式匹配,则返回值是 0,否则返回 1。如果这个正则表达式有语法错误,则整个条件表达式的返回值是 2。如果打开了 shell 的 \code{nocasematch} 选项~(参见\fullref{shopt:nocasematch}),则匹配时不考虑字母的大小写。模式的任何部分都可以被引用以强制把其当作字符串来匹配。由正则表达式中括号里面的子模式匹配的字符串被保存在数组变量 \code{BASH\_REMATCH} 中。\code{BASH\_REMATCH} 中下标为 \hs{0} 的元素是字符串中与整个正则表达式匹配的部分。\code{BASH\_REMATCH} 中下标为 \hs{n} 的元素是字符串中与第 \hl{n} 个括号里面的子模式匹配的部分。
表达式可以通过下面的运算符~(按优先级降序排列)~组合在一起:
\begin{description}
\item[\code{(} \hs{表达式} \code{)}] 返回\hs{表达式}的值。这样写可以改变运算符的正常优先级。
\item[\code{!} \hs{表达式}] 如果表达式为假,则返回真。
\item[\hs{表达式一} \code{\&\&} \hs{表达式二}] 如果\hs{表达式一}和\hs{表达式二}同时为真则返回真。
\item[\hs{表达式一} \code{||} \hs{表达式二}] 如果\hs{表达式一}或者\hs{表达式二}为真则返回真。
\end{description}
如果\hs{表达式一}的值已经足以判断整个\hs{条件表达式}的返回值,则\code{\&\&} 和 \code{||} 运算符就不再对\hs{表达式二}进行求值。
% ]]]
% conditions ]]]
\subsubsection{命令组合}\label{command-grouping}\index{命令!命令组合} % [[[
Bash 提供了两种方式来把一系列命令放在一起作为整体执行。当命令被组织在一起时,可以对整个命令列表进行重定向。例如,命令列表中所有命令的输出都可以重定向到一个单一的流中。
\paragraph{\code{()}} % [[[
\codeline{\code{(} \hs{表达式} \code{)}}
把一列命令放在括号中间就会创建一个子 shell 环境~(参见\fullref{execution-environment})~并在这个子 shell 中执行该列表中的每个俱。就是因为命令列表是在子 shell 中执行的,所以在子 shell 结束后,其中的变量赋值将不再有效。
% ]]]
\paragraph{\code{\{\}}} % [[[
\codeline{\code{\{} \hs{表达式}\code{; \}}}
把一列命令放在大括号中间,这列命令就会在当前 shell 中执行,而不是创建子 shell。命令列表后面的逗号~(或者换行符)~是必须的。
\note[info]{命令的分隔符}{%
在 Bash 的分隔符中,``\code{;}''的使用是最频繁的。它除了具有分隔的作用,没有其它任何含义;所以,任何命令的末尾都可以使用它。但如果一个命令单独成行,这个符号完全可以省略。例如,
\codeblock{\codeStatement{for} name \codeStatement{in} *;\codeStatement{do} \codeStatement{echo} \codePreProc{\$\{}\codePreProc{name}\codeStatement{/}.png\codeStatement{/}.jpg\codePreProc{\}}\codeStatement{;} \codeStatement{done}}
和
\codeblock{%
\codeStatement{for} name \codeStatement{in} *\\
\codeStatement{do}\\
\tab\codeStatement{echo} \codePreProc{\$\{}\codePreProc{name}\codeStatement{/}.png\codeStatement{/}.jpg\codePreProc{\}}\\
\codeStatement{done}
}
是完全等价的。}
\stopparagraph
% ]]]
除了子 shell 的创建,上述两种结构之间由于历史的原因还有微妙的差别。大括号是保留字,所以它们与命令列表之间必须用空白符或其它 shell 的元字符分开;而圆括号是运算符,所以即使它们和命令列表之间没有用空白符分开也会被 shell 当作独立的符号。
这两种结构的命令返回值都是其中命令列表的返回值。
% group command ]]]
\subsection{协同进程}\label{coproc}\index{命令!协同进程} % [[[
协同进程 \hl{coprocess} 是指一个 shell 命令前面有 \code{coproc}\index{保留字!\code{coproc}} 保留字;它是在子 shell 中异步执行的,就好像这个命令后面有控制运算符 \code{\&} 一样。协同进程和其父 shell 进程之间有双向的管道。这个命令的格式是:
\codeline{\code{coproc} [ \code{NAME} ] \hs{命令} [ \hs{重定向} ]}
上述命令创建了一个名为 \code{NAME} 的协同进程。如果没有指定 \code{NAME},默认的名称是 \code{COPROC}。如果这里的\hs{命令}只是一个简单命令~(参见\fullref{simple-command}),则不能指定 \code{NAME},否则它会被当作\hs{命令}的第一个单词。
当 \code{coproc} 执行时,shell 会在父进程中创建一个名为 \code{NAME} 的数组变量~(参见\fullref{array})。\hs{命令}的标准输出通过管道和父进程的一个文件描述符相连;该文件描述符被赋给 \code{NAME[0]}。\hs{命令}的标准输入通过管道和父进程的一个文件描述符相连;该文件描述符被赋给 \code{NAME[1]}。这个管道是在命令当中指定的任何重定向~(参见\fullref{redirection})~之前就建立了。这些文件描述符可以在 shell 命令和重定向中通过标准的单词扩展而当作参数使用。
用来执行协同进程的子 shell 的进程号保留在数组变量 \code{NAME[PID]} 中。可以使用内部命令 \code{wait} 来等待协同命令的结束。协同进程的返回状态是其中命令的返回状态。
% ]]]
% compound command ]]]
\index{命令|)}
% shell commands ]]]
\section{Shell 函数}\label{shell-function}\index{函数} % [[[
Shell 函数把一组命令与单一的名称相关联,以便以后执行。在执行时,它们就和``常规''命令一样。如果 shell 函数的名称被当作一个简单命令使用,与它相关联的命令就会被执行。Shell 函数是在当前的 shell 环境中执行的,而不是创建新的进程来执行。
函数通过下面的语法来定义:
\codeline{[ \code{function} ] \hs{名称} () \hs{复合命令块} [ \hs{重定向} ]}
上面定义了一个叫做\hs{名称}的函数。保留字 \code{funnction}\index{保留字!\code{function}} 是可选的。如果有 \code{function} 这个保留字,则可以省略括号。\hs{复合命令块}~(参见\fullref{compound-command})~是函数体;它通常是包含在 \code{\{} 和 \code{\}} 之间的命令列表,也可以是上面列出的任何复合命令。每当\hs{名称}被指定为一个命令名时,\hs{复合命令块}就会被执行。当函数被执行时,与之相关的重定向~(参见\fullref{redirection})~也会同时被执行。
可以使用内部命令 \code{unset} 的``\code{-f}''选项~(参见\fullref{builtin:unset})~来取消函数的定义。
除非发生语法错误,或者一个同名并且为只读的函数已经存在,函数定义的返回值是零。执行时,函数的返回值是函数体内最后一个被执行命令的返回值。
注意,由于历史的原因,通常情况下,函数体外的大括号与函数体之间必须用空白符或者换行符分开。因为大括号是保留字\index{保留字!\char123@\code{\char123}},但是只有它们与其中间的命令列表空格或其它 shell 元字符分隔时才能被识别为保留字。此外,使用大括号时,其中间的命令列表必须用逗号、``\code{\&}''或者换行符结束。
函数执行时,传递给它的参数成为它执行期间的位置参数~(参见\fullref{positional-parameter})。能扩展为位置参数个数的特殊参数``\code{\#}''将随之更新。特殊参数 \code{0}\footnote{即 \code{\$0},通常表示脚本的名称。} 不变。在函数执行时,变量 \code{FUNCNAME} 的第一名元素被设为函数的名称。除了 \code{DEBUG} 和 \code{RETURN} 这两个陷阱没有被继承以外,函数和其调用者之间在Shell 执行环境所有其它方面都完全一样。如果用内部命令 \code{declare} 设置了函数的 \code{trace} 属性,或者设置了内部命令 \code{set} 的 \code{functrace} 选项,以便使,则函数也会继承调用者的 \code{DEBUG} 和 \code{RETURN} 陷阱。参见\fullref{builtin:trap} 的描述。
如果在函数里面执行了内部命令 \code{return},则函数的执行将结束,并且返回到调用函数那里的下一个命令。任何与 \code{RETURN} 陷阱相关联的命令都将在执行恢复前被执行。函数结束时,位置参数以及特殊参数``\code{\#}''的值恢复到函数被执行以前的状态。如果 \code{return} 带有一个数值型参数,则这个参数就是函数的返回值;否则,函数的返回状态是其返回前最后一个被执行命令的返回状态。
函数本地的变量可以用内部命令 \code{local} 来声明。这些变量只对函数及它使用的命令是可见的。
函数名称及其定义可以用内部命令 \code{typeset} 或者 \code{declare} 加上``\code{-f}''选项~(参见\fullref{builtin:declare})~来列出;而\code{typeset} 或者 \code{declare} 加上``\code{-F}''选项只列出函数名~(如果打开了 shell 的 \code{extdebug} 选项,则还会列出源文件和行号)。通过内部命令 \code{export} 的``\code{-f}''选项~(参见\fullref{builtin:export}),还可以把函数导出,使得它们在子 shell 中自动得以定义。注意,如果 shell 函数和变量同名,则可能导致传给 shell 子进程的环境中有多个完全一样的名字。如果这样会引发问题,就需要避免同名。
函数可以是递归的。对递归调用的次数没有限制。
% ]]]
\section{Shell 参数}\index{参数} % [[[
参数是能存储值的实体;它可以是一个名称、一个数字、或者下面列出的特殊字符之一。变量是名称所代表的参数。每个变量都有值以及零个或多个属性。属性通过内部命令 \code{declare} (参见\fullref{builtin:declare})~来设置。
参数通过赋值来设置。空字符串也是一个有效的值。参数一旦设置以后,只能通过内部命令 \code{unset} 才能取消设置。
可以通过下面的语句形式给参数赋值\footnote{紧挨着等号前后的第一个字符不能是空格,否则在进行下述的七种扩展时会遇到麻烦。如果的确想在\hs{值}中使用空格,可以将\hs{值}放在引号中。}:
\codeline{\hs{名称} =[\hs{值}]}
如果没有给定\hs{值},则变量被赋于空字符串。所有的值都会进行大括号扩展、参数和变量扩展、命令替换、算术扩展、以及引用去除~(详见下述)。如果启用了变量的 \code{integer} 属性,则把该变量当作算术表达式求值,即使没有使用 \code{\$(($\cdots$))} 扩展~(参见\fullref{arithmetic-expansion})。除了下述的``\code{\$@}'',否则不会进行单词扩展。文件名扩展也不会进行。赋值语句还可以作为内部命令 \code{alias}、\code{declare}、\code{typeset}、\code{export}、\code{readonly} 和 \code{local} 的参数。
在赋值语句给 shell 变量或数组~(参见\fullref{array})~元素\footnote{原文为 \hl{index},实际应该是指数组中位于 \hl{index} 的某个元素。}赋值的行文中,可以使用``\code{+=}''运算符附加或增加到变量原来的值中。如果变量启用了 \code{integer} 属性并且使用``\code{+=}'',则按照算术表达式对\hs{值}进行求值,并把它加入到变量原来的值中后再求值。如果对数组变量进行复合赋值~(参见\fullref{array})~时使用了``\code{+=}'',则变量原来的值不会被覆盖~(像用``\code{=}''那样),新的值被附加到数组中下标最大的那个元素的后面~(对下标数组而言),或者在键值数组中新增一个键值对。如果对字符串变量使用它,则把\hs{值}进行扩展并附加在变量 的值后面。
\subsection{位置参数}\label{positional-parameter}\index{参数!位置参数} % [[[
位置参数是由除了单个 \hl{0} 以外的一个或多个数字表示的参数;它是在 shell 启动时由其参数赋值的,并且可以用内部命令 \code{set} 来重新赋值。第 \hl{N} 个位置参数可以表示为 \code{\$\{N\}};如果 \hl{N} 只含有一个数字,也可以表示为 \code{\$N}。位置参数不可以通过赋值语句来赋值;而应该用内部命令 \code{set} 或者 \code{shift} (参见\fullref{set}和\fullref{builtin:shift})~来设置或删除。在执行 shell 函数时~(参见\fullref{shell-function}),位置参数会暂时被更换。
含有多于一个数字的位置参数在扩展时必须放在大括号中。
% ]]]
\subsection{特殊参数}\label{special-parameter}\index{参数!特殊参数} % [[[
Shell 会对一些参数特殊处理。这些参数只能使用而不能对它们赋值。
\begin{description}
\item[\code{*}] 扩展为从 \hl{1} 开始的所有位置参数。如果它出现在双引号中,则扩展为一个包含每个参数的单词,参数之间用特殊变量 \code{IFS} 的第一个字符分隔。也就是说,\code{"\$*"} 和 \code{"\$1c\$2c$\cdots$"} 是等价的;其中,\code{c} 是特殊变量 \code{IFS} 的第一个字符。如果 \code{IFS} 没有设置,则参数之间用空格分隔。如果 \code{IFS} 为空,则参数直接相连,中间没有分隔。
\item[\code{@}] 扩展为从 \hl{1} 开始的所有位置参数。如果它出现在双引号中,则每个参数都扩展为一个单词;也就是说,\code{"\$@"} 和 \code{"\$1c" "\$2c" $\cdots$} 是等价的。其中,\code{c} 是特殊变量 \code{IFS} 的第一个字符。如果 \code{IFS} 没有设置,则参数之间用空格分隔。如果 \code{IFS} 为空,则参数直接相连,中间没有分隔。如果这这样的双引号扩展发生中单词里面,则第一个参数扩展后与原单词的开始部分连在一起,而最后一个参数扩展后与原单词的最后一个部分连在一起。如果没有位置参数,则 \code{"\$@"} 和 \code{\$@} 扩展后为空,也即它们会被删除。
\item[\code{\#}] 扩展为位置参数的个数,用十进制表示。
\item[\code{?}] 扩展为最近在前台\index{前台}执行的命令的退出状态。
\item[\code{-}] (连字符)~扩展为当前的所有选项;这些选项是启动时给定的,或者通过内部命令 \code{set} 打开的,或者由 shell 本身打开的~(例如 \code{-i} 选项)。
\item[\code{\$}] 扩展为当前 shell 的进程号。在子 shell \code{()} 中,扩展为启动 shell 的进程号,而不是子 shell 的进程号。
\item[\code{!}] 扩展为最近在后台\index{后台}~(异步)~执行的命令的退出状态。
\item[\code{0}] 扩展为 shell 或者 shell 脚本的名称。它是在 shell 初始化时设置的。如果 Bash 启动时带有包含命令的文件名参数~(参见\fullref{invoking-bash}),\code{\$0} 就被设为该文件名。如果 Bash 启动时带有``\code{-c}''选项~(参见\fullref{invoking-bash}),则 \code{\$0} 被设为待执行字符串后面的第一个参数~(如果这个参数存在)。否则,它就是用来启动 Bash 的文件名,即~(命令行的)~第一个参数。
\item[\code{\_}] (下划线)~在 shell 启动时,设为启动 shell 的绝对路径,或者在执行环境或参数列表中所传递的待执行的 shell 脚本的绝对路径。随后,扩展为前一条命令的最后一个参数扩展后的值。还可设为每个已执行命令的绝对路径,这些路径是启动时指定的并且导入到命令的执行环境中。检查邮件时,这个变量保存邮箱文件的文件名。
\end{description}
% ]]]
% shell parameters ]]]
\section{Shell 扩展}\label{shell-expansion}\index{扩展|(} % [[[
命令行被拆分成符号以后要进行扩展;扩展的方式有七种:
\begin{itemize}
\item 大括号扩展
\item 波浪号扩展
\item 参数和变量扩展
\item 命令替换
\item 算术扩展
\item 单词拆分
\item 文件名扩展
\end{itemize}
扩展的顺序是:大括号扩展,波浪号扩展,参数、变量和算术扩展以及命令替换~(按从左到右的顺序),单词拆分,以及文件名扩展。如果系统支持,则还有另外一种扩展,即进程替换;它和参数、变量和算术扩展以及命令替换是同时进行的。
只有大括号扩展,单词扩展,以及文件名扩展在扩展时能够改变单词的数目。其它的扩展都是单个单词扩展成单个单词。唯一例外的是对``\code{\$@}''~(参见\fullref{special-parameter})~和``\code{\$\{name[@]\}}''~(参见\fullref{array})~的扩展。所有扩展完成后再进行引用去除~(参见\fullref{quote-removal})。
\subsection{大括号扩展}\label{brace-expansion}\index{扩展!大括号扩展} % [[[
大括号扩展是一种能够生成任意字符串的机制;它和文件名扩展~(参见\fullref{filename-expansion})~是相似的,但是生成的文件名不一定存在。进行大括号扩展的模式在形式上有一个可选的前缀;其后是一组用逗号分隔的字符串,或者是一个序列表达式,它们都在一对大括号之间;最后是一个可行的后缀。前缀部分将放在大括号中每个字符串的前面,而后缀将放在每个结果的后面,它们都是从左到右进行扩展的。例如,
\codeblock{\code{%
bash\$ echo a\{d,c,b\}e\newline%
ade ace abe%
}}
序列表达式\index{表达式!序列表达式|see{大括号扩展}}的形式是
\codeline{ \code{\{x .. y} [ \hs{增量} ] \code{\}}}
其中,\code{x} 和 \code{y} 是整数或者单个字符;而可选的\hs{增量}是一个整数。如果使用了整数,则扩展成 \code{x} 的 \code{y} 之间的每个整数,包括 \code{x} 和 \code{y}。所使用的整数可以用``\hl{0}''开头,以使每个量都有同样的宽度。当 \code{x} 或者 \code{y} 有前导的零时,shell 会试图强制让每个生成的量都含有同样多的数位,如果不同就在前面补零。如果使用了字符,那么表达式就扩展成在字母表中 \code{x} 和 \code{y} 中间的每个字母,包括 \code{x} 和 \code{y}。注意,\code{x} 和 \code{y} 必须有同样的类型。如果指定了\hs{增量},它就是每个量之间的差值。默认的\hs{增量}是 \hl{1} 或者 \hl{-1},根据情况\footnote{具体来说,如果 \code{x} 比 \code{y} 小,则增量是 \hl{1};如果 \code{x} 比 \code{y} 大,则增量是 \hl{-1}。}而定。
大括号扩展在其它所有扩展之前进行;在其它扩展中特殊的字符都被保留下来。它完全是字面上的扩展。Bash 不会对扩展的上下文字或大括号之间的文本进行任何语义解释。为了避免与参数扩展冲突,大括号扩展不会识别字符串中的``\code{\$\{}''。
格式正确的大括号扩展必须包含没有被引用的起始和结束大括号,还有至少一个未被引用的逗号或者序列表达式。格式不正确的大括号扩展不会被处理。
为了防止被认为是大括号扩展的一部分,\code{\{} 或者``\code{,}''可以用反斜杠转义。为了避免与参数扩展冲突,大括号扩展不会识别字符串中的``\code{\$\{}''。
当要生成的字符串具有的公共前缀比上面的例子更长时,大括号扩展通常用来简写:
\codeblock{%
\codeStatement{mkdir} /usr/local/src/bash/\codeSpecial{\{}old,new,dist,bugs\codeSpecial{\}}\newline%
\codeStatement{chown} root /usr/\codeSpecial{\{}ucb/\codeSpecial{\{}\codeStatement{ex},edit\codeSpecial{\}},lib/\codeSpecial{\{}\codeStatement{ex}?.?*,how\_ex\codeSpecial{\}\}}%
}
% ]]]
\subsection{波浪号扩展}\label{tilde-expansion}\index{扩展!波浪号扩展} % [[[
如果一个单词以未被引用的波浪号``\code{\textasciitilde}''开头,则其后的所有字符,直到第一个未被引用的斜杠~(如果有未被引用的斜杠),都被看作波浪号前缀。如果波浪号前缀里面的字符都没有被引用,则其中波浪号后面的所有字符就被当成一个可能存在的登录用户名。如果这个登录名是个空字符串,波浪号就被替换成 shell 变量中 \code{HOME} 的值。如果没有设置 \code{HOME},则替换成执行该脚本那个用户的主目录。否则,波浪号前缀就被替换成其中指定的那个登录名的主目录\index{主目录|see{波浪号扩展}}。
如果波浪号前缀是``\code{\textasciitilde +}'',它就会被 shell 变量 \code{PWD} 的值取代。如果波浪号前缀是``\code{\textasciitilde -}'',它就会被 shell 变量 \code{OLDPWD} 的值取代~(如果这个值已经设置)。在波浪号前缀中,如果波浪号后面的字符包含数字 \hl{N},前面可能还有``\code{+}''或``\code{-}'',那么这个波浪号前缀就会被目录栈~(参见\fullref{directory-stack})~中相应的元素取代,这个元素是内部命令 \code{dirs} 以波浪号前缀中波浪号后面各字符作为参数所要显示的内容。如果波浪号前缀中除去波浪号的部分仅仅包含数字,而没有前导的``\code{+}''或``\code{-}'',则认为指定了``\code{+}''。