Linux Command Line and Shell Scripting Bible
Transcripción
Linux Command Line and Shell Scripting Bible
TableofContents 1. Introduction 1. WhoShouldReadThisBook 2. HowThisBookIsOrganized 3. MinimumRequirements 4. WheretoGofromHere 2. PartI:TheLinuxCommandLine 3. Chapter1:StartingwithLinuxShells 1. WhatIsLinux? 2. LinuxDistributions 3. Summary 4. Chapter2:GettingtotheShell 1. ReachingtheCommandLine 2. AccessingCLIviaaLinuxConsoleTerminal 3. AccessingCLIviaGraphicalTerminalEmulation 4. UsingtheGNOMETerminalEmulator 5. UsingtheKonsoleTerminalEmulator 6. UsingthextermTerminalEmulator 7. Summary 5. Chapter3:BasicbashShellCommands 1. StartingtheShell 2. UsingtheShellPrompt 3. InteractingwiththebashManual 4. NavigatingtheFilesystem 5. ListingFilesandDirectories 6. HandlingFiles 7. ManagingDirectories 8. ViewingFileContents 9. Summary 6. Chapter4:MorebashShellCommands 1. MonitoringPrograms 2. MonitoringDiskSpace 3. WorkingwithDataFiles 4. Summary 7. Chapter5:UnderstandingtheShell 1. ExploringShellTypes 2. ExploringParentandChildShellRelationships 3. UnderstandingShellBuilt-InCommands 4. Summary 8. Chapter6:UsingLinuxEnvironmentVariables 1. ExploringEnvironmentVariables 2. SettingUser-DefinedVariables 3. RemovingEnvironmentVariables 4. UncoveringDefaultShellEnvironmentVariables 5. SettingthePATHEnvironmentVariable 6. LocatingSystemEnvironmentVariables 7. LearningaboutVariableArrays 8. Summary 9. Chapter7:UnderstandingLinuxFilePermissions 1. LinuxSecurity 2. UsingLinuxGroups 3. DecodingFilePermissions 4. ChangingSecuritySettings 5. SharingFiles 6. Summary 10. Chapter8:ManagingFilesystems 1. ExploringLinuxFilesystems 2. WorkingwithFilesystems 3. ManagingLogicalVolumes 4. Summary 11. Chapter9:InstallingSoftware 1. PackageManagementPrimer 2. TheDebian-BasedSystems 3. TheRedHat–BasedSystems 4. InstallingfromSourceCode 5. Summary 12. Chapter10:WorkingwithEditors 1. VisitingthevimEditor 2. NavigatingthenanoEditor 3. ExploringtheemacsEditor 4. ExploringtheKDEFamilyofEditors 5. ExploringtheGNOMEEditor 6. Summary 13. PartII:ShellScriptingBasics 14. Chapter11:BasicScriptBuilding 1. UsingMultipleCommands 2. CreatingaScriptFile 3. DisplayingMessages 4. UsingVariables 5. RedirectingInputandOutput 6. Pipes 7. PerformingMath 8. ExitingtheScript 9. Summary 15. Chapter12:UsingStructuredCommands 1. Workingwiththeif-thenStatement 2. Exploringtheif-then-elseStatement 3. Nestingifs 4. TryingthetestCommand 5. ConsideringCompoundTesting 6. WorkingwithAdvancedif-thenFeatures 7. ConsideringthecaseCommand 8. Summary 16. Chapter13:MoreStructuredCommands 1. TheforCommand 2. TheC-StyleforCommand 3. ThewhileCommand 4. TheuntilCommand 5. NestingLoops 6. LoopingonFileData 7. ControllingtheLoop 8. ProcessingtheOutputofaLoop 9. PracticalExamples 10. Summary 17. Chapter14:HandlingUserInput 1. PassingParameters 2. UsingSpecialParameterVariables 3. BeingShifty 4. WorkingwithOptions 5. StandardizingOptions 6. GettingUserInput 7. Summary 18. Chapter15:PresentingData 1. UnderstandingInputandOutput 2. RedirectingOutputinScripts 3. RedirectingInputinScripts 4. CreatingYourOwnRedirection 5. ListingOpenFileDescriptors 6. SuppressingCommandOutput 7. UsingTemporaryFiles 8. LoggingMessages 9. PracticalExample 10. Summary 19. Chapter16:ScriptControl 1. HandlingSignals 2. RunningScriptsinBackgroundMode 3. RunningScriptswithoutaHang-Up 4. ControllingtheJob 5. BeingNice 6. RunningLikeClockwork 7. Summary 20. PartIII:AdvancedShellScripting 21. Chapter17:CreatingFunctions 1. BasicScriptFunctions 2. ReturningaValue 3. UsingVariablesinFunctions 4. ArrayVariablesandFunctions 5. FunctionRecursion 6. CreatingaLibrary 7. UsingFunctionsontheCommandLine 8. FollowingaPracticalExample 9. Summary 22. Chapter18:WritingScriptsforGraphicalDesktops 1. CreatingTextMenus 2. DoingWindows 3. GettingGraphic 4. Summary 23. Chapter19:Introducingsedandgawk 1. ManipulatingText 2. CommandingatthesedEditorBasics 3. Summary 24. Chapter20:RegularExpressions 1. WhatAreRegularExpressions? 2. DefiningBREPatterns 3. ExtendedRegularExpressions 4. RegularExpressionsinAction 5. Summary 25. Chapter21:Advancedsed 1. LookingatMultilineCommands 2. HoldingSpace 3. NegatingaCommand 4. ChangingtheFlow 5. ReplacingviaaPattern 6. PlacingsedCommandsinScripts 7. CreatingsedUtilities 8. Summary 26. Chapter22:Advancedgawk 1. UsingVariables 2. WorkingwithArrays 3. UsingPatterns 4. StructuredCommands 5. FormattedPrinting 6. Built-InFunctions 7. User-DefinedFunctions 8. WorkingthroughaPracticalExample 9. Summary 27. Chapter23:WorkingwithAlternativeShells 1. WhatIsthedashShell? 2. ThedashShellFeatures 3. Scriptingindash 4. ThezshShell 5. PartsofthezshShell 6. Scriptingwithzsh 7. Summary 28. PartIV:CreatingPracticalScripts 29. Chapter24:WritingSimpleScriptUtilities 1. PerformingArchives 2. ManagingUserAccounts 3. MonitoringDiskSpace 4. Summary 30. Chapter25:ProducingScriptsforDatabase,Web,andE-Mail 1. UsingaMySQLDatabase 2. UsingtheWeb 3. UsingE-Mail 4. Summary 31. Chapter26:CreatingFunLittleShellScripts 1. SendingaMessage 2. ObtainingaQuote 3. GeneratinganExcuse 4. Summary 32. AppendixA:QuickGuidetobashCommands 1. ReviewingBuilt-InCommands 2. LookingatCommonbashCommands 3. AssessingEnvironmentVariables 33. AppendixB:QuickGuidetosedandgawk 1. ThesedEditor 2. ThegawkProgram 34. EndUserLicenseAgreement ListofIllustrations 1. Figure1.1 2. Figure1.2 3. Figure1.3 4. Figure1.4 5. Figure1.5 6. Figure1.6 7. Figure2.1 8. Figure2.2 9. Figure2.3 10. Figure2.4 11. Figure2.5 12. Figure3.1 13. Figure3.2 14. Figure3.3 15. Figure4.1 16. Figure5.1 17. Figure5.2 18. Figure5.3 19. Figure7.1 20. Figure8.1 21. Figure9.1 22. Figure10.1 23. Figure10.2 24. Figure10.3 25. Figure10.4 26. Figure10.5 27. Figure10.6 28. Figure10.7 29. Figure10.8 30. Figure10.9 31. Figure10.10 32. Figure10.11 33. Figure10.12 34. Figure10.13 35. Figure10.14 36. Figure11.1 37. Figure11.2 38. Figure18.1 39. Figure18.2 40. Figure18.3 41. Figure18.4 42. Figure18.5 43. Figure18.6 44. Figure18.7 45. Figure18.8 46. Figure18.9 47. Figure18.10 48. Figure18.11 49. Figure18.12 50. Figure18.13 51. Figure20.1 52. Figure21.1 53. Figure24.1 54. Figure25.1 55. Figure25.2 ListofTables 1. Table1.1 2. Table1.2 3. Table1.3 4. Table1.4 5. Table1.5 6. Table1.6 7. Table2.1 8. Table2.2 9. Table2.3 10. Table2.4 11. Table2.5 12. Table2.6 13. Table2.7 14. Table2.8 15. Table2.9 16. Table2.10 17. Table2.11 18. Table2.12 19. Table2.13 20. Table2.14 21. Table2.15 22. Table2.16 23. Table2.17 24. Table2.18 25. Table3.1 26. Table3.2 27. Table3.3 28. Table4.1 29. Table4.2 30. Table4.3 31. Table4.4 32. Table4.5 33. Table4.6 34. Table4.7 35. Table4.8 36. Table4.9 37. Table5.1 38. Table6.1 39. Table6.2 40. Table7.1 41. Table7.2 42. Table7.3 43. Table7.4 44. Table7.5 45. Table7.6 46. Table8.1 47. Table8.2 48. Table8.3 49. Table8.4 50. Table8.5 51. Table8.6 52. Table9.1 53. Table9.2 54. Table9.3 55. Table9.4 56. Table9.5 57. Table9.6 58. Table9.7 59. Table10.1 60. Table10.2 61. Table10.3 62. Table10.4 63. Table10.5 64. Table11.1 65. Table11.2 66. Table12.1 67. Table12.2 68. Table12.3 69. Table12.4 70. Table14.1 71. Table15.1 72. Table15.2 73. Table16.1 74. Table16.2 75. Table17.1 76. Table18.1 77. Table18.2 78. Table18.3 79. Table18.4 80. Table19.1 81. Table19.2 82. Table20.1 83. Table21.1 84. Table22.1 85. Table22.2 86. Table22.3 87. Table22.4 88. Table22.5 89. Table22.6 90. Table23.1 91. Table23.2 92. Table23.3 93. Table23.4 94. Table23.5 95. Table25.1 96. Table25.2 97. TableA.1 98. TableA.2 99. TableA.3 100. TableB.1 101. TableB.2 102. TableB.3 103. TableB.4 Introduction WelcometothethirdeditionofLinuxCommandLineandShellScriptingBible.Likeall booksintheBible series, you can expect to find both hands-on tutorials and real-world information, as well as reference and background information that provide a context for what you are learning. This book is a fairly comprehensive resource on the Linux command line and shell commands. By the time you have completed Linux Command LineandShellScriptingBible,youwillbewellpreparedtowriteyourownshellscripts thatcanautomatepracticallyanytaskonyourLinuxsystem. WhoShouldReadThisBook If you’re a system administrator in a Linux environment, you’ll benefit greatly by knowing how to write shell scripts. The book doesn’t walk you through the process of settingupaLinuxsystem,butafteryouhaveitrunning,you’llwanttostartautomating someoftheroutineadministrativetasks.That’swhereshellscriptingcomesin,andthat’s where this book helps you out. This book demonstrates how to automate any administrativetaskusingshellscripts,frommonitoringsystemstatisticsanddatafilesto generatingreportsforyourboss. If you’re a home Linux enthusiast, you’ll also benefit from Linux Command Line and ShellScriptingBible. Nowadays, it’s easy to get lost in the graphical world of pre-built widgets.MostdesktopLinuxdistributionstrytheirbesttohidetheLinuxsystemfromthe typicaluser.However,sometimesyoumustknowwhat’sgoingonunderthehood.This bookshowsyouhowtoaccesstheLinuxcommandlinepromptandwhattodowhenyou get there. Often, performing simple tasks, such as file management, can be done more quickly from the command line than from a fancy graphical interface. You can use a wealthofcommandsfromthecommandline,andthisbookshowsyouhowtousethem. HowThisBookIsOrganized This book leads you through the basics of the Linux command line and into more complicatedtopics,suchascreatingyourownshellscripts.Thebookisdividedintofour parts,eachonebuildingonthepreviousparts. PartIassumesthatyoueitherhaveaLinuxsystemrunningorarelookingintogettinga Linuxsystem.Chapter1,“StartingwithLinuxShells,”describesthepartsofatotalLinux systemandshowshowtheshellfitsin.AfterdescribingthebasicsoftheLinuxsystem, thispartcontinueswiththefollowing: Usingaterminalemulationpackagetoaccesstheshell(Chapter2) Introducingthebasicshellcommands(Chapter3) Usingmoreadvancedshellcommandstopeekatsysteminformation(Chapter4) Understandingwhattheshellisusedfor(Chapter5) Workingwithshellvariablestomanipulatedata(Chapter6) UnderstandingtheLinuxfilesystemandsecurity(Chapter7) WorkingwithLinuxfilesystemsfromthecommandline(Chapter8) Installingandupdatingsoftwarefromthecommandline(Chapter9) UsingtheLinuxeditorstostartwritingshellscripts(Chapter10) InPartII,youbeginwritingshellscripts.Asyougothroughthechapters,you’lldothe following: Learnhowtocreateandrunshellscripts(Chapter11) Altertheprogramflowinashellscript(Chapter12) Iteratethroughcodesections(Chapter13) Handledatafromtheuserinyourscripts(Chapter14) SeedifferentmethodsforstoringanddisplayingdatafromyourScript(Chapter15) Controlhowandwhenyourshellscriptsrunonthesystem(Chapter16) Part III dives into more advanced areas of shell script programming, including these things: Creatingyourownfunctionstouseinallyourscripts(Chapter17) UtilizingtheLinuxgraphicaldesktopforinteractingwithyourscriptusers(Chapter 18) UsingadvancedLinuxcommandstofilterandparsedatafiles(Chapter19) Usingregularexpressionstodefinedata(Chapter20) Learningadvancedmethodsofmanipulatingdatainyourscripts(Chapter21) Generatingreportsfromrawdata(Chapter22) ModifyingyourshellscriptstoruninotherLinuxshells(Chapter23) Thelastsectionofthebook,PartIV,demonstrateshowtouseshellscriptsinreal-world environments.Inthispart,youwilllearnthesethings: Howtoputallthescriptingfeaturestogethertowriteyourownscripts(Chapter24) Howtostoreandretrievedatausingdatabases,accessdataontheInternet,andsend e-mailmessages(Chapter25) WritemoreadvancedshellscriptstointeractonyourLinuxsystem(Chapter26) Cautions,Tips,andNotes You will find many different organizational and typographical features throughout this bookdesignedtohelpyougetthemostoftheinformation. Caution Thisinformationisimportantandissetoffinaseparateparagraphwithaspecial icon.Cautionsprovideinformationaboutthingstowatchoutfor,whethersimply inconvenientorpotentiallyhazardoustoyourdataorsystems. Tip Tipsprovidehelpfuladvicetomakeyourworkeasierandmoreeffective.Tipsmay suggestasolutiontoaproblemorabetterwaytoaccomplishatask. Note Notesprovideadditional,ancillaryinformationthatishelpful,butsomewhatoutside ofthecurrentpresentationofinformation. Downloadablecode Youcanobtainthebook’scodefilesatwww.wiley.com/go/linuxcommandline. MinimumRequirements Linux Command Line and Shell Scripting Bible doesn’t focus on any specific Linux distribution, so you can follow along in the book using any Linux system you have available. The bulk of the book references the bash shell, which is the default shell for mostLinuxsystems. WheretoGofromHere Afteryou’vefinishedreadingLinuxCommandLineandShellScriptingBible,you’rewell on your way to incorporating Linux commands in your daily Linux work. In the everchangingworldofLinux,it’salwaysagoodideatostayintouchwithnewdevelopments. Often,Linuxdistributionschange,addingnewfeaturesandremovingolderones.Tokeep yourknowledgeofLinuxfresh,alwaysstaywell-informed.FindagoodLinuxforumsite andmonitorwhat’shappeningintheLinuxworld.ManypopularLinuxnewssites,such asSlashdotandDistrowatch,provideup-to-the-minuteinformationaboutnewadvancesin Linux. PartI TheLinuxCommandLine InThisPart 1. Chapter1StartingwithLinuxShells 1. Chapter2GettingtotheShell 1. Chapter3BasicbashShellCommands 1. Chapter4MorebashShellCommands 1. Chapter5UnderstandingtheShell 1. Chapter6UsingLinuxEnvironmentVariables 1. Chapter7UnderstandingLinuxFilePermissions 1. Chapter8ManagingFilesystems 1. Chapter9InstallingSoftware 1. Chapter10WorkingwithEditors Chapter1 StartingwithLinuxShells InThisChapter 1. WhatisLinux? 2. PartsoftheLinuxkernel 3. ExploringtheLinuxdesktop 4. VisitingLinuxdistributions BeforeyoucandiveintoworkingwiththeLinuxcommandlineandshells,you shouldfirstunderstandwhatLinuxis,whereitcamefrom,andhowitworks.This chapterwalksyouthroughwhatLinuxisandexplainswheretheshellandcommand linefitintheoverallLinuxpicture. WhatIsLinux? If you’ve never worked with Linux before, you may be confused about why so many differentversionsareavailable.I’msureyouhavebeenconfusedbyvarioustermssuchas distribution, LiveCD, and GNU when looking at Linux packages. Wading through the worldofLinuxforthefirsttimecanbeatrickyexperience.Thischaptertakessomeofthe mysteryoutoftheLinuxsystembeforeyoustartworkingoncommandsandscripts. First,fourmainpartsmakeupaLinuxsystem: TheLinuxkernel TheGNUutilities Agraphicaldesktopenvironment Applicationsoftware EachofthesepartshasaspecificjobintheLinuxsystem.Nopartisveryusefulbyitself. Figure1.1showsabasicdiagramofhowthepartsfittogethertocreatetheoverallLinux system. Figure1.1TheLinuxsystem Thissectiondescribesthesefourmainpartsindetailandgivesyouanoverviewofhow theyworktogethertocreateacompleteLinuxsystem. LookingintotheLinuxKernel The core of the Linux system is the kernel. The kernel controls all the hardware and software on the computer system, allocating hardware when necessary and executing softwarewhenrequired. Ifyou’vebeenfollowingtheLinuxworldatall,nodoubtyou’veheardthenameLinus Torvalds.LinusisthepersonresponsibleforcreatingthefirstLinuxkernelsoftwarewhen he was a student at the University of Helsinki. He intended it to be a copy of the Unix system,atthetimeapopularoperatingsystemusedatmanyuniversities. After developing the Linux kernel, Linus released it to the Internet community and solicited suggestions for improving it. This simple process started a revolution in the world of computer operating systems. Soon Linus was receiving suggestions from studentsaswellasprofessionalprogrammersfromaroundtheworld. Allowing anyone to change programming code in the kernel would result in complete chaos.Tosimplifythings,Linusactedasacentralpointforallimprovementsuggestions. It was ultimately Linus’s decision whether or not to incorporate suggested code in the kernel.ThissameconceptisstillinplacewiththeLinuxkernelcode,exceptthatinstead ofjustLinuscontrollingthekernelcode,ateamofdevelopershastakenonthetask. Thekernelisprimarilyresponsibleforfourmainfunctions: Systemmemorymanagement Softwareprogrammanagement Hardwaremanagement Filesystemmanagement Thefollowingsectionsexploreeachofthesefunctionsinmoredetail. SystemMemoryManagement Oneoftheprimaryfunctionsoftheoperatingsystemkernelismemorymanagement.Not onlydoesthekernelmanagethephysicalmemoryavailableontheserver,butitcanalso createandmanagevirtualmemory,ormemorythatdoesnotactuallyexist. Itdoesthisbyusingspaceontheharddisk,calledtheswapspace.Thekernelswapsthe contents of virtual memory locations back and forth from the swap space to the actual physical memory. This allows the system to think there is more memory available than whatphysicallyexists,asshowninFigure1.2. Figure1.2TheLinuxsystemmemorymap Thememorylocationsaregroupedintoblockscalledpages.Thekernellocateseachpage ofmemoryeitherinthephysicalmemoryortheswapspace.Thekernelthenmaintainsa tableofthememorypagesthatindicateswhichpagesareinphysicalmemoryandwhich pagesareswappedouttodisk. The kernel keeps track of which memory pages are in use and automatically copies memory pages that have not been accessed for a period of time to the swap space area (calledswappingout),evenifthere’sothermemoryavailable.Whenaprogramwantsto access a memory page that has been swapped out, the kernel must make room for it in physicalmemorybyswappingoutadifferentmemorypageandswappingintherequired page from the swap space. Obviously, this process takes time and can slow down a running process. The process of swapping out memory pages for running applications continuesforaslongastheLinuxsystemisrunning. SoftwareProgramManagement TheLinuxoperatingsystemcallsarunningprogramaprocess.Aprocesscanruninthe foreground, displaying output on a display, or it can run in the background, behind the scenes.ThekernelcontrolshowtheLinuxsystemmanagesalltheprocessesrunningon thesystem. Thekernelcreatesthefirstprocess,calledtheinitprocess,tostartallotherprocesseson the system. When the kernel starts, it loads the init process into virtual memory. As the kernelstartseachadditionalprocess,itgivesitauniqueareainvirtualmemorytostore thedataandcodethattheprocessuses. SomeLinuximplementationscontainatableofprocessestostartautomaticallyonbootup. OnLinuxsystems,thistableisusuallylocatedinthespecialfile/etc/inittabs. Other systems (such as the popular Ubuntu Linux distribution) utilize the /etc/init.d folder, which contains scripts for starting and stopping individual applications at boot time. The scripts are started via entries under the /etc/rcX.d folders, where X is a run level. TheLinuxoperatingsystemusesaninitsystemthatutilizesrunlevels.Arunlevelcanbe used to direct the init process to run only certain types of processes, as defined in the /etc/inittabsfileorthe /etc/rcX.dfolders.TherearefiveinitrunlevelsintheLinux operatingsystem. At run level 1, only the basic system processes are started, along with one console terminalprocess.Thisiscalledsingle-usermode.Single-usermodeismostoftenusedfor emergency filesystem maintenance when something is broken. Obviously, in this mode, onlyoneperson(usuallytheadministrator)canlogintothesystemtomanipulatedata. The standard init run level is 3. At this run level, most application software, such as network support software, is started. Another popular run level in Linux is run level 5. ThisistherunlevelwherethesystemstartsthegraphicalXWindowsoftwareandallows youtologinusingagraphicaldesktopwindow. TheLinuxsystemcancontroltheoverallsystemfunctionalitybycontrollingtheinitrun level.Bychangingtherunlevelfrom3to5,thesystemcanchangefromaconsole-based systemtoanadvanced,graphicalXWindowsystem. In Chapter 4, you’ll see how to use the ps command to view the processes currently runningontheLinuxsystem. HardwareManagement Still another responsibility for the kernel is hardware management. Any device that the Linuxsystemmustcommunicatewithneedsdrivercodeinsertedinsidethekernelcode. The driver code allows the kernel to pass data back and forth to the device, acting as a middlemanbetweenapplicationsandthehardware.Twomethodsareusedforinserting devicedrivercodeintheLinuxkernel: Driverscompiledinthekernel Drivermodulesaddedtothekernel Previously, the only way to insert device driver code was to recompile the kernel. Each timeyouaddedanewdevicetothesystem,youhadtorecompilethekernelcode.This process became even more inefficient as Linux kernels supported more hardware. Fortunately, Linux developers devised a better method to insert driver code into the runningkernel. Programmersdevelopedtheconceptofkernelmodulestoallowyoutoinsertdrivercode intoarunningkernelwithouthavingtorecompilethekernel.Also,akernelmodulecould be removed from the kernel when the device was finished being used. This greatly simplifiedandexpandedusinghardwarewithLinux. The Linux system identifies hardware devices as special files, called devicefiles. There arethreeclassificationsofdevicefiles: Character Block Network Character device files are for devices that can only handle data one character at a time. Most types of modems and terminals are created as character files. Block files are for devicesthatcanhandledatainlargeblocksatatime,suchasdiskdrives. Thenetworkfiletypesareusedfordevicesthatusepacketstosendandreceivedata.This includes network cards and a special loopback device that allows the Linux system to communicatewithitselfusingcommonnetworkprogrammingprotocols. Linux creates special files, called nodes, for each device on the system. All communication with the device is performed through the device node. Each node has a uniquenumberpairthatidentifiesittotheLinuxkernel.Thenumberpairincludesamajor and a minor device number. Similar devices are grouped into the same major device number.Theminordevicenumberisusedtoidentifyaspecificdevicewithinthemajor devicegroup. FilesystemManagement Unlike some other operating systems, the Linux kernel can support different types of filesystemstoreadandwritedatatoandfromharddrives.Besideshavingoveradozen filesystems of its own, Linux can read and write to and from filesystems used by other operatingsystems,suchasMicrosoftWindows.Thekernelmustbecompiledwithsupport foralltypesoffilesystemsthatthesystemwilluse.Table1.1liststhestandardfilesystems thataLinuxsystemcanusetoreadandwritedata. Table1.1LinuxFilesystems Filesystem ext ext2 ext3 ext4 hpfs jfs iso9660 minix msdos ncp nfs Description LinuxExtendedfilesystem—theoriginalLinuxfilesystem Secondextendedfilesystem,providedadvancedfeaturesoverext Thirdextendedfilesystem,supportsjournaling Fourthextendedfilesystem,supportsadvancedjournaling OS/2high-performancefilesystem IBM’sjournalingfilesystem ISO9660filesystem(CD-ROMs) MINIXfilesystem MicrosoftFAT16 Netwarefilesystem NetworkFileSystem ntfs SupportforMicrosoftNTfilesystem proc Accesstosysteminformation ReiserFS AdvancedLinuxfilesystemforbetterperformanceanddiskrecovery smb SambaSMBfilesystemfornetworkaccess sysv OlderUnixfilesystem ufs BSDfilesystem umsdos Unix-likefilesystemthatresidesontopofmsdos vfat Windows95filesystem(FAT32) XFS High-performance64-bitjournalingfilesystem AnyharddrivethataLinuxserveraccessesmustbeformattedusingoneofthefilesystem typeslistedinTable1.1. The Linux kernel interfaces with each filesystem using the Virtual File System (VFS). This provides a standard interface for the kernel to communicate with any type of filesystem.VFScachesinformationinmemoryaseachfilesystemismountedandused. TheGNUUtilities Besideshavingakerneltocontrolhardwaredevices,acomputeroperatingsystemneeds utilitiestoperformstandardfunctions,suchascontrollingfilesandprograms.WhileLinus created the Linux system kernel, he had no system utilities to run on it. Fortunately for him,atthesametimehewasworking,agroupofpeoplewereworkingtogetheronthe Internet trying to develop a standard set of computer system utilities that mimicked the popularUnixoperatingsystem. The GNU organization (GNU stands for GNU’s Not Unix) developed a complete set of Unix utilities, but had no kernel system to run them on. These utilities were developed underasoftwarephilosophycalledopensourcesoftware(OSS). The concept of OSS allows programmers to develop software and then release it to the world with no licensing fees attached. Anyone can use the software, modify it, or incorporate it into his or her own system without having to pay a license fee. Uniting Linus’s Linux kernel with the GNU operating system utilities created a complete, functional,freeoperatingsystem. WhilethebundlingoftheLinuxkernelandGNUutilitiesisoftenjustcalledLinux,you will see some Linux purists on the Internet refer to it as the GNU/Linux system to give credittotheGNUorganizationforitscontributionstothecause. TheCoreGNUUtilities TheGNUprojectwasmainlydesignedforUnixsystemadministratorstohaveaUnix-like environment available. This focus resulted in the project porting many common Unix systemcommandlineutilities.ThecorebundleofutilitiessuppliedforLinuxsystemsis calledthecoreutilspackage. TheGNUcoreutilspackageconsistsofthreeparts: Utilitiesforhandlingfiles Utilitiesformanipulatingtext Utilitiesformanagingprocesses Each of these three main groups of utilities contains several utility programs that are invaluabletotheLinuxsystemadministratorandprogrammer.Thisbookcoverseachof theutilitiescontainedintheGNUcoreutilspackageindetail. TheShell The GNU/Linux shell is a special interactive utility. It provides a way for users to start programs, manage files on the filesystem, and manage processes running on the Linux system. The core of the shell is the command prompt. The command prompt is the interactivepartoftheshell.Itallowsyoutoentertextcommands,andthenitinterpretsthe commandsandexecutestheminthekernel. The shell contains a set of internal commands that you use to control things such as copyingfiles,movingfiles,renamingfiles,displayingtheprogramscurrentlyrunningon thesystem,andstoppingprogramsrunningonthesystem.Besidestheinternalcommands, the shell also allows you to enter the name of a program at the command prompt. The shellpassestheprogramnameofftothekerneltostartit. You can also group shell commands into files to execute as a program. Those files are called shellscripts. Any command that you can execute from the command line can be placedinashellscriptandrunasagroupofcommands.Thisprovidesgreatflexibilityin creating utilities for commonly run commands, or processes that require several commandsgroupedtogether. There are quite a few Linux shells available to use on a Linux system. Different shells havedifferentcharacteristics,somebeingmoreusefulforcreatingscriptsandsomebeing moreusefulformanagingprocesses.ThedefaultshellusedinallLinuxdistributionsisthe bash shell. The bash shell was developed by the GNU project as a replacement for the standard Unix shell, called the Bourne shell (after its creator). The bash shell name is a playonthiswording,referredtoasthe“Bourneagainshell.” Inadditiontothebashshell,wewillcoverseveralotherpopularshellsinthisbook.Table 1.2liststhedifferentshellswewillexamine. Table1.2LinuxShells Shell Description Asimple,lightweightshellthatrunsinlow-memoryenvironmentsbuthasfull ash compatibilitywiththebashshell AprogrammingshellcompatiblewiththeBourneshellbutsupportingadvanced korn programmingfeatureslikeassociativearraysandfloating-pointarithmetic AshellthatincorporateselementsfromtheCprogramminglanguageintoshell tcsh scripts Anadvancedshellthatincorporatesfeaturesfrombash,tcsh,andkorn,providing zsh advancedprogrammingfeatures,sharedhistoryfiles,andthemedprompts MostLinuxdistributionsincludemorethanoneshell,althoughusuallytheypickoneof them to be the default. If your Linux distribution includes multiple shells, feel free to experimentwithdifferentshellsandseewhichonefitsyourneeds. TheLinuxDesktopEnvironment In the early days of Linux (the early 1990s) all that was available was a simple text interfacetotheLinuxoperatingsystem.Thistextinterfaceallowedadministratorstostart programs,controlprogramoperations,andmovefilesaroundonthesystem. With the popularity of Microsoft Windows, computer users expected more than the old textinterfacetoworkwith.ThisspurredmoredevelopmentintheOSScommunity,and theLinuxgraphicaldesktopsemerged. Linux is famous for being able to do things in more than one way, and no place is this morerelevantthaningraphicaldesktops.Thereareaplethoraofgraphicaldesktopsyou can choose from in Linux. The following sections describe a few of the more popular ones. TheXWindowSystem Twobasicelementscontrolyourvideoenvironment:thevideocardinyourPCandyour monitor.Todisplayfancygraphicsonyourcomputer,theLinuxsoftwareneedstoknow how to talk to both of them. The X Window software is the core element in presenting graphics. The X Window software is a low-level program that works directly with the video card andmonitorinthePC,anditcontrolshowLinuxapplicationscanpresentfancywindows andgraphicsonyourcomputer. Linuxisn’ttheonlyoperatingsystemthatusesXWindow;versionsarewrittenformany different operating systems. In the Linux world, several different software packages can implementit. ThemostpopularpackageisX.org.Itprovidesanopensourcesoftwareimplementation oftheXWindowsystemandsupportsmanyofthenewervideocardsusedtoday. TwootherXWindowpackagesaregaininginpopularity.TheFedoraLinuxdistributionis experimenting with the Wayland software, and the Ubuntu Linux distribution has developedtheMirdisplayserverforusewithitsdesktopenvironment. When you first install a Linux distribution, it attempts to detect your video card and monitor, and then it creates an X Window configuration file that contains the required information. During installation, you may notice a time when the installation program scansyourmonitorforsupportedvideomodes.Sometimes,thiscausesyourmonitortogo blank for a few seconds. Because there are lots of different types of video cards and monitors,thisprocesscantakeawhiletocomplete. ThecoreXWindowsoftwareproducesagraphicaldisplayenvironment,butnothingelse. Although this is fine for running individual applications, it is not useful for day-to-day computer use. No desktop environment allows users to manipulate files or launch programs. To do that, you need a desktop environment on top of the X Window system software. TheKDEDesktop TheKDesktopEnvironment(KDE)wasfirstreleasedin1996asanopensourceproject toproduceagraphicaldesktopsimilartotheMicrosoftWindowsenvironment.TheKDE desktopincorporatesallthefeaturesyouareprobablyfamiliarwithifyouareaWindows user. Figure 1.3 shows a sample KDE 4 desktop running in the openSUSE Linux distribution. Figure1.3TheKDE4desktoponanopenSUSELinuxsystem TheKDEdesktopallowsyoutoplacebothapplicationandfileiconsinaspecialareaon the desktop. If you click an application icon, the Linux system starts the application. If youclickafileicon,theKDEdesktopattemptstodeterminewhatapplicationtostartto handlethefile. ThebaratthebottomofthedesktopiscalledthePanel.ThePanelconsistsoffourparts: TheKmenu:MuchliketheWindowsStartmenu,theKmenucontainslinkstostart installedapplications. Programshortcuts:Thesearequicklinkstostartapplicationsdirectlyfromthe Panel. Thetaskbar:Thetaskbarshowsiconsforapplicationscurrentlyrunningonthe desktop. Applets:ThesearesmallapplicationsthathaveaniconinthePanelthatoftencan changedependingoninformationfromtheapplication. The Panel features are similar to what you would find in Windows. In addition to the desktopfeatures,theKDEprojecthasproducedawideassortmentofapplicationsthatrun intheKDEenvironment. TheGNOMEDesktop The GNU Network Object Model Environment (GNOME) is another popular Linux desktop environment. First released in 1999, GNOME has become the default desktop environmentformanyLinuxdistributions.(However,themostpopularisRedHatLinux.) AlthoughGNOMEchosetodepartfromthestandardMicrosoftWindowslook-and-feel,it incorporatesmanyfeaturesthatmostWindowsusersarecomfortablewith: Adesktopareaforicons Apanelareaforshowingrunningapplications Drag-and-dropcapabilities Figure1.4showsthestandardGNOMEdesktopusedintheCentOSLinuxdistribution. Figure1.4AGNOMEdesktoponaCentOSLinuxsystem Not to be outdone by KDE, the GNOME developers have also produced a host of graphicalapplicationsthatintegratewiththeGNOMEdesktop. TheUnityDesktop If you’re using the Ubuntu Linux distribution, you’ll notice that it’s somewhat different from both the KDE and GNOME desktop environments. Canonical, the company responsible for developing Ubuntu, has decided to embark on its own Linux desktop environment,calledUnity. The Unity desktop gets its name from the goal of the project — to provide a single desktop experience for workstations, tablet devices, and mobile devices. The Unity desktop works the same whether you’re running Ubuntu on a workstation or a mobile phone!Figure1.5showsanexampleoftheUnitydesktopinUbuntu14.04LTS. Figure1.5TheUnitydesktopontheUbuntuLinuxdistribution OtherDesktops The downside to a graphical desktop environment is that it requires a fair amount of system resources to operate properly. In the early days of Linux, a hallmark and selling feature of Linux was its ability to operate on older, less powerful PCs that the newer Microsoft desktop products couldn’t run on. However, with the popularity of KDE and GNOMEdesktops,thishaschanged,becauseittakesjustasmuchmemorytorunaKDE orGNOMEdesktopasthelatestMicrosoftdesktopenvironment. If you have an older PC, don’t be discouraged. The Linux developers have banded together to take Linux back to its roots. They’ve created several low-memory–oriented graphicaldesktopapplicationsthatprovidebasicfeaturesthatrunperfectlyfineonolder PCs. Althoughthesegraphicaldesktopsdon’thaveaplethoraofapplicationsdesignedaround them,theystillrunmanybasicgraphicalapplicationsthatsupportfeaturessuchasword processing,spreadsheets,databases,drawing,and,ofcourse,multimediasupport. Table1.3 shows some of the smaller Linux graphical desktop environments that can be usedonlower-poweredPCsandlaptops. Table1.3OtherLinuxGraphicalDesktops Desktop Fluxbox Description Abare-bonesdesktopthatdoesn’tincludeaPanel,onlyapop-upmenuto launchapplications Xfce Adesktopthat’ssimilartotheKDEdesktop,butwithfewergraphicsforlowmemoryenvironments Joe’sWindowManager,averylightweightdesktopidealforlow-memoryand JWM low-diskspaceenvironments SupportssomeadvanceddesktopfeaturessuchasvirtualdesktopsandPanels, Fvwm butrunsinlow-memoryenvironments fvwm95 Derivedfromfvwm,butmadetolooklikeaWindows95desktop These graphical desktop environments are not as fancy as the KDE and GNOME desktops,buttheyprovidebasicgraphicalfunctionalityjustfine.Figure1.6showswhat theJWMdesktopusedinthePuppyLinuxantiXdistributionlookslike. Figure1.6TheJWMdesktopasseeninthePuppyLinuxdistribution IfyouareusinganolderPC,tryaLinuxdistributionthatusesoneofthesedesktopsand seewhathappens.Youmaybepleasantlysurprised. LinuxDistributions NowthatyouhaveseenthefourmaincomponentsrequiredforacompleteLinuxsystem, youmaybewonderinghowyouaregoingtogetthemallputtogethertomakeaLinux system.Fortunately,otherpeoplehavealreadydonethatforyou. A complete Linux system package is called a distribution. Many different Linux distributionsareavailabletomeetjustaboutanycomputingrequirementyoucouldhave. Most distributions are customized for a specific user group, such as business users, multimedia enthusiasts, software developers, or average home users. Each customized distributionincludesthesoftwarepackagesrequiredtosupportspecializedfunctions,such as audio- and video-editing software for multimedia enthusiasts, or compilers and integrateddevelopmentenvironments(IDEs)forsoftwaredevelopers. ThedifferentLinuxdistributionsareoftendividedintothreecategories: FullcoreLinuxdistributions Specializeddistributions LiveCDtestdistributions The following sections describe these different types of Linux distributions and show someexamplesofLinuxdistributionsineachcategory. CoreLinuxDistributions AcoreLinuxdistributioncontainsakernel,oneormoregraphicaldesktopenvironments, and just about every Linux application that is available, precompiled for the kernel. It providesone-stopshoppingforacompleteLinuxinstallation.Table1.4showssomeofthe morepopularcoreLinuxdistributions. Table1.4CoreLinuxDistributions Distribution Slackware RedHat Fedora Gentoo openSUSE Debian Description OneoftheoriginalLinuxdistributionsets,popularwithLinuxgeeks AcommercialbusinessdistributionusedmainlyforInternetservers Aspin-offfromRedHatbutdesignedforhomeuse AdistributiondesignedforadvancedLinuxusers,containingonlyLinux sourcecode Differentdistributionsforbusinessandhomeuse PopularwithLinuxexpertsandcommercialLinuxproducts IntheearlydaysofLinux,adistributionwasreleasedasasetoffloppydisks.Youhadto downloadgroupsoffilesandthencopythemontodisks.Itwouldusuallytake20ormore diskstomakeanentiredistribution!Needlesstosay,thiswasapainfulexperience. Nowadays,withhomecomputerscommonlyhavingCDandDVDplayersbuiltin,Linux distributionsarereleasedaseitheraCDsetorasingleDVD.ThismakesinstallingLinux mucheasier. However,beginnersstilloftenrunintoproblemswhentheyinstalloneofthecoreLinux distributions. To cover just about any situation in which someone might want to use Linux, a single distribution must include lots of application software. They include everything from high-end Internet database servers to common games. Because of the quantity of applications available for Linux, a complete distribution often takes four or moreCDs. AlthoughhavinglotsofoptionsavailableinadistributionisgreatforLinuxgeeks,itcan become a nightmare for beginning Linux users. Most distributions ask a series of questions during the installation process to determine which applications to load by default, what hardware is connected to the PC, and how to configure the hardware. Beginnersoftenfindthesequestionsconfusing.Asaresult,theyofteneitherloadwaytoo many programs on their computer or don’t load enough and later discover that their computerwon’tdowhattheywantitto. Fortunatelyforbeginners,there’samuchsimplerwaytoinstallLinux. SpecializedLinuxDistributions AnewsubgroupofLinuxdistributionshasstartedtoappear.Thesearetypicallybasedon one of the main distributions but contain only a subset of applications that would make senseforaspecificareaofuse. In addition to providing specialized software (such as only office products for business users), customized Linux distributions also attempt to help beginning Linux users by autodetecting and autoconfiguring common hardware devices. This makes installing Linuxamuchmoreenjoyableprocess. Table 1.5 shows some of the specialized Linux distributions available and what they specializein. Table1.5SpecializedLinuxDistributions Distribution Description CentOS AfreedistributionbuiltfromtheRedHatEnterpriseLinuxsourcecode Ubuntu Afreedistributionforschoolandhomeuse PCLinuxOS Afreedistributionforhomeandofficeuse Mint Afreedistributionforhomeentertainmentuse dyne:bolic AfreedistributiondesignedforaudioandMIDIapplications PuppyLinux AfreesmalldistributionthatrunswellonolderPCs That’s just a small sampling of specialized Linux distributions. There are literally hundredsofspecializedLinuxdistributions,andmorearepoppingupallthetimeonthe Internet.Nomatterwhatyourspecialty,you’llprobablyfindaLinuxdistributionmadefor you. Many of the specialized Linux distributions are based on the Debian Linux distribution. TheyusethesameinstallationfilesasDebianbutpackageonlyasmallfractionofafullblownDebiansystem. TheLinuxLiveCD ArelativelynewphenomenonintheLinuxworldisthebootableLinuxCDdistribution. ThisletsyouseewhataLinuxsystemislikewithoutactuallyinstallingit.Mostmodern PCs can boot from a CD instead of the standard hard drive. To take advantage of this, some Linux distributions create a bootable CD that contains a sample Linux system (calledaLinuxLiveCD).BecauseofthelimitationsofthesingleCDsize,thesamplecan’t containacompleteLinuxsystem,butyou’dbesurprisedatallthesoftwaretheycancram inthere.TheresultisthatyoucanbootyourPCfromtheCDandrunaLinuxdistribution withouthavingtoinstallanythingonyourharddrive! ThisisanexcellentwaytotestvariousLinuxdistributionswithouthavingtomesswith yourPC.JustpopinaCDandboot!AlltheLinuxsoftwarewillrundirectlyfromtheCD. You can download lots of Linux LiveCDs from the Internet and burn onto a CD to test drive. Table1.6showssomepopularLinuxLiveCDsthatareavailable. Table1.6LinuxLiveCDDistributions Distribution Description Knoppix AGermanLinux,thefirstLinuxLiveCDdeveloped PCLinuxOS Full-blownLinuxdistributiononaLiveCD Ubuntu AworldwideLinuxproject,designedformanylanguages Slax AliveLinuxCDbasedonSlackwareLinux PuppyLinux Afull-featuredLinuxdesignedforolderPCs Youmaynoticeafamiliarityinthistable.ManyspecializedLinuxdistributionsalsohave aLinuxLiveCDversion.SomeLinuxLiveCDdistributions,suchasUbuntu,allowyouto installtheLinuxdistributiondirectlyfromtheLiveCD.Thisenablesyoutobootwiththe CD,testdrivetheLinuxdistribution,andthenifyoulikeit,installitonyourharddrive. Thisfeatureisextremelyhandyanduser-friendly. As with all good things, Linux LiveCDs have a few drawbacks. Because you access everything from the CD, applications run more slowly, especially if you’re using older, slowercomputersandCDdrives.Also,becauseyoucan’twritetotheCD,anychanges youmaketotheLinuxsystemwillbegonethenexttimeyoureboot. ButadvancesarebeingmadeintheLinuxLiveCDworldthatwillhelptosolvesomeof theseproblems.Theseadvancesincludetheabilityto: CopyLinuxsystemfilesfromtheCDtomemory Copysystemfilestoafileontheharddrive StoresystemsettingsonaUSBmemorystick StoreusersettingsonaUSBmemorystick Some Linux LiveCDs, such as Puppy Linux, are designed with a minimum number of Linux system files. The LiveCD boot scripts copy them directly into memory when the CDboots.ThisallowsyoutoremovetheCDfromthecomputerassoonasLinuxboots. Notonlydoesthismakeyourapplicationsrunmuchfaster(becauseapplicationsrunfaster frommemory),butitalsogivesyouafreeCDtraytouseforrippingaudioCDsorplaying videoDVDsfromthesoftwareincludedinPuppyLinux. OtherLinuxLiveCDsuseanalternativemethodthatallowsyoutoremovetheCDfrom the tray after booting. It involves copying the core Linux files onto the Windows hard driveasasinglefile.AftertheCDboots,itlooksforthatfileandreadsthesystemfiles from it. The dyne:bolic Linux LiveCD uses this technique, which is called docking. Of course,youmustcopythesystemfiletoyourharddrivebeforeyoucanbootfromtheCD. A very popular technique for storing data from a live Linux CD session is to use a commonUSBmemorystick(alsocalledaflashdriveorathumbdrive).Justaboutevery Linux LiveCD can recognize a plugged-in USB memory stick (even if the stick is formattedforWindows)andreadandwritefilestoandfromit.Thisallowsyoutoboota LinuxLiveCD,usetheLinuxapplicationstocreatefiles,storethosefilesonyourmemory stick, and then access them from your Windows applications later (or from a different computer).Howcoolisthat? Summary ThischapterdiscussedtheLinuxsystemandthebasicsofhowitworks.TheLinuxkernel is the core of the system, controlling how memory, programs, and hardware all interact withoneanother.TheGNUutilitiesarealsoanimportantpieceintheLinuxsystem.The Linuxshell,whichisthemainfocusofthisbook,ispartoftheGNUcoreutilities.The chapteralsodiscussedthefinalpieceofaLinuxsystem,theLinuxdesktopenvironment. Things have changed over the years, and Linux now supports several graphical desktop environments. ThechapteralsodiscussedthevariousLinuxdistributions.ALinuxdistributionbundles the various parts of a Linux system into a simple package that you can easily install on your PC. The Linux distribution world consists of full-blown Linux distributions that includejustabouteveryapplicationimaginable,aswellasspecializedLinuxdistributions thatincludeapplicationsfocusedonlyonaspecialfunction.TheLinuxLiveCDcrazehas created another group of Linux distributions that allow you to easily test-drive Linux withoutevenhavingtoinstallitonyourharddrive. In the next chapter, you look at what you need to start your command line and shell scriptingexperience.You’llseewhatyouneedtodotogettotheLinuxshellutilityfrom yourfancygraphicaldesktopenvironment.Thesedays,that’snotalwaysaneasything. Chapter2 GettingtotheShell InThisChapter 1. Accessingthecommandline 2. ReachingCLIviaaLinuxconsoleterminal 3. ReachingCLIviaagraphicalterminalemulator 4. UsingtheGNOMEterminalemulator 5. UsingtheKonsoleterminalemulator 6. Usingthextermterminalemulator IntheolddaysofLinux,allyouhadtoworkwithwastheshell.System administrators,programmers,andsystemusersallsatatsomethingcalledaLinux consoleterminalenteringshellcommandsandviewingtextoutput.Thesedays,with graphicaldesktopenvironments,it’sgettinghardertofindashellpromptonthe systeminordertoentershellcommands.Thischapterdiscusseswhatisrequiredto reachacommandlineenvironment.Itwalksyouthroughtheterminalemulation packagesthatyoumayrunintointhevariousLinuxdistributions. ReachingtheCommandLine Beforethedaysofgraphicaldesktops,theonlywaytointeractwithaUnixsystemwas throughatextcommandlineinterface(CLI)providedbytheshell.TheCLIallowedtext inputonlyandcoulddisplayonlytextandrudimentarygraphicsoutput. Becauseoftheserestrictions,outputdeviceswerenotveryfancy.Often,youneededonly a simple dumb terminal to interact with the Unix system. A dumb terminal was usually nothing more than a monitor and keyboard connected to the Unix system via a communication cable (usually a multi-wire serial cable). This simple combination providedaneasywaytoentertextdataintotheUnixsystemandviewtextresults. As you well know, things are significantly different in today’s Linux environment. Just about every Linux distribution uses some type of graphical desktop environment. However,toentershellcommands,youstillneedatextdisplaytoaccesstheshell’sCLI. The problem now is getting to one. Sometimes finding a way to get a CLI in a Linux distributionisnotaneasytask. ConsoleTerminals OnewaytogettoaCLIistotaketheLinuxsystemoutofgraphicaldesktopmodeand placeitintextmode.ThisprovidesnothingmorethanasimpleshellCLIonthemonitor, justlikethedaysbeforegraphicaldesktops.ThismodeiscalledtheLinuxconsolebecause itemulatestheolddaysofahard-wiredconsoleterminalandisadirectinterfacetothe Linuxsystem. WhentheLinuxsystemstarts,itautomaticallycreatesseveralvirtualconsoles.Avirtual consoleisaterminalsessionthatrunsinLinuxsystemmemory.Insteadofhavingseveral dumbterminalsconnectedtothecomputer,mostLinuxdistributionsstartfiveorsix(or sometimes even more) virtual consoles that you can access from a single computer keyboardandmonitor. GraphicalTerminals Thealternativetousingavirtualconsoleterminalistouseaterminalemulationpackage from within the Linux graphical desktop environment. A terminal emulation package simulatesworkingonaconsoleterminal,butwithinadesktopgraphicalwindow.Figure 2.1 shows an example of a terminal emulator running in a Linux graphical desktop environment. Figure2.1AsimpleterminalemulatorrunningonaLinuxdesktop Graphical terminal emulation is responsible only for a portion of the Linux graphical experience.Asawhole,theexperienceisaccomplishedviaseveralcomponents,including graphical terminal emulation software (called a client). Table 2.1 shows the different componentsintheLinuxgraphicaldesktopenvironment. Table2.1GraphicalInterfaceElements Name Client Examples Graphicalterminalemulator, desktopenvironment,network browser Display Server Mir,WaylandCompositor, Xserver Window Manager Compiz,Metacity,Kwin Widgets Library Athena(Xaw),XIntrinsics Description Anapplicationthatrequestsgraphical services Elementthatmanagesthedisplay(screen)and theinputdevices(keyboard,mouse,touch screen) Elementthataddsborderstowindowsand providesfeaturestomoveandmanage windows Elementthataddsmenusandappearance itemsfordesktopenvironmentclients For dealing with the command line from the desktop, the focus is on the graphical terminalemulator.YoucanthinkofgraphicalterminalemulatorsasCLIterminals“inthe GUI”andvirtualconsoleterminalsasCLIterminals“outsidetheGUI.”Understandingthe variousterminalsandtheirfeaturescanenhanceyourcommandlineexperience. AccessingCLIviaaLinuxConsoleTerminal IntheearlydaysofLinux,whenyoubootedupyoursystemyouwouldseealoginprompt onyourmonitor,andthat’sall.Asmentionedearlier,thisiscalledtheLinuxconsole.It wastheonlyplaceyoucouldentercommandsforthesystem. Even though several virtual consoles are created at boot time, many Linux distributions switch to a graphical environment after the boot sequence completes. This provides the user with a graphical login and desktop experience. Therefore, in this case, accessing a virtualconsoleisdonemanually. In most Linux distributions, you can access one of the Linux virtual consoles using a simplekeystrokecombination.Usually,youmustholddowntheCtrl+Altkeycombination and then press a function key (F1 through F7) for the virtual console you want to use. Function key F2 produces virtual console 2, key F3 produces virtual console 3, key F4 producesvirtualconsole4,andsoon. Note LinuxdistributionstypicallyusetheCtrl+AltkeycombinationwitheitherF1orF7to reachthegraphicalinterface.UbuntuusesF7,whileRHELusesF1.Itisbesttotest andseewhereyourdistributionputsthegraphicalinterface. Text mode virtual consoles use the whole screen and start with the text login screen displayed.AnexampleofatextloginscreenfromavirtualconsoleisshowninFigure2.2. Figure2.2Linuxvirtualconsoleloginscreen NoticeinFigure2.2thewordstty2attheendofthefirsttextline.The2intty2indicates thatitisvirtualconsole2andwasreachedbypressingtheCtrl+Alt+F2keysequence.tty stands for teletypewriter. Teletypewriter is an old term, indicating a machine used for sendingmessages. Note NotallLinuxdistributionsshowthevirtualconsole’sttynumberattheloginscreen. You log into a console terminal by entering your user ID after the login: prompt and typingyourpasswordafterthe Password:prompt.Ifyouhaveneverloggedinthisway before, be aware that typing your password is a different experience than in a graphical environment. In a graphical environment, you may see dots or asterisks indicating the password characters as you type. However, at the virtual console, nothing is displayed whenyoutypeyourpassword. Afterloggingintoavirtualconsole,youaretakentotheLinuxCLI.Keepinmindthat, within the Linux virtual console, you do not have the ability to run any graphical programs. Afteryouhaveloggedintoavirtualconsole,youcankeepitactiveandswitchtoanother virtualconsolewithoutlosingyouractivesession.Youcanswitchbetweenallthevirtual consoles, with multiple active sessions running. This feature provides a great deal of flexibilitywhileyouworkattheCLI. Additionalflexibilitydealswiththevirtualconsole’sappearance.Eventhoughitisatext modeconsoleterminal,youcanmodifythetextandbackgroundcolors. Forexample,itmaybeeasieronyoureyestosetthebackgroundoftheterminaltowhite andthetexttoblack.Afteryouhaveloggedin,youcanaccomplishthismodificationina coupleofways.Onewayistotypeinthecommandsetterm-inversescreenonandpress theEnterkey,asshowninFigure2.3.Noticeinthefigurethatthe inversescreenfeature isbeingturnedonusingtheoptionon.Youcanalsoturnitoffusingtheoffoption. Figure2.3Linuxvirtualconsolewithinversescreenbeingturnedon Another way is to type two commands, one after the other. Type setterm-background white and press Enter, and then type setterm -foreground black and press Enter. Be carefulbecause,whenyouchangeyourterminalbackgroundfirst,itmaybehardtosee thecommandsyouaretyping. Withthecommandsintheprecedingparagraph,youarenotturningfeaturesonandoff,as with inversescreen.Instead,youhaveachoiceofeightcolors.Thechoicesare black, red, green, yellow, blue, magenta, cyan, and white (which looks gray on some distributions). You can get rather creative with your plain text mode console terminals. Table2.2 shows some options you can use with the settermcommandtohelpimprove yourconsoleterminal’sreadabilityorappearance. Table2.2settermOptionsforForegroundandBackgroundAppearance Option ParameterChoices Description -background black,red,green, yellow,blue,magenta, cyan,orwhite Changestheterminal’sbackgroundcolorto theonespecified -foreground black,red,green, yellow,blue,magenta, cyan,orwhite Changestheterminal’sforegroundcolor, specificallytext,totheonespecified inversescreen onoroff -reset None -store None Switchesthebackgroundcolortothe foregroundcolorandtheforegroundcolor tothebackgroundcolor Changestheterminalappearancebacktoits defaultsettingandclearsthescreen Setsthecurrentterminal’sforegroundand backgroundcolorsasthevaluestobeused for-reset Virtual console terminals are great for accessing the CLI outside the GUI. However, sometimes, you need to access the CLI and run graphical programs. Using a terminal emulationpackagesolvesthisproblemandisapopularwaytoaccesstheshellCLIfrom withintheGUI.Thefollowingsectionsdescribecommonsoftwarepackagesthatprovide graphicalterminalemulation. AccessingCLIviaGraphicalTerminalEmulation The graphical desktop environment offers a great deal more variety for CLI access than the virtual console terminal does. Many graphical terminal emulator packages are available for the graphical environment. Each package provides its own unique set of features and options. Some popular graphical terminal emulator packages are shown in Table2.3alongwiththeirwebsites. Table2.3PopularGraphicalTerminalEmulatorPackages Name Website Eterm http://www.eterm.org FinalTerm http://finalterm.org GNOMETerminal https://help.gnome.org/users/gnome-terminal/stable Guake https://github.com/Guake/guake KonsoleTerminal http://konsole.kde.org LillyTerm http://lilyterm.luna.com.tw/index.html LXTerminal http://wiki.lxde.org/en/LXTerminal mrxvt https://code.google.com/p/mrxvt ROXTerm http://roxterm.sourceforge.net rxvt http://sourceforge.net/projects/rxvt rxvt-unicode http://software.schmorp.de/pkg/rxvt-unicode Sakura https://launchpad.net/sakura st http://st.suckless.org Terminator https://launchpad.net/terminator Terminology http://www.enlightenment.org/p.php?p=about/terminology tilda http://tilda.sourceforge.net/tildaabout.php UXterm http://manpages.ubuntu.com/manpages/gutsy/man1/uxterm.1.html Wterm http://sourceforge.net/projects/wterm xterm http://invisible-island.net/xterm Xfce4Terminal http://docs.xfce.org/apps/terminal/start Yakuake http://extragear.kde.org/apps/yakuake Although many graphical terminal emulator packages are available, the focus in this chapterisonthreecommonlyusedones.OfteninstalledinLinuxdistributionsbydefault, theyareGNOMETerminal,KonsoleTerminal,andxterm. UsingtheGNOMETerminalEmulator GNOMETerminalistheGNOMEdesktopenvironment’sdefaultterminalemulator.Many distributions,suchasRHEL,Fedora,andCentOS,usetheGNOMEdesktopenvironment by default, and therefore use GNOME Terminal by default. However, other desktop environments, such as Ubuntu Unity, also use the GNOME terminal as their default terminal emulator package. It is fairly easy to use and a good terminal emulator for individuals who are new to Linux. This chapter section walks you through the various partsofaccessing,configuringandusingtheGNOMEterminalemulator. AccessingtheGNOMETerminal Each graphical desktop environment has different methods for accessing the GNOME terminalemulator.ThissectionlooksataccessingtheGNOMETerminalintheGNOME, Unity,andKDEdesktopenvironments. Note IfyouareusingadifferentdesktopenvironmentthantheoneslistedinTable2.3,you mustlookthroughthevariousmenusofferedinyourenvironmenttofindthe GNOMEterminalemulator.Inthemenus,itistypicallynamedTerminal In the GNOME desktop environment, accessing the GNOME Terminal is fairly straightforward. From the menu system in the upper-left corner of the window, click Applications, then select System Tools from the drop-down menu, and finally click Terminal. Written in shorthand, the directions look like the following: Applications⇨SystemTools⇨Terminal. Refer to Figure 2.1 to see a picture of the GNOME Terminal. It was accessed in a GNOMEdesktopenvironmentonaCentOSdistribution. In the Unity desktop environment, accessing the GNOME terminal takes a little more effort. The simplest access method is Dash⇨Search and type Terminal. The GNOME terminalshowsupintheDashhomeareaasanapplicationnamed Terminal.Clickthat icontoopentheGNOMEterminalemulator. Tip InsomeLinuxdistributiondesktopenvironments,suchasUbuntu’sUnity,youcan quicklyaccesstheGNOMEterminalusingtheshortcutkeycombinationCtrl+Alt+T. IntheKDEdesktopenvironment,theKonsoleterminalemulatoristhedefaultemulator. Therefore,youmustdigdownthroughthemenustoaccessGNOMETerminal.Startwith the icon labeled Kickoff Application Launcher in the lower-left corner of the screen andthenclickApplications⇨Utilities⇨Terminal. In most desktop environments, you can create a launcher for accessing GNOME Terminal. A launcher is an icon you create on your desktop that allows you to start a chosen application. This is a great feature that allows you to quickly access a terminal emulator in the graphical desktop. It is especially helpful if you do not want to use shortcutkeysortheshortcutkeyfeatureisnotavailableinyourdesktopenvironmentof choice. Forexample,intheGNOMEdesktopenvironment,tocreatealauncher,right-clickyour mouse in the middle of the desktop area; a drop-down menu appears. Select Create Launcher… from the menu; the Create Launcher application window opens. In the Type field,selectApplication.TypeanameforyouriconintheNamefield.IntheCommand field,typegnome-terminal.Click Oktosaveyournewlauncher.Aniconwiththename yougavethelaunchernowappearsonyourdesktop.Double-clickittoopentheGNOME terminalemulator. Note Whenyoutypegnome-terminalintheCommandfield,youaretypingtheshell commandforstartingtheGNOMEterminalemulator.YoulearninChapter3howto addspecialoptionstocommands,suchasgnome-terminal,toprovidespecial configurationoptions,andhowtoviewalltheoptionsavailabletoyou. Severalconfigurationoptionsareprovidedbymenusandshort-cutkeysintheapplication, whichyoucanapplyafteryougettheGNOMEterminalemulationstarted.Understanding theseoptionscanenhanceyourGNOMETerminalCLIexperience. TheMenuBar The GNOME Terminal menu bar contains the configuration and customization options youneedtomakeyourGNOMETerminaljustthewayyouwantit.Thefollowingtables briefly describe the different configuration options in the menu bar and shortcut keys associatedwiththeoptions. Note AsyoureadthroughtheseGNOMETerminalmenuoptions,keepinmindthatyour Linuxdistribution’sGNOMETerminalmayhaveslightlydifferentmenuoptions available.ThisisbecauseseveralLinuxdistributionsuseolderversionsofGNOME Terminal. Table2.4 shows the configuration options available within the GNOME Terminal File menusystem.The FilemenuitemcontainsitemstocreateandmanageyouroverallCLI terminalsessions. Table2.4TheFileMenu Name ShortcutKey Description StartsanewshellsessioninanewGNOMETerminal window Startsanewshellsessioninanewtabintheexisting GNOMETerminalwindow Customizesasessionandsavesasaprofile,whichcanbe recalledforlateruse Open Terminal Shift+Ctrl+N OpenTab Shift+Ctrl+T New Profile None Save Contents None Savesthescrollbackbuffercontentstoatextfile CloseTab Shift+Ctrl+W Closesthecurrenttabsession Close Window Shift+Ctrl+Q ClosesthecurrentGNOMETerminalsession Noticethat,asinanetworkbrowser,youcanopennewtabswithintheGNOMETerminal session to start a whole new CLI session. Each tab session is considered to be an independentCLIsession. Tip YoudonothavetoclickthroughthemenutoreachoptionsintheFilemenu.Mostof theitemsarealsoavailablebyright-clickinginthesessionarea. TheEditmenucontainsitems,showninTable2.5,forhandlingtextwithinthetabs.You canuseyourmousetocopyandpastetextanywherewithinthesessionwindow. Table2.5TheEditMenu Name ShortcutKey Description Copy Shift+Ctrl+C CopiesselectedtexttotheGNOMEclipboard Paste Shift+Ctrl+V PastestextfromtheGNOMEclipboardintoasession PasteFilenames Properlypastescopiedfilenamesandtheirpaths SelectAll None Selectsoutputintheentirescrollbackbuffer Profiles None Adds,deletes,ormodifiesGNOMETerminalprofiles Keyboard CreateskeycombinationstoquicklyaccessGNOME None Shortcuts Terminalfeatures Profile Preferences None Editsthecurrentsessionprofile The Paste Filenames menu option is available only in later versions of GNOME Terminal.Therefore,youmaynotseethatmenuoptiononyoursystem. The Viewmenu,showninTable2.6,containsitemsforcontrollinghowtheCLIsession windowsappear.Theseoptionscanbehelpfulforindividualswithvisualimpairment. Table2.6TheViewMenu Name Shortcut Key Description Show Menubar None Toggleson/offthemenubardisplay FullScreen F11 ZoomIn Ctrl++ Ctrl+Ctrl+0 ZoomOut NormalSize Toggleson/offtheterminalwindowfillingtheentire desktop Enlargesthefontsizeinthewindowincrementally Reducesthefontsizeinthewindowincrementally Returnsthefontsizetodefault Beawarethatifyoutoggleoffthemenubardisplay,thesession’smenubardisappears. However, you can easily get the menu bar to display again by right-clicking in any terminalsessionwindowandtogglingontheShowMenubaroption. The Search menu, shown in Table 2.7, contains items for conducting simple searches withintheterminalsession.Thesesearchesaresimilartoonesyoumayhaveconductedin anetworkbrowserorwordprocessor. Table2.7TheSearchMenu Name ShortcutKey Description Find Shift+Ctrl+F OpensFindwindowtoprovidedesignatedtextsearchoptions Searchesforwardfromcurrentterminalsessionlocationfor FindNext Shift+Ctrl+H designatedtext Find Searchesbackwardfromcurrentterminalsessionlocationfor Shift+Ctrl+G Previous designatedtext The Terminal menu, shown in Table 2.8, contains options for controlling the terminal emulationsessionfeatures.Therearenoshortcutkeystoaccesstheseitems. Table2.8TheTerminalMenu Name SetTitle Description Switchestoanewprofileconfiguration Modifiessessiontabtitlebarsetting SetCharacter Encoding Selectscharactersetusedtosendanddisplaycharacters Reset Sendsresetterminalsessioncontrolcode Sendsresetterminalsessioncontrolcodeandclearsterminal sessionscreen Listswindowsizesforadjustingthecurrentterminalwindowsize ChangeProfile ResetandClear WindowSizeList The Resetoptionisextremelyuseful.Oneday,youmayaccidentlycauseyourterminal session to display random characters and symbols. When this occurs, the text is unreadable. It is typically caused by displaying a non-text file to the screen. You can quicklygettheterminalsessionbacktonormalbyselectingResetorResetandClear. TheTabsmenu,showninTable2.9,providesitemsforcontrollingthelocationofthetabs andselectingwhichtabisactive.Thismenudisplaysonlywhenyouhavemorethanone tabsessionopen. Table2.9TheTabsMenu Name ShortcutKey Ctrl+Page NextTab Down Description Makesthenexttabinthelistactive Previous Tab Ctrl+PageUp Makestheprevioustabinthelistactive MoveTab Left Shift+Ctrl+Page Up Shufflesthecurrenttabinfrontoftheprevioustab MoveTab Right Shift+Ctrl+Page Down Detach Tab None TabList None Terminal List None Shufflesthecurrenttabinfrontofthenexttab RemovesthetabandstartsanewGNOMETerminalwindow usingthistabsession Liststhecurrentlyrunningtabs(Selectatabtojumptothat session.) Liststhecurrentlyrunningterminals(Selectaterminalto jumptothatsession.Thisisdisplayedonlyifmultiple windowsessionsareopen.) Finally, the Help menu contains two menu options. Contents provides a full GNOME Terminal manual so you can research individual GNOME Terminal items and features. TheAboutoptionshowsyouthecurrentGNOMETerminalversionthat’srunning. Besides the GNOME terminal emulator package, another commonly used package is Konsole Terminal. In many ways, Konsole Terminal is similar to GNOME Terminal. However,enoughdifferencesexisttowarrantitsownsection. UsingtheKonsoleTerminalEmulator The KDE Desktop Project created its own terminal emulation package called Konsole Terminal.TheKonsolepackageincorporatesbasicterminalemulationfeatures,alongwith more advanced ones expected from a graphical application. This section describes KonsoleTerminalfeaturesandshowsyouhowtousethem. AccessingtheKonsoleTerminal TheKonsoleTerminalisthedefaultterminalemulatorfortheKDEdesktopenvironment. You can easily access it via the KDE environment’s menu system. In other desktop environments,accessingtheKonsoleTerminalcanbealittlemoredifficult. In the KDE desktop environment, you can access the Konsole Terminal by clicking the iconlabeledKickoffApplicationLauncherinthelower-leftcornerofthescreen.Then clickApplications⇨System⇨Terminal(Konsole). Note YoumayseetwoterminalmenuoptionswithintheKDEmenuenvironment.Ifyou do,theTerminalmenuoptionwiththewordsKonsolebeneathitistheKonsole terminal. In the GNOME desktop environment, the Konsole terminal is typically not installed by default.IfKonsoleTerminalhasbeeninstalled,youcanaccessitviatheGNOMEmenu system. In the upper-left corner of the window, click Applications⇨System Tools⇨Konsole. Note YoumaynothavetheKonsoleterminalemulationpackageinstalledonyoursystem. Ifyouwouldliketoinstallit,readthroughChapter9tolearnhowtoinstallsoftware viathecommandline. In the Unity desktop environment, if Konsole has been installed, you can access it via Dash⇨SearchandtypeKonsole.TheKonsoleTerminalshowsupintheDashhomearea asanapplicationnamedKonsole.ClickthaticontoopentheKonsoleterminalemulator. Figure2.4showstheKonsoleTerminal.ItwasaccessedonaKDEdesktopenvironment inaCentOSLinuxdistribution. Figure2.4TheKonsoleTerminal Remember that, in most desktop environments, you can create a launcher to access applications such as the Konsole Terminal. The command you need to type for the launcher to start up the Konsole terminal emulator is konsole. Also, if the Konsole Terminal is installed, you can start it from another terminal emulator by typing konsole andpressingEnter. The Konsole Terminal, similar to GNOME Terminal, has several configuration options provided by menus and shortcut keys. The following section describes these various options. TheMenuBar TheKonsoleTerminalmenubarcontainstheconfigurationandcustomizationoptionsyou needtoeasilyviewandchangefeaturesinyourterminalemulationsession.Thefollowing tablesbrieflydescribethemenuoptionsandassociatedshortcutkeys. Tip TheKonsoleTerminalprovidesasimplemenuwhenyouright-clickintheactive sessionarea.Severalmenuitemsareavailableinthiseasy-to-accessmenu. TheFilemenu,showninTable2.10,providesoptionsforstartinganewtabinthecurrent windoworinanewwindow. Table2.10TheFileMenu Name ShortcutKey NewTab Ctrl+Shift+N NewWindow Ctrl+Shift+M Shell None Description Startsanewshellsessioninanewtabintheexisting KonsoleTerminalwindow StartsanewshellsessioninanewKonsoleTerminal window Opensthedefaultprofile,Shell OpenBrowser Here None Opensthedefaultfilebrowserapplication CloseTab Ctrl+Shift+W Ctrl+Shift+Q Closesthecurrenttabsession QuitstheKonsoleTerminalemulationapplication Quit WhenyoufirststarttheKonsoleTerminal,theonlyprofilelistedinthemenuisShell.As moreprofilesarecreatedandsaved,theirnamesappearinthemenulist. Note AsyoureadthroughtheseKonsoleTerminalmenuoptions,keepinmindthatyour Linuxdistribution’sKonsoleTerminalmayhaveverydifferentmenuoptions available.ThisisbecausesomeLinuxdistributionshavekeptolderversionsofthe KonsoleTerminalemulationpackage. The Edit menu, shown in Table2.11, provides options for handling text in the session. Also,managingtabnamesisinthisoptionslist. Table2.11TheEditMenu Name ShortcutKey Copy Ctrl+Shift+C Paste Ctrl+Shift+V RenameTab Ctrl+Alt+S Description CopiesselectedtexttotheKonsoleclipboard PastestextfromtheKonsoleclipboardintoasession Modifiessessiontabtitlebarsetting CopyInput To None Starts/stopssessioninputcopiestochosenadditionalsessions Clear Display None Clearstheterminalsessionscreen Clear& Reset None Clearstheterminalsessionscreenandsendstheresetterminal sessioncontrolcode Konsoleprovidesanexcellentmethodfortrackingwhatfunctionistakingplaceineach tabsession.UsingtheRenameTabmenuoption,youcannameatabtomatchitscurrent task.Thishelpsintrackingwhichopentabsessionisperformingwhatfunction. The View menu, shown in Table 2.12, contains items for controlling individual session views in the Konsole Terminal window. In addition, options are available that aid in monitoringterminalsessionactivity. Table2.12TheViewMenu Name ShortcutKey SplitView None DetachView Ctrl+Shift+H ShowMenuBar None FullScreen Mode Ctrl+Shift+F11 Monitorfor Silence Ctrl+Shift+I Description Controlsthemultipletabsessiondisplaywithinthe currentKonsoleTerminalwindow RemovesatabsessionandstartsanewKonsole Terminalwindowusingthistabsession Toggleson/offMenubardisplay Toggleson/offtheterminalwindowfillingtheentire monitordisplayarea Toggleson/offaspecialmessagefortabsilence Monitorfor Activity Ctrl+Shift+A Toggleson/offaspecialmessagefortabactivity Character Encoding None Selectsthecharactersetusedtosendanddisplay characters IncreaseText Size Ctrl++ Enlargesthefontsizeinthewindowincrementally DecreaseText Size Ctrl+- Reducesthefontsizeinthewindowincrementally The Monitor for Silence menu option is used for indicating tab silence. Tab silence occurs when no new text appears in the current tab session for 10 seconds. This allows youtoswitchtoanothertabwhilewaitingforapplicationoutputtostop. Tabactivity,toggledbytheMonitorforActivityoption,issuesaspecialmessagewhen new text appears in the tab session. This option allows you to be notified when output fromanapplicationoccurs. Konsole retains a history, formally called a scrollbackbuffer, for each tab. The history containsoutputtextthathasscrolledoutoftheterminalviewingarea.Bydefault,thelast 1,000linesinthescrollbackbufferareretained.The Scrollbackmenu,showninTable 2.13,containsoptionsforviewingthisbuffer. Table2.13TheScrollbackMenu Name ShortcutKey Description OpenstheFindwindowatthebottomoftheKonsole SearchOutput Ctrl+Shift+F Terminalwindowtoprovidescrollbacktextsearch options Findsthenexttextmatchinmorerecentscrollbackbuffer FindNext F3 history Findsthenexttextmatchinolderscrollbackbuffer FindPrevious Shift+F3 history SaveOutput None SavesscrollbackbuffercontentstoatextorHTMLfile Scrollback OpenstheScrollbackOptionswindowtoconfigure None Options scrollbackbufferoptions Clear Scrollback None Removesscrollbackbuffercontents Clear Scrollback& Reset Ctrl+Shift+X Removesscrollbackbuffercontentsandresetsthe terminalwindow You can scroll back through the scrollback buffer by simply using the scrollbar in the viewing area. Also, you can scroll back line by line by pressing the Shift+Up Arrow or scrollbackapage(24lines)atatimebypressingShift+PageUp. The Bookmarksmenuoptions,showninTable2.14,provideawaytomanagebookmarks set in the Konsole Terminal window. A bookmark enables you to save your active session’sdirectorylocationandtheneasilyreturnthereineitherthesamesessionoranew session. Table2.14TheBookmarksMenu Name Shortcut Key AddBookmark Ctrl+Shift+B BookmarkTabsas Folder None NewBookmarkFolder None None EditBookmarks Description Createsanewbookmarkatthecurrentdirectory location Createsanewbookmarkforallcurrentterminal tabsessions Createsanewbookmarkstoragefolder Editsexistingbookmarks The Settings menu, shown in Table 2.15, allows you to customize and manage your profiles.Also,youcanaddalittlemorefunctionalitytoyourcurrenttabsession.Thereare noshortcutkeystoaccesstheseitems. Table2.15TheSettingsMenu Name Description ChangeProfile Appliestothecurrenttabaselectedprofile OpenstheEditProfilewindowtoprovideprofileconfiguration EditCurrentProfile options OpenstheManageProfilewindowtoprovideprofile ManageProfiles managementoptions ConfigureShortcuts CreatesKonsoleTerminalcommandkeyboardshortcuts Configure Notifications CreatescustomKonsoleTerminalschemasandsessions ConfigureNotificationsallowsyoutoassociatespecificeventsthatcanoccurwithina session with different actions. When one of the events occurs, the defined action (or actions)istaken. The Help menu, shown in Table 2.16, provides the full Konsole handbook (if KDE handbooks were installed in your Linux distribution) and the standard About Konsole dialogbox. Table2.16TheHelpMenu Name KonsoleHandbook What’sThis? ReportBug Shortcut Key None Shift+F1 None Description ContainsthefullKonsoleHandbook Containshelpmessagesforterminalwidgets OpenstheSubmitBugReportform SwitchApplication Language None OpenstheSwitchApplication’sLanguageform AboutKonsole None DisplaysthecurrentKonsoleTerminalversion DisplaysthecurrentKDEdesktopenvironment version AboutKDE Rather extensive documentation is provided to help you use the Konsole terminal emulatorpackage.Inadditiontohelpitems,youareprovidedwithaBugReportformto submittotheKonsoleTerminaldeveloperswhenyouencounteraprogrambug. TheKonsoleterminalemulatorpackageisyoungcomparedtoanotherpopularpackage, xterm.Inthenextsection,weexplorethe“old-timer”xterm. UsingthextermTerminalEmulator The oldest and most basic of terminal emulation packages is xterm. The xterm package hasbeenaroundsincebeforetheoriginaldaysofXWindow,apopulardisplayserver,and it’softenincludedbydefaultindistributions. Although xterm is a full terminal emulation package, it doesn’t require many resources (suchasmemory)tooperate.Becauseofthis,thextermpackageisstillpopularinLinux distributionsdesignedtorunonolderhardware.Somegraphicaldesktopenvironmentsuse itasthedefaultterminalemulationpackage. Although it doesn’t offer many fancy features, the xterm package does one thing extremely well: It emulates older terminals, such as the Digital Equipment Corporation (DEC) VT102, VT220, and Tektronix 4014 terminals. For the VT102 and VT220 terminals,xtermcanevenemulatetheVTseriesofcolorcontrolcodes,allowingyouto usecolorinyourscripts. Note TheDECVT102andVT220weredumbtextterminalspopularforconnectingto Unixsystemsinthe1980sandearly1990s.AVT102/VT220coulddisplaytextand displayrudimentarygraphicsusingblockmodegraphics.Thisstyleofterminal accessisstillusedinmanybusinessenvironmentstoday,thuskeepingVT102/VT220 emulationpopular. Figure2.5 shows what the basic xterm display looks like running on a graphical Linux desktop.Youcanseeitisverybasic. Figure2.5ThextermTerminal Thextermterminalemulatorcanbetrickytofindthesedays.Often,itisnotincludedina desktopenvironmentgraphicalmenuarrangement. Accessingxterm In Ubuntu’s Unity desktop, xterm is installed by default. You can access it via Dash⇨Searchandtypexterm.xtermshowsupintheDashhomeareaasanapplication namedXTerm.Clickthaticontoopenthextermterminalemulator. Note YoumayseeanotherterminalcalledUXTermwhenyousearchforxtermonUbuntu. ThisissimplythextermemulatorpackagewithUnicodesupport. In the GNOME and KDE desktop environment, xterm is not installed by default. You must install it first (see Chapter 9 for help on installing software packages). After it’s installed,youmuststartxtermfromanotherterminalemulator.Openaterminalemulator forCLIaccess,typexterm,andpressEnter.Also,rememberthatyoucancreateyourown desktoplaunchertostartupxterm. Thextermpackageallowsyoutosetindividualfeaturesusingcommandlineparameters. Thefollowingsectionsdiscussthesefeaturesandhowtochangethem. CommandLineParameters Thelistofxtermcommandlineparametersisextensive.Youcancontrollotsoffeaturesto customize the terminal emulation features, such as enabling or disabling individual VT emulations. Note xtermhasahugenumberofconfigurationoptions—somanythattheycannotallbe coveredhere.Extensivedocumentationisavailableviathebashmanual.Accessing thebashmanualiscoveredinChapter3.Inaddition,thextermdevelopmentteam providessomeexcellenthelponitswebsite:http://invisibleisland.net/xterm/. You can invoke certain configuration options by adding a parameter to the xterm command. For example, to have the xterm emulate a DEC VT100 terminal, type the commandxterm -ti vt100 and press Enter. Table2.17 shows some parameters you can includewheninvokingthextermterminalemulatorsoftware. Table2.17xtermCommandLineParameters Parameter Description -bgcolor Specifiesthecolortousefortheterminalbackground -fbfont Specifiesthefonttouseforboldtext -fgcolor Specifiesthecolortousefortheforegroundtext -fnfont Specifiesthefonttousefortext -fwfont Specifiesthefonttouseforwidetext -lffilename Specifiesthefilenametouseforscreenlogging -mscolor Specifiesthecolorusedforthetextcursor -namename Specifiesthenameoftheapplicationthatappearsinthetitlebar -titerminal Specifiestheterminaltypetoemulate Somextermcommandlineparametersuseaplussign(+)orminussign(-)tosignifyhow a feature is set. A plus sign may turn a feature on, while a minus sign turns it off. However, the opposite can be true as well. A plus sign may disable a feature, while a minussignenablesit,suchaswhenusingthe bcparameter.Table2.18listssomeofthe morecommonfeaturesyoucansetusingthe+/-commandlineparameters. Table2.18xterm+/-CommandLineParameters Parameter Description ah Enables/disableshighlightedtextcursor aw Enables/disablesauto-line-wrap bc Enables/disablestextcursorblinking cm Enables/disablesrecognitionofANSIcolorchangecontrolcodes fullscreen Enables/disablesfullscreenmode j Enables/disablesjumpscrolling l mb rv t Enables/disablesloggingscreendatatoalogfile Enables/disablesmarginbell Enables/disablesreversevideocolors Enables/disablesTektronixmode It is important to note that not all implementations of xterm support all these command lineparameters.Youcandeterminewhichparametersyourxtermimplementsbyusingthe -helpparameterwhenyoustartxtermonyoursystem. Nowthatyouhavebeenintroducedtothreeterminalemulatorpackages,thebigquestion iswhichisthebestterminalemulatortouse?Thereisnodefiniteanswertothatquestion. Which terminal emulator package you use depends upon your individual needs and desires.Butitisgreattohavesomanychoices. Summary TostartlearningLinuxcommandlinecommands,youneedaccesstoaCLI.Intheworld of graphical interfaces, this can sometimes be challenging. This chapter discussed differentinterfacesyoushouldconsidertogettotheLinuxcommandline. First,thischapterdiscussedthedifferencebetweenaccessingtheCLIviaavirtualconsole terminal (a terminal outside the GUI) and a graphical terminal emulation package (a terminalinsidetheGUI).Wetookabrieflookatthebasicdifferencesbetweenthesetwo accessmethods. Next, we explored in detail accessing the CLI via a virtual console terminal, including specifics on how to change console terminal configuration options such as background color. Afterlookingatvirtualconsoleterminals,thechaptertraveledthroughaccessingtheCLI viaagraphicalterminalemulator.Primarily,wecoveredthreedifferenttypesofterminal emulators:GNOMETerminal,KonsoleTerminal,andxterm. This chapter also covered the GNOME desktop project’s GNOME terminal emulation package. GNOME Terminal is typically installed by default on the GNOME desktop environment.Itprovidesconvenientwaystosetmanyterminalfeaturesviamenuoptions andshortcutkeys. We also covered the KDE desktop project’s Konsole terminal emulation package. The Konsole Terminal is typically installed by default on the KDE desktop environment. It providesseveralnicefeatures,suchastheabilitytomonitoraterminalforsilence. Finally, we covered the xterm terminal emulator package. xterm was the first terminal emulatoravailableforLinux.ItcanemulateolderterminalhardwaresuchastheVTand Tektronixterminals. Inthenextchapter,youstartlookingattheLinuxcommandlinecommands.Itwalksyou throughthecommandsnecessarytonavigatearoundtheLinuxfilesystem,andtocreate, delete,andmanipulatefiles. Chapter3 BasicbashShellCommands InThisChapter 1. Interactingwiththeshell 2. Usingthebashmanual 3. Traversingthefilesystem 4. Listingfilesanddirectories 5. Managingfilesanddirectories 6. Viewingfilecontents ThedefaultshellusedinmanyLinuxdistributionsistheGNUbashshell.This chapterdescribesthebasicfeaturesavailableinthebashshell,suchasthebash manual,tabauto-completionandhowtodisplayafile’scontents.Youwillwalk throughhowtoworkwithLinuxfilesanddirectoriesusingthebasiccommands providedbythebashshell.Ifyou’realreadycomfortablewiththebasicsintheLinux environment,feelfreetoskipthischapterandcontinuewithChapter4toseemore advancedcommands. StartingtheShell TheGNUbashshellisaprogramthatprovidesinteractiveaccesstotheLinuxsystem.It runsasaregularprogramandisnormallystartedwheneverauserlogsintoaterminal. TheshellthatthesystemstartsdependsonyouruserIDconfiguration. The/etc/passwdfilecontainsalistofallthesystemuseraccounts,alongwithsomebasic configurationinformationabouteachuser.Here’sasampleentryfroma/etc/passwdfile: christine:x:501:501:ChristineBresnahan:/home/christine:/bin/bash Eachentryhassevendatafields,withfieldsseparatedbycolons.Thesystemusesthedata inthesefieldstoassignspecificfeaturesfortheuser.Mostoftheseentriesarediscussedin moredetailinChapter7.Fornow,justpayattentiontothelastfield,whichspecifiesthe user’sshellprogram. Note ThoughthefocusisontheGNUbashshell,additionalshellsarereviewedinthis book.Chapter23coversworkingwithalternativeshells,suchasdashandtcsh. In the earlier /etc/passwd sample entry, the user christine has /bin/bash set as her defaultshellprogram.Thismeanswhen christinelogsintotheLinuxsystem,thebash shellprogramisautomaticallystarted. Although the bash shell program is automatically started at login, whether a shell command line interface (CLI) is presented depends on which login method is used. If a virtualconsoleterminalisusedtologin,theCLIpromptisautomaticallypresented,and youcanbegintotypeshellcommands.However,ifyoulogintotheLinuxsystemviaa graphicaldesktopenvironment,youneedtostartagraphicalterminalemulatortoaccess theshellCLIprompt. UsingtheShellPrompt AfteryoustartaterminalemulationpackageorlogintoaLinuxvirtualconsole,youget accesstotheshellCLIprompt.Thepromptisyourgatewaytotheshell.Thisistheplace whereyouentershellcommands. Thedefaultpromptsymbolforthebashshellisthedollarsign($).Thissymbolindicates that the shell is waiting for you to enter text. Different Linux distributions use different formatsfortheprompt.OnthisUbuntuLinuxsystem,theshellpromptlookslikethis: christine@server01:∼$ OntheCentOSLinuxsystem,itlookslikethis: [christine@server01∼]$ Besidesactingasyouraccesspointtotheshell,thepromptcanprovideadditionalhelpful information. In the two preceding examples, the current user ID name, christine, is shownintheprompt.Also,thenameofthesystemisshown,server01.Youlearnlaterin thischapteraboutadditionalitemsshownintheprompt. Tip IfyouarenewtotheCLI,keepinmindthat,afteryoutypeinashellcommandatthe prompt,youneedtopresstheEnterkeyfortheshelltoactuponyourcommand. The shell prompt is not static. It can be changed to suit your needs. Chapter 6, “Using LinuxEnvironmentVariables,”coversmodifyingyourshellCLIpromptconfiguration. ThinkoftheshellCLIpromptasahelpmate,assistingyouwithyourLinuxsystem,giving you helpful insights, and letting you know when the shell is ready for new commands. AnotherhelpfulitemintheshellisthebashManual. InteractingwiththebashManual Most Linux distributions include an online manual for looking up information on shell commands,aswellaslotsofotherGNUutilitiesincludedinthedistribution.Youshould become familiar with the manual, because it’s invaluable for working with commands, especiallywhenyou’retryingtofigureoutvariouscommandlineparameters. The man command provides access to the manual pages stored on the Linux system. Enteringthe mancommandfollowedbyaspecificcommandnameprovidesthatutility’s manualentry.Figure3.1showsanexampleoflookingupthe xtermcommand’smanual pages.Thispagewasreachedbytypingthecommandmanxterm. Figure3.1Manualpagesforthextermcommand NoticethextermcommandDESCRIPTIONparagraphsinFigure3.1.Theyarerathersparse and full of technical jargon. The bash manual is not a step-by-step guide, but instead a quickreference. Tip Ifyouarenewtothebashshell,youmayfindthatthemanpagesarenotveryhelpful atfirst.However,getintothehabitofusingthem,especiallytoreadthefirst paragraphortwoofacommand’sDESCRIPTIONsection.Eventually,youwilllearn thetechnicallingo,andthemanpageswillbecomemorehelpfultoyou. Whenyouusethe mancommandtoviewacommand’smanualpages,theyaredisplayed with something called a pager. A pager is a utility that allows you to page through displayedtext.Thus,youcanpagethroughthemanpagesbypressingthespacebar,oryou cangolinebylineusingtheEnterkey.Inaddition,youcanusethearrowkeystoscroll forwardandbackwardthroughthemanpagetext(assumingthatyourterminalemulation packagesupportsthearrowkeyfunctions). Whenyouarefinishedwiththemanpages,presstheqkeytoquit.Whenyouquittheman pages, you receive a shell CLI prompt, indicating the shell is waiting for your next command. Tip Thebashmanualevenhasreferenceinformationonitself.Typemanmantosee manualpagesconcerningthemanpages The manual page divides information about a command into separate sections. Each sectionhasaconventionalnamingstandardasshowninTable3.1. Table3.1TheLinuxmanPageConventionalSectionNames Section Description Name Displayscommandnameandashortdescription Synopsis Showscommandsyntax Configuration Providesconfigurationinformation Description Describescommandgenerally Options Describescommandoption(s) ExitStatus Definescommandexitstatusindicator(s) ReturnValue Describescommandreturnvalue(s) Errors Providescommanderrormessages Environment Describesenvironmentvariable(s)used Files Definesfilesusedbycommand Versions Describescommandversioninformation ConformingTo Providesstandardsfollowed Notes Describesadditionalhelpfulcommandmaterial Bugs Providesthelocationtoreportfoundbugs Example Showscommanduseexamples Authors Providesinformationoncommanddevelopers Copyright Definescommandcodecopyrightstatus SeeAlso Referssimilaravailablecommands Not every command’s man page has all the section names described in Table3.1.Also, somecommandshavesectionnamesthatarenotlistedintheconventionalstandard. Tip Whatifyoucan’trememberthecommandname?Youcansearchthemanpages usingkeywords.Thesyntaxisman-kkeyword.Forexample,tofindcommands dealingwiththeterminals,youtypeman-kterminal. In addition to the conventionally named sections for a man page, there are man page sectionareas.Eachsectionareahasanassignednumber,startingat1andgoingto9;they arelistedinTable3.2. Table3.2TheLinuxmanPageSectionAreas SectionNumber AreaContents 1 Executableprogramsorshellcommands 2 Systemcalls 3 Librarycalls 4 Specialfiles 5 Fileformatsandconventions 6 Games 7 Overviews,conventions,andmiscellaneous 8 Superuserandsystemadministrationcommands 9 Kernelroutines Typically, the man utility provides the lowest numbered content area for the command. For example, looking back to Figure3.1 where the command man xterm was entered, noticethatintheupper-leftandupper-rightdisplaycorners,thewordXTERMisfollowedby a number in parentheses, (1). This means the man pages displayed are coming from contentarea1(executableprogramsorshellcommands). Occasionally,acommandhasmanpagesinmultiplesectioncontentareas.Forexample, thereisacommandcalledhostname.Themanpagescontaininformationonthecommand aswellasanoverviewsectiononsystemhostnames.Toseethepagesdesired,youtype mansection#topic.Forthecommand’smanpagesinsection1,typeman1hostname. Fortheoverviewmanpagesinsection7,typeman7hostname. Youcanalsostepthroughanintroductiontothevarioussectioncontentareasbytyping man1introtoreadaboutsection1,man2introtoreadaboutsection2,man3introto readaboutsection3,andsoon. Themanpagesarenottheonlyreference.Therearealsotheinformationpagescalledinfo pages.Youcanlearnabouttheinfopagesbytypinginfoinfo. Inaddition,mostcommandsacceptthe-helpor—helpoption.Forexample,youcantype hostname-helptoseeahelpscreen.Formoreinformationonusinghelp,typehelphelp. (Seeapatternhere?) Obviously, several helpful resources are available for reference. However, many basic shell concepts still need detailed explanation. In the next section, we cover navigating throughtheLinuxfilesystem. NavigatingtheFilesystem When you log into the system and reach the shell command prompt, you are usually placedinyourhomedirectory.Often,youwanttoexploreotherareasintheLinuxsystem besides just your home directory. This section describes how to do that using shell commands.Tostart,youneedtotakeatourofjustwhattheLinuxfilesystemlookslikeso youknowwhereyouaregoing. LookingattheLinuxfilesystem If you’re new to the Linux system, you may be confused by how it references files and directories,especiallyifyou’reusedtothewaytheMicrosoftWindowsoperatingsystem doesthat.BeforeexploringtheLinuxsystem,ithelpstohaveanunderstandingofhowit’s laidout. Thefirstdifferenceyou’llnoticeisthatLinuxdoesnotusedrivelettersinpathnames.In theWindowsworld,thephysicaldrivesinstalledonthecomputerdeterminethepathname ofthefile.Windowsassignsalettertoeachphysicaldiskdrive,andeachdrivecontainsits owndirectorystructureforaccessingfilesstoredonit. Forexample,inWindowsyoumaybeusedtoseeingthefilepathssuchas: c:\Users\Rich\Documents\test.doc The Windows file path tells you exactly which physical disk partition contains the file namedtest.doc.Forexample,ifyousavedtest.doconaflashdrive,designatedbytheJ drive,thefilepathwouldbeJ:∖test.doc.Thispathindicatesthatthefileislocatedatthe rootofthedriveassignedtheletterJ. ThisisnotthemethodusedbyLinux.Linuxstoresfileswithinasingledirectorystructure, called a virtual directory. The virtual directory contains file paths from all the storage devicesinstalledonthecomputer,mergedintoasingledirectorystructure. The Linux virtual directory structure contains a single base directory, called the root. Directoriesandfilesbeneaththerootdirectoryarelistedbasedonthedirectorypathused togettothem,similartothewayWindowsdoesit. Tip You’llnoticethatLinuxusesaforwardslash(/)insteadofabackwardslash(∖)to denotedirectoriesinfilepaths.ThebackslashcharacterinLinuxdenotesanescape characterandcausesallsortsofproblemswhenyouuseitinafilepath.Thismay takesomegettingusedtoifyou’recomingfromaWindowsenvironment. InLinux,youwillseefilepathssimilartothefollowing: /home/Rich/Documents/test.doc Thisindicatesthefile test.docisinthedirectory Documents,underthedirectory rich, which is contained in the directory home. Notice that the path doesn’t provide any informationastowhichphysicaldiskthefileisstoredon. The tricky part about the Linux virtual directory is how it incorporates each storage device.ThefirstharddriveinstalledinaLinuxsystemiscalledtherootdrive.Theroot drivecontainsthevirtualdirectorycore.Everythingelsebuildsfromthere. On the root drive, Linux can use special directories as mountpoints. Mount points are directoriesinthevirtualdirectorywhereyoucanassignadditionalstoragedevices.Linux causes files and directories to appear within these mount point directories, even though theyarephysicallystoredonadifferentdrive. Oftensystemfilesarephysicallystoredontherootdrive.Userfilesaretypicallystoredon aseparatedriveordrives,asshowninFigure3.2. Figure3.2TheLinuxfilestructure Figure3.2showstwoharddrivesonthecomputer.Oneharddriveisassociatedwiththe rootofthevirtualdirectory(indicatedbyasingleforwardslash).Otherharddrivescanbe mountedanywhereinthevirtualdirectorystructure.Inthisexample,thesecondharddrive ismountedatthelocation/home,whichiswheretheuserdirectoriesarelocated. TheLinuxfilesystemstructureoriginallyevolvedfromtheUnixfilestructure.InaLinux filesystem,commondirectorynamesareusedforcommonfunctions.Table3.3listssome ofthemorecommonLinuxvirtualtop-leveldirectorynamesandtheircontents. Table3.3CommonLinuxDirectoryNames Directory Usage / rootofthevirtualdirectory,wherenormally,nofilesareplaced /bin binarydirectory,wheremanyGNUuser-levelutilitiesarestored /boot bootdirectory,wherebootfilesarestored /dev devicedirectory,whereLinuxcreatesdevicenodes /etc systemconfigurationfilesdirectory /home homedirectory,whereLinuxcreatesuserdirectories /lib librarydirectory,wheresystemandapplicationlibraryfilesarestored /media mediadirectory,acommonplaceformountpointsusedforremovablemedia mountdirectory,anothercommonplaceformountpointsusedforremovable /mnt media optionaldirectory,oftenusedtostorethird-partysoftwarepackagesanddata /opt files /proc processdirectory,wherecurrenthardwareandprocessinformationisstored /root roothomedirectory /sbin systembinarydirectory,wheremanyGNUadmin-levelutilitiesarestored /run rundirectory,whereruntimedataisheldduringsystemoperation /srv servicedirectory,wherelocalservicesstoretheirfiles /sys systemdirectory,wheresystemhardwareinformationfilesarestored /tmp temporarydirectory,wheretemporaryworkfilescanbecreatedanddestroyed userbinarydirectory,wherethebulkofGNUuser-levelutilitiesanddatafiles /usr arestored /var variabledirectory,forfilesthatchangefrequently,suchaslogfiles The common Linux directory names are based upon the Filesystem Hierarchy Standard (FHS). Many Linux distributions maintain compliance with FHS. Therefore, you should beabletoeasilyfindfilesonanyFHS-compliantLinuxsystems. Note TheFHSisoccasionallyupdated.YoumayfindthatsomeLinuxdistributionsare stillusinganolderFHSstandard,whileotherdistributionsonlypartiallyimplement thecurrentstandard.TokeepuptodateontheFHSstandard,visititsofficialhomeat http://www.pathname.com/fhs/. WhenyoulogintoyoursystemandreachashellCLIprompt,yoursessionstartsinyour homedirectory.Yourhomedirectoryisauniquedirectoryassignedtoyouruseraccount. When a user account is created, the system normally assigns a unique directory for the account(seeChapter7). Youcanmovearoundthevirtualdirectoryusingagraphicalinterface.However,tomove aroundthevirtualdirectoryfromaCLIprompt,youneedtolearntousethecdcommand. Traversingdirectories You use the change directory command (cd) to move your shell session to another directory in the Linux filesystem. The cd command syntax is pretty simplistic: cd destination. Thecdcommandmaytakeasingleparameter,destination,whichspecifiesthedirectory nameyouwanttogoto.Ifyoudon’tspecifyadestinationonthe cdcommand,ittakes youtoyourhomedirectory. Thedestinationparametercanbeexpressedusingtwodifferentmethods.Onemethodis using an absolute directory reference. The other method uses a relative directory reference. Thefollowingsectionsdescribeeachofthesemethods.Thedifferencesbetweenthesetwo methodsareimportanttounderstandasyoutraversethefilesystem. Usingabsolutedirectoryreferences Youcanreferenceadirectorynamewithinthevirtualdirectorysystemusinganabsolute directoryreference.Theabsolutedirectoryreferencedefinesexactlywherethedirectory is in the virtual directory structure, starting at the root. Think of the absolute directory referenceasthefullnameforadirectory. An absolute directory reference always begins with a forward slash (/), indicating the virtualdirectorysystem’sroot.Thus,toreferenceuserbinaries,containedwithinthe bin directorystoredwithintheusrdirectory,youwoulduseanabsolutedirectoryreferenceas follows: /usr/bin Withtheabsolutedirectoryreference,there’snodoubtastoexactlywhereyouwanttogo. Tomovetoaspecificlocationinthefilesystemusingtheabsolutedirectoryreference,you justspecifythefullpathnameinthecdcommand: christine@server01:∼$cd/usr/bin christine@server01:/usr/bin$ Noticeintheprecedingexamplethatthepromptoriginallyhadatilde(∼)init.Afterthe changetoanewdirectoryoccurred,thetildewasreplacedby /usr/bin.Thisiswherea CLI prompt can help you keep track of where you are in the virtual directory structure. The tilde indicates that your shell session is located in your home directory. After you moveoutofyourhomedirectory,theabsolutedirectoryreferenceisshownintheprompt, iftheprompthasbeenconfiguredtodoso. Note IfyourshellCLIpromptdoesnotshowyourshellsession’scurrentlocation,thenit hasnotbeenconfiguredtodoso.Chapter6showsyouhowtomakeconfiguration changes,ifyoudesiremodificationstoyourCLIprompt. If your prompt has not been configured to show the shell session’s current absolute directory location, then you can display the location via a shell command. The pwd command displays the shell session’s current directory location, which is called the presentworkingdirectory.Anexampleofusingthepwdcommandisshownhere. christine@server01:/usr/bin$pwd /usr/bin christine@server01:/usr/bin$ Tip Itisagoodhabittousethepwdcommandwheneveryouchangetoanewpresent workingdirectory.Becausemanyshellcommandsoperateonthepresentworking directory,youalwayswanttomakesureyouareinthecorrectdirectorybefore issuingacommand. You can move to any level within the entire Linux virtual directory structure from any levelusingtheabsolutedirectoryreference: christine@server01:/usr/bin$cd/var/log christine@server01:/var/log$ christine@server01:/var/log$pwd /var/log christine@server01:/var/log$ YoucanalsoquicklyjumptoyourhomedirectoryfromanylevelwithintheLinuxvirtual directorystructure: christine@server01:/var/log$cd christine@server01:~$ christine@server01:∼$pwd /home/christine christine@server01:∼$ However, if you’re just working within your own home directory structure, often using absolute directory references can get tedious. For example, if you’re already in the directory /home/christine, it seems somewhat cumbersome to have to type the command: cd/home/christine/Documents justtogettoyourDocumentsdirectory.Fortunately,there’sasimplersolution. Usingrelativedirectoryreferences Relative directory references allow you to specify a destination directory reference relativetoyourcurrentlocation.Arelativedirectoryreferencedoesn’tstartwithaforward slash(/). Instead, a relative directory reference starts with either a directory name (if you’re traversingtoadirectoryunderyourcurrentdirectory)oraspecialcharacter.Forexample, ifyouareinyour homedirectoryandwanttomovetoyour Documentssubdirectory,you canusethecdcommandalongwitharelativedirectoryreference: christine@server01:∼$pwd /home/christine christine@server01:~$ christine@server01:∼$cdDocuments christine@server01:∼/Documents$pwd /home/christine/Documents christine@server01:~/Documents$ In the preceding example, note that no forward slash (/) was used. Instead a relative directory reference was used and the present work directory was changed from /home/christineto/home/christine/Documents,withmuchlesstyping. Alsonoticeintheexamplethatifthepromptisconfiguredtodisplaythepresentworking directory,itkeepsthetildeinthedisplay.Thisshowsthatthepresentworkingdirectoryis inadirectoryundertheuser’shomedirectory. Tip IfyouarenewtothecommandlineandtheLinuxdirectorystructure,itis recommendedthatyoustickwithabsolutedirectoryreferencesforawhile.Afteryou becomemorefamiliarwiththedirectorylayout,switchtousingrelativedirectory references. You can use a relative directory reference with the cd command in any directory containing subdirectories. You can also use a special character to indicate a relative directorylocation. Thetwospecialcharactersusedforrelativedirectoryreferencesare: Thesingledot(.)torepresentthecurrentdirectory Thedoubledot(..)torepresenttheparentdirectory Youcanusethesingledot,butitdoesn’tmakesensetouseitwiththecdcommand.Later inthechapter,youwillseehowanothercommandusesthesingledotforrelativedirectory referenceseffectively. Thedoubledotcharacterisextremelyhandywhentryingtotraverseadirectoryhierarchy. Forexample,ifyouareintheDocumentsdirectoryunderyourhomedirectoryandneedto gotoyourDownloadsdirectory,alsounderyourhomedirectory,youcandothis: christine@server01:∼/Documents$pwd /home/christine/Documents christine@server01:∼/Documents$cd../Downloads christine@server01:∼/Downloads$pwd /home/christine/Downloads christine@server01:∼/Downloads$ The double dot character takes you back up one level to your home directory; then the /Downloadsportionofthecommandtakesyoubackdownintothe Downloadsdirectory. Youcanuseasmanydoubledotcharactersasnecessarytomovearound.Forexample,if youareinyourhomedirectory(/home/christine)andwanttogotothe /etcdirectory, youcouldtypethefollowing: christine@server01:∼$cd../../etc christine@server01:/etc$pwd /etc christine@server01:/etc$ Ofcourse,inacaselikethis,youactuallyhavetodomoretypingratherthanjusttyping the absolute directory reference, /etc. Thus, use a relative directory reference only if it makessensetodoso. Note It’shelpfultohavealonginformativeshellCLIprompt,asusedinthischapter section.However,forclaritypurposes,asimple$promptisusedintherestofthe book’sexamples. Now that you know how to traverse the directory system and confirm your present workingdirectory,youcanstarttoexplorewhat’scontainedwithinthevariousdirectories. The next section takes you through the process of looking at files within the directory structure. ListingFilesandDirectories To see what files are available on the system, use the list command (ls). This section describesthelscommandandoptionsavailabletoformattheinformationitcandisplay. Displayingabasiclisting The lscommandatitsmostbasicformdisplaysthefilesanddirectorieslocatedinyour currentdirectory: $ls DesktopDownloadsMusicPicturesTemplatesVideos Documentsexamples.desktopmy_scriptPublictest_file $ Noticethatthe lscommandproducesthelistinginalphabeticalorder(incolumnsrather thanrows).Ifyou’reusingaterminalemulatorthatsupportscolor,the lscommandmay also show different types of entries in different colors. The LS_COLORS environment variablecontrolsthisfeature.(EnvironmentvariablesarecoveredinChapter6).Different Linux distributions set this environment variable depending on the capabilities of the terminalemulator. If you don’t have a color terminal emulator, you can use the -F parameter with the ls commandtoeasilydistinguishfilesfromdirectories.Usingthe-Fparameterproducesthe followingoutput: $ls-F Desktop/Downloads/Music/Pictures/Templates/Videos/ Documents/examples.desktopmy_script*Public/test_file $ The-Fparameterflagsthedirectorieswithaforwardslash(/),tohelpidentifytheminthe listing.Similarly,itflagsexecutablefiles(likethe my_scriptfileintheprecedingcode) withanasterisk(*),tohelpyoumoreeasilyfindfilesthatcanberunonthesystem. The basic ls command can be somewhat misleading. It shows the files and directories containedinthecurrentdirectory,butnotnecessarilyallofthem.Linuxoftenuseshidden files to store configuration information. In Linux, hidden files are files with filenames startingwithaperiod(.).Thesefilesdon’tappearinthedefault lslisting.Thus,theyare calledhiddenfiles. Todisplayhiddenfilesalongwithnormalfilesanddirectories,usethe-aparameter.Here isanexampleofusingthe-aparameterwiththelscommand. $ls-a ..compizexamples.desktopMusictest_file ...config.gconfmy_scriptVideos .bash_historyDesktop.gstreamer-0.10Pictures.Xauthority .bash_logout.dmrc.ICEauthority.profile.xsession-errors .bashrcDocuments.localPublic.xsession-errors.old .cacheDownloads.mozillaTemplates $ Allthefilesbeginningwithaperiod,hiddenfiles,arenowshown.Noticethatthreefiles begin with .bash. These are hidden files that are used by the bash shell environment. ThesefeaturesarecoveredindetailinChapter6. The -Rparameterisanotheroptionthe lscommandcanuse.Calledtherecursiveoption, itshowsfilesthatarecontainedwithinsubdirectoriesinthecurrentdirectory.Ifyouhave lotsofsubdirectories,thiscanbequitealonglisting.Here’sasimpleexampleofwhatthe -Rparameterproduces.The-Foptionwastackedontohelpyouseethefiletypes: $ls-F-R .: Desktop/Downloads/Music/Pictures/Templates/Videos/ Documents/examples.desktopmy_script*Public/test_file ./Desktop: ./Documents: ./Downloads: ./Music: ILoveLinux.mp3* ./Pictures: ./Public: ./Templates: ./Videos: $ Notice that the -R parameter shows the contents of the current directory, which are the files from a user’s home directory shown in earlier examples. It also shows each subdirectory in the user’s home directory and their contents. The only subdirectory containing a file is the Music subdirectory, and it contains the executable file, ILoveLinux.mp3. Tip Optionparametersdon’thavetobeenteredseparatelyasshowninthenearby example:ls-F-R.Theycanoftenbecombinedasfollows:ls-FR. Inthepreviousexample,therewerenosubdirectorieswithinsubdirectories.Iftherehad been further subdirectories, the -R parameter would have continued to traverse those as well.Asyoucansee,forlargedirectorystructures,thiscanbecomequitealargeoutput listing. Displayingalonglisting Inthebasiclistings,the lscommanddoesn’tproducemuchinformationabouteachfile. For listing additional information, another popular parameter is -l. The -l parameter produces a long listing format, providing more information about each file in the directory: $ls-l total48 drwxr-xr-x2christinechristine4096Apr2220:37Desktop drwxr-xr-x2christinechristine4096Apr2220:37Documents drwxr-xr-x2christinechristine4096Apr2220:37Downloads -rw-r—r—1christinechristine8980Apr2213:36examples.desktop -rw-rw-r—1christinechristine0May2113:44fall -rw-rw-r—1christinechristine0May2113:44fell -rw-rw-r—1christinechristine0May2113:44fill -rw-rw-r—1christinechristine0May2113:44full drwxr-xr-x2christinechristine4096May2111:39Music -rw-rw-r—1christinechristine0May2113:25my_file -rw-rw-r—1christinechristine0May2113:25my_scrapt -rwxrw-r—1christinechristine54May2111:26my_script -rw-rw-r—1christinechristine0May2113:42new_file drwxr-xr-x2christinechristine4096Apr2220:37Pictures drwxr-xr-x2christinechristine4096Apr2220:37Public drwxr-xr-x2christinechristine4096Apr2220:37Templates -rw-rw-r—1christinechristine0May2111:28test_file drwxr-xr-x2christinechristine4096Apr2220:37Videos $ Thelonglistingformatlistseachfileandsubdirectoryonasingleline.Inadditiontothe filename,thelistingshowsadditionalusefulinformation.Thefirstlineintheoutputshows thetotalnumberofblockscontainedwithinthedirectory.Afterthat,eachlinecontainsthe followinginformationabouteachfile(ordirectory): Thefiletype—suchasdirectory(d),file(-),linkedfile(l),characterdevice(c),or blockdevice(b) Thefilepermissions(seeChapter6) Thenumberoffilehardlinks(Seethesection“LinkingFiles”inChapter7.) Thefileownerusername Thefileprimarygroupname Thefilebytesize Thelasttimethefilewasmodified Thefilenameordirectoryname The-lparameterisapowerfultooltohave.Armedwiththisparameter,youcanseemost oftheinformationyouneedforanyfileordirectory. The ls command has lots of parameters that can come in handy as you do file management.Ifyoutypeattheshellprompt manls,youseeseveralpagesofavailable parametersforyoutousetomodifythelscommandoutput. Don’t forget that you can also combine many of the parameters. You can often find a parameter combination that not only displays the desired output, but also is easy to remember,suchasls-alF. Filteringlistingoutput As you’ve seen in the examples, by default the ls command lists all the non-hidden directory files. Sometimes, this can be overkill, especially when you’re just looking for informationonafewfiles. Fortunately, the ls command also provides a way for you to define a filter on the commandline.Itusesthefiltertodeterminewhichfilesordirectoriesitshoulddisplayin theoutput. Thefilterworksasasimpletext-matchingstring.Includethefilterafteranycommandline parametersyouwanttouse: $ls-lmy_script -rwxrw-r—1christinechristine54May2111:26my_script $ Whenyouspecifythenameofaspecificfileasthefilter,the ls commandonlyshows thatfile’sinformation.Sometimes,youmightnotknowtheexactfilenameyou’relooking for.Thelscommandalsorecognizesstandardwildcardcharactersandusesthemtomatch patternswithinthefilter: Aquestionmark(?)torepresentonecharacter Anasterisk(*)torepresentanynumberofcharacters The question mark can be used to replace exactly one character anywhere in the filter string.Forexample: $ls-lmy_scr?pt -rw-rw-r—1christinechristine0May2113:25my_scrapt -rwxrw-r—1christinechristine54May2111:26my_script $ Thefiltermy_scr?ptmatchedtwofilesinthedirectory.Similarly,theasteriskcanbeused tomatchzeroormorecharacters: $ls-lmy* -rw-rw-r—1christinechristine0May2113:25my_file -rw-rw-r—1christinechristine0May2113:25my_scrapt -rwxrw-r—1christinechristine54May2111:26my_script $ Using the asterisk finds three different files, starting with the name my. As with the questionmark,youcanplacetheasterisksanywhereinthefilter: $ls-lmy_s*t -rw-rw-r—1christinechristine0May2113:25my_scrapt -rwxrw-r—1christinechristine54May2111:26my_script $ Usingtheasteriskandquestionmarkinthefilteriscalledfileglobbing.Fileglobbingis the processing of pattern matching using wildcards. The wildcards are officially called metacharacter wildcards. You can use more metacharacter wildcards for file globbing thanjusttheasteriskandquestionmark.Youcanalsousebrackets: $ls-lmy_scr[ai]pt -rw-rw-r—1christinechristine0May2113:25my_scrapt -rwxrw-r—1christinechristine54May2111:26my_script $ In this example, we used the brackets along with two potential choices for a single character in that position, aor i. The brackets represent a single character position and giveyoumultipleoptionsforfileglobbing.Youcanlistchoicesofcharacters,asshownin theprecedingexample,andyoucanspecifyarangeofcharacters,suchasanalphabetic range[a-i]: $ls-lf[a-i]ll -rw-rw-r—1christinechristine0May2113:44fall -rw-rw-r—1christinechristine0May2113:44fell -rw-rw-r—1christinechristine0May2113:44fill $ Also, you can specify what should not be included in the pattern match by using the exclamationpoint(!): $ls-lf[!a]ll -rw-rw-r—1christinechristine0May2113:44fell -rw-rw-r—1christinechristine0May2113:44fill -rw-rw-r—1christinechristine0May2113:44full $ Fileglobbingisapowerfulfeaturewhensearchingforfiles.Itcanalsobeusedwithother shellcommandsbesidesls.Youfindoutmoreaboutthislaterinthechapter. HandlingFiles The shell provides many file manipulation commands on the Linux filesystem. This sectionwalksyouthroughthebasicshellcommandsyouneedtohandlefiles. Creatingfiles Everyonceinawhileyourunintoasituationwhereyouneedtocreateanemptyfile.For example,sometimesapplicationsexpectalogfiletobepresentbeforetheycanwritetoit. Inthesesituations,youcanusethetouchcommandtoeasilycreateanemptyfile: $touchtest_one $ls-ltest_one -rw-rw-r—1christinechristine0May2114:17test_one $ The touch command creates the new file you specify and assigns your username as the file owner. Notice in the preceding example that the file size is zero because the touch commandjustcreatedanemptyfile. The touch command can also be used to change the modification time. This is done withoutchangingthefilecontents: $ls-ltest_one -rw-rw-r—1christinechristine0May2114:17test_one $touchtest_one $ls-ltest_one -rw-rw-r—1christinechristine0May2114:35test_one $ Themodificationtimeoftest_oneisnowupdatedto14:35fromtheoriginaltime,14:17. Tochangeonlytheaccesstime,usethe-aparameterwiththetouchcommand: $ls-ltest_one -rw-rw-r—1christinechristine0May2114:35test_one $touch-atest_one $ls-ltest_one -rw-rw-r—1christinechristine0May2114:35test_one $ls-l—time=atimetest_one -rw-rw-r—1christinechristine0May2114:55test_one $ Intheprecedingexample,noticethatbyusingonlythe ls-lcommand,theaccesstime doesnotdisplay.Thisisbecausethemodificationtimeisshownbydefault.Toseeafile’s access time, you need to add an additional parameter, —time=atime. After we add that parameterintheprecedingexample,thefile’salteredaccesstimeisdisplayed. CreatingemptyfilesandalteringfiletimestampsisnotsomethingyouwilldoonaLinux systemdaily.However,copyingfilesisanactionyouwilldooftenwhileusingtheshell. Copyingfiles Copyingfilesanddirectoriesfromonelocationinthefilesystemtoanotherisacommon practiceforsystemadministrators.Thecpcommandprovidesthisfeature. Initsmostbasicform,the cpcommandusestwoparameters—thesourceobjectandthe destinationobject:cpsourcedestination. When both the source and destination parameters are filenames, the cp command copies the source file to a new destination file. The new file acts like a brand new file, withanupdatedmodificationtime: $cptest_onetest_two $ls-ltest_* -rw-rw-r—1christinechristine0May2114:35test_one -rw-rw-r—1christinechristine0May2115:15test_two $ Thenewfile test_twoshowsadifferentmodificationtimethanthe test_onefile.Ifthe destinationfilealreadyexists,the cpcommandmaynotpromptyoutothisfact.Itisbest toaddthe-ioptiontoforcetheshelltoaskwhetheryouwanttooverwriteafile: $ls-ltest_* -rw-rw-r—1christinechristine0May2114:35test_one -rw-rw-r—1christinechristine0May2115:15test_two $ $cp-itest_onetest_two cp:overwrite‘test_two’?n $ Ifyoudon’tanswer y,thefilecopydoesnotproceed.Youcanalsocopyafileintoapreexistingdirectory: $cp-itest_one/home/christine/Documents/ $ $ls-l/home/christine/Documents total0 -rw-rw-r—1christinechristine0May2115:25test_one $ The new file is now under the Documents subdirectory, using the same filename as the original. Note Theprecedingexampleusesatrailingforwardslash(/)onthedestinationdirectory name.UsingtheslashindicatesDocumentsisadirectoryandnotafile.Thisis helpfulforclaritypurposesandisimportantwhencopyingsinglefiles.Iftheforward slashisnotusedandthesubdirectory/home/christine/Documentsdoesnotexist, problemscanoccur.Inthiscase,attemptingtocopyasinglefiletotheDocuments subdirectorycreatesafilenamedDocumentsinstead,andnoerrormessagesdisplay! This last example used an absolute directory reference, but you can just as easily use a relativedirectoryreference: $cp-itest_oneDocuments/ cp:overwrite‘Documents/test_one’?y $ $ls-lDocuments total0 -rw-rw-r—1christinechristine0May2115:28test_one $ Earlier in this chapter, you read about the special symbols that can be used in relative directoryreferences.Oneofthem,thesingledot(.),isgreattousewiththe cpcommand. Remember that the single dot represents your present working directory. If you need to copyafilewithalongsourceobjectnametoyourpresentworkingdirectory,thesingle dotcansimplifythetask: $cp-i/etc/NetworkManager/NetworkManager.conf. $ $ls-lNetworkManager.conf -rw-r—r—1christinechristine76May2115:55NetworkManager.conf $ It’s hard to see that single dot! If you look closely, you’ll see it at the end of the first example code line. Using the single dot symbol is much easier than typing a full destinationobjectname,whenyouhavelongsourceobjectnames. Tip Therearemanymorecpcommandparametersthanthosedescribedhere.Remember thatyoucanseeallthedifferentavailableparametersavailableforthecpcommand, bytypingmancp. The-Rparameterisapowerful cpcommandoption.Itallowsyoutorecursivelycopythe contentsofanentiredirectoryinonecommand: $ls-Fd*Scripts Scripts/ $ls-lScripts/ total25 -rwxrw-r—1christinechristine929Apr208:23file_mod.sh -rwxrw-r—1christinechristine254Jan214:18SGID_search.sh -rwxrw-r—1christinechristine243Jan213:42SUID_search.sh $ $cp-RScripts/Mod_Scripts $ls-Fd*Scripts Mod_Scripts/Scripts/ $ls-lMod_Scripts total25 -rwxrw-r—1christinechristine929May2116:16file_mod.sh -rwxrw-r—1christinechristine254May2116:16SGID_search.sh -rwxrw-r—1christinechristine243May2116:16SUID_search.sh $ ThedirectoryMod_Scriptsdidnotexistpriortothe cp-Rcommand.Itwascreatedwith the cp -R command, and the entire Scripts directory’s contents were copied into it. Noticethatallthefilesinthenew Mod_Scriptsdirectoryhavenewdatesassociatedwith them.NowMod_ScriptsisacompletecopyoftheScriptsdirectory. Note Intheprecedingexample,theoptions-Fdwereaddedtothelscommand.Youread aboutthe-Foptionearlierinthischapter.However,the-doptionmaybenewtoyou. The-doptionlistsadirectory’sinformationbutnotitscontents. Youcanalsousewildcardmetacharactersinyourcpcommands: $cp*scriptMod_Scripts/ $ls-lMod_Scripts total26 -rwxrw-r—1christinechristine929May2116:16file_mod.sh -rwxrw-r—1christinechristine54May2116:27my_script -rwxrw-r—1christinechristine254May2116:16SGID_search.sh -rwxrw-r—1christinechristine243May2116:16SUID_search.sh $ ThiscommandcopiedanyfilesthatendedwithscripttoMod_Scripts.Inthiscase,only onefileneededtobecopied:my_script. Whencopyingfiles,anothershellfeaturecanhelpyoubesidesthesingledotandwildcard metacharacters.Itiscalledtabauto-complete. Usingtabauto-complete Whenworkingatthecommandline,youcaneasilymistypeacommand,directoryname, or filename. In fact, the longer a directory reference or filename, the greater the chance thatyouwillmistypeit. Thisiswheretabauto-completecanbealifesaver.Tabauto-completeallowsyoutostart typingafilenameordirectorynameandthenpressthetabkeytohavetheshellcomplete itforyou: $lsreally* really_ridiculously_long_file_name $ $cpreally_ridiculously_long_file_nameMod_Scripts/ ls-lMod_Scripts total26 -rwxrw-r—1christinechristine929May2116:16file_mod.sh -rwxrw-r—1christinechristine54May2116:27my_script -rw-rw-r—1christinechristine0May2117:08 really_ridiculously_long_file_name -rwxrw-r—1christinechristine254May2116:16SGID_search.sh -rwxrw-r—1christinechristine243May2116:16SUID_search.sh $ Intheprecedingexample,wetypedthecommandcpreallyandpressedthetabkey,and theshellauto-completedtherestofthefilename!Ofcourse,thedestinationdirectoryhad to be typed, but still tab auto-complete saved the command from several potential typographicalerrors. Thetricktousingtabauto-completeistogivetheshellenoughfilenamecharacterssoit candistinguishthedesiredfilefromotherfiles.Forexample,ifanotherfilenamestarted with really, pressing the tab key would not auto-complete the filename. Instead, you wouldhearabeep.Ifthishappens,youcanpressthetabkeyagain,andtheshellshows youallthefilenamesstartingwithreally.Thisfeatureallowsyoutoseewhatneedstobe typedfortabauto-completetoworkproperly. Linkingfiles LinkingfilesisagreatoptionavailableintheLinuxfilesystem.Ifyouneedtomaintain two(ormore)copiesofthesamefileonthesystem,insteadofhavingseparatephysical copies,youcanuseonephysicalcopyandmultiplevirtualcopies,calledlinks.Alinkisa placeholderinadirectorythatpointstothereallocationofthefile.Twotypesoffilelinks areavailableinLinux: Asymboliclink Ahardlink A symbolic link is simply a physical file that points to another file somewhere in the virtual directory structure. The two symbolically linked together files do not share the samecontents. Tocreateasymboliclinktoafile,theoriginalfilemustpre-exist.Wecanthenusethe ln commandwiththe-soptiontocreatethesymboliclink: $ls-ldata_file -rw-rw-r—1christinechristine1092May2117:27data_file $ $ln-sdata_filesl_data_file $ $ls-l*data_file -rw-rw-r—1christinechristine1092May2117:27data_file lrwxrwxrwx1christinechristine9May2117:29sl_data_file-> data_file $ In the preceding example, notice that the name of the symbolic link, sl_data_file, is listedsecondinthe lncommand.The —>symboldisplayedafterthesymboliclinkfile’s longlistingshowsthatitissymbolicallylinkedtothefiledata_file. Alsonotethesymboliclink’sfilesizeversusthedatafile’sfilesize.Thesymboliclink, sl_data_file, is only 9 bytes, whereas the data_file is 1092 bytes. This is because sl_data_file is only pointing to data_file. They do not share contents and are two physicallyseparatefiles. Another way to tell that these linked files are separate physical files is by viewing their inodenumber.Theinodenumberofafileordirectoryisauniqueidentificationnumber thatthekernelassignstoeachobjectinthefilesystem.Toviewafileordirectory’sinode number,addthe-iparametertothelscommand: $ls-i*data_file 296890data_file296891sl_data_file $ Theexampleshowsthatthedatafile’sinodenumberis 296890,whilethe sl_data_file inodenumberisdifferent.Itis296891.Thus,theyaredifferentfiles. Ahardlinkcreatesaseparatevirtualfilethatcontainsinformationabouttheoriginalfile andwheretolocateit.However,theyarephysicallythesamefile.Whenyoureferencethe hardlinkfile,it’sjustasifyou’rereferencingtheoriginalfile.Tocreateahardlink,again the original file must pre-exist, except that this time no parameter is needed on the ln command: $ls-lcode_file -rw-rw-r—1christinechristine189May2117:56code_file $ $lncode_filehl_code_file $ $ls-li*code_file 296892-rw-rw-r—2christinechristine189May2117:56 code_file 296892-rw-rw-r—2christinechristine189May2117:56 hl_code_file $ Intheprecedingexample,weusedthe ls-licommandtoshowboththeinodenumbers and a long listing for the *code_files. Notice that both files, which are hard linked together,sharethenameinodenumber.Thisisbecausetheyarephysicallythesamefile. Alsonoticethatthelinkcount(thethirditeminthelisting)nowshowsthatbothfileshave twolinks.Inaddition,theirfilesizeisexactlythesamesizeaswell. Note Youcanonlycreateahardlinkbetweenfilesonthesamephysicalmedium.Tocreate alinkbetweenfilesunderseparatephysicalmediums,youmustuseasymboliclink. Be careful when copying linked files. If you use the cp command to copy a file that’s linkedtoanothersourcefile,allyou’redoingismakinganothercopyofthesourcefile. Thiscanquicklygetconfusing.Insteadofcopyingthelinkedfile,youcancreateanother link to the original file. You can have many links to the same file with no problems. However,youalsodon’twanttocreatesoftlinkstoothersoft-linkedfiles.Thiscreatesa chainoflinksthatcanbeconfusing—andeasilybroken—causingallsortsofproblems. Youmayfindsymbolicandhardlinksdifficultconcepts.Fortunately,renamingfilesinthe nextsectionisagreatdealeasiertounderstand. Renamingfiles IntheLinuxworld,renamingfilesiscalledmovingfiles.The mvcommandisavailableto movebothfilesanddirectoriestoanotherlocationoranewname: $ls-lif?ll 296730-rw-rw-r—1christinechristine0May2113:44fall 296717-rw-rw-r—1christinechristine0May2113:44fell 294561-rw-rw-r—1christinechristine0May2113:44fill 296742-rw-rw-r—1christinechristine0May2113:44full $ $mvfallfzll $ $ls-lif?ll 296717-rw-rw-r—1christinechristine0May2113:44fell 294561-rw-rw-r—1christinechristine0May2113:44fill 296742-rw-rw-r—1christinechristine0May2113:44full 296730-rw-rw-r—1christinechristine0May2113:44fzll $ Notice that moving the file changed the name from fall to fzll, but it kept the same inodenumberandtimestampvalue.Thisisbecausemvaffectsonlyafile’sname. Youcanalsousemvtochangeafile’slocation: $ls-li/home/christine/fzll 296730-rw-rw-r—1christinechristine0May2113:44 /home/christine/fzll $ $ls-li/home/christine/Pictures/ total0 $mvfzllPictures/ $ $ls-li/home/christine/Pictures/ total0 296730-rw-rw-r—1christinechristine0May2113:44fzll $ $ls-li/home/christine/fzll ls:cannotaccess/home/christine/fzll:Nosuchfileordirectory $ In the preceding example, we moved the file fzll from /home/christine to /home/christine/Picturesusingthe mvcommand.Again,therewerenochangestothe file’sinodenumberortimestampvalue. Tip Likethecpcommand,youcanusethe-ioptiononthemvcommand.Thus,youare askedbeforethecommandattemptstooverwriteanypre-existingfiles. The only change was to the file’s location. The fzll file no longer exists in /home/christine, because a copy of it was not left in its original location, as the cp commandwouldhavedone. Youcanusethemvcommandtomoveafile’slocationandrenameit,allinoneeasystep: $ls-liPictures/fzll 296730-rw-rw-r—1christinechristine0May2113:44 Pictures/fzll $ $mv/home/christine/Pictures/fzll/home/christine/fall $ $ls-li/home/christine/fall 296730-rw-rw-r—1christinechristine0May2113:44 /home/christine/fall $ $ls-li/home/christine/Pictures/fzll ls:cannotaccess/home/christine/Pictures/fzll: Nosuchfileordirectory For this example, we moved the file fzll from a subdirectory, Pictures, to the home directory, /home/christine,andrenameditto fall.Neitherthetimestampvaluenorthe inodenumberchanged.Onlythelocationandnamewerealtered. Youcanalsousethemvcommandtomoveentiredirectoriesandtheircontents: $ls-liMod_Scripts total26 296886-rwxrw-r—1christinechristine929May2116:16 file_mod.sh 296887-rwxrw-r—1christinechristine54May2116:27 my_script 296885-rwxrw-r—1christinechristine254May2116:16 SGID_search.sh 296884-rwxrw-r—1christinechristine243May2116:16 SUID_search.sh $ $mvMod_ScriptsOld_Scripts $ $ls-liMod_Scripts ls:cannotaccessMod_Scripts:Nosuchfileordirectory $ $ls-liOld_Scripts total26 296886-rwxrw-r—1christinechristine929May2116:16 file_mod.sh 296887-rwxrw-r—1christinechristine54May2116:27 my_script 296885-rwxrw-r—1christinechristine254May2116:16 SGID_search.sh 296884-rwxrw-r—1christinechristine243May2116:16 SUID_search.sh $ Thedirectory’sentirecontentsareunchanged.Theonlythingthatchangesisthenameof thedirectory. Afteryouknowhowtorename…err…movefileswiththe mvcommand,yourealizehow simpleitistoaccomplish.Anothereasy,butpotentiallydangerous,taskisdeletingfiles. Deletingfiles Most likely at some point you’ll want to be able to delete existing files. Whether it’s to cleanupafilesystemortoremoveasoftwarepackage,youalwayshaveopportunitiesto deletefiles. IntheLinuxworld,deletingiscalledremoving.Thecommandtoremovefilesinthebash shellisrm.Thebasicformofthermcommandissimple: $rm-ifall rm:removeregularemptyfile‘fall’?y $ $ls-lfall ls:cannotaccessfall:Nosuchfileordirectory $ Noticethatthe-icommandparameterpromptsyoutomakesurethatyou’reseriousabout removingthefile.Theshellhasnorecyclebinortrashcan.Afteryouremoveafile,it’s gone forever. Therefore, a good habit is to always tack on the -i parameter to the rm command. Youcanalsousewildcardmetacharacterstoremovegroupsoffiles.However,again,use that-ioptiontoprotectyourself: $rm-if?ll rm:removeregularemptyfile‘fell’?y rm:removeregularemptyfile‘fill’?y rm:removeregularemptyfile‘full’?y $ $ls-lf?ll ls:cannotaccessf?ll:Nosuchfileordirectory $ Oneotherfeatureofthermcommand,ifyou’reremovinglotsoffilesanddon’twanttobe botheredwiththeprompt,istousethe-fparametertoforcetheremoval.Justbecareful! ManagingDirectories Linux has a few commands that work for both files and directories (such as the cp command),andsomethatworkonlyfordirectories.Tocreateanewdirectory,youneedto use a specific command, which is covered in this section. Removing directories can get interesting,sothatiscoveredinthissectionaswell. Creatingdirectories CreatinganewdirectoryinLinuxiseasy—justusethemkdircommand: $mkdirNew_Dir $ls-ldNew_Dir drwxrwxr-x2christinechristine4096May2209:48New_Dir $ The system creates a new directory named New_Dir. Notice in the new directory’s long listingthatthedirectory’srecordbeginswitha d.Thisindicatesthat New_Dirisnotafile, butadirectory. Youcancreatedirectoriesandsubdirectoriesin“bulk”ifneeded.However,ifyouattempt thiswithjustthemkdircommand,yougetthefollowingerrormessage: $mkdirNew_Dir/Sub_Dir/Under_Dir mkdir:cannotcreatedirectory‘New_Dir/Sub_Dir/Under_Dir’: Nosuchfileordirectory $ Tocreateseveraldirectoriesandsubdirectoriesatthesametime,youneedtoaddthe -p parameter: $mkdir-pNew_Dir/Sub_Dir/Under_Dir $ $ls-RNew_Dir New_Dir: Sub_Dir New_Dir/Sub_Dir: Under_Dir New_Dir/Sub_Dir/Under_Dir: $ The-poptiononthemkdircommandmakesanymissingparentdirectoriesasneeded.A parent directory is a directory that contains other directories at the next level down the directorytree. Ofcourse,afteryoumakesomething,youneedtoknowhowtodeleteit.Thisisespecially usefulifyoucreatedadirectoryinthewronglocation. Deletingdirectories Removingdirectoriescanbetricky,andforgoodreason.Therearelotsofopportunities forbadthingstohappenwhenyoustartdeletingdirectories.Theshelltriestoprotectus from accidental catastrophes as much as possible. The basic command for removing a directoryisrmdir: $touchNew_Dir/my_file $ls-liNew_Dir/ total0 294561-rw-rw-r—1christinechristine0May2209:52my_file $ $rmdirNew_Dir rmdir:failedtoremove‘New_Dir’:Directorynotempty $ Bydefault,the rmdircommandworksonlyforremovingemptydirectories.Becausewe createdafile,my_file,intheNew_Dirdirectory,thermdircommandrefusestoremoveit. Tofixthis,wemustremovethefilefirst.Thenwecanusethermdircommandonthenow emptydirectory: $rm-iNew_Dir/my_file rm:removeregularemptyfile‘New_Dir/my_file’?y $ $rmdirNew_Dir $ $ls-ldNew_Dir ls:cannotaccessNew_Dir:Nosuchfileordirectory Thermdirhasno -ioptiontoaskifyouwanttoremovethedirectory.Thisisonereason itishelpfulthatrmdirremovesonlyemptydirectories. You can also use the rm command on entire non-empty directories. Using the -roption allowsthecommandtodescendintothedirectory,removethefiles,andthenremovethe directoryitself: $ls-lMy_Dir total0 -rw-rw-r—1christinechristine0May2210:02another_file $ $rm-riMy_Dir rm:descendintodirectory‘My_Dir’?y rm:removeregularemptyfile‘My_Dir/another_file’?y rm:removedirectory‘My_Dir’?y $ $ls-lMy_Dir ls:cannotaccessMy_Dir:Nosuchfileordirectory $ Thisalsoworksfordescendingintomultiplesubdirectoriesandisespeciallyusefulwhen youhavelotsofdirectoriesandfilestodelete: $ls-FRSmall_Dir Small_Dir: a_fileb_filec_fileTeeny_Dir/Tiny_Dir/ Small_Dir/Teeny_Dir: e_file Small_Dir/Tiny_Dir: d_file $ $rm-irSmall_Dir rm:descendintodirectory‘Small_Dir’?y rm:removeregularemptyfile‘Small_Dir/a_file’?y rm:descendintodirectory‘Small_Dir/Tiny_Dir’?y rm:removeregularemptyfile‘Small_Dir/Tiny_Dir/d_file’?y rm:removedirectory‘Small_Dir/Tiny_Dir’?y rm:descendintodirectory‘Small_Dir/Teeny_Dir’?y rm:removeregularemptyfile‘Small_Dir/Teeny_Dir/e_file’?y rm:removedirectory‘Small_Dir/Teeny_Dir’?y rm:removeregularemptyfile‘Small_Dir/c_file’?y rm:removeregularemptyfile‘Small_Dir/b_file’?y rm:removedirectory‘Small_Dir’?y $ $ls-FRSmall_Dir ls:cannotaccessSmall_Dir:Nosuchfileordirectory $ Althoughthisworks,it’ssomewhatawkward.Noticethatyoustillmustverifyeachand everyfilethatgetsremoved.Foradirectorywithlotsoffilesandsubdirectories,thiscan becometedious. Note Forthermcommand,the-rparameterandthe-Rparameterworkexactlythesame. Whenusedwiththermcommand,the-Rparameteralsorecursivelytraversesthrough thedirectoryremovingfiles.Itisunusualforashellcommandtohavedifferentcased parameterswiththesamefunction. Theultimatesolutionforthrowingcautiontothewindandremovinganentiredirectory, contentsandall,isthermcommandwithboththe-rand-fparameters: $treeSmall_Dir Small_Dir ├a_file ├b_file ├c_file ├Teeny_Dir |└e_file └Tiny_Dir └d_file 2directories,5files $ $rm-rfSmall_Dir $ $treeSmall_Dir Small_Dir[erroropeningdir] 0directories,0files $ The rm-rfcommandgivesnowarningsandnofanfare.This,ofcourse,isanextremely dangeroustooltohave,especiallyifhavesuperuserprivileges.Useitsparingly,andonly aftertriplecheckingtomakesurethatyou’redoingexactlywhatyouwanttodo! Note Noticeintheprecedingexamplethatweusedthetreeutility.Itnicelydisplays directories,subdirectories,andtheirfiles.It’sausefulutilitywhenyouneedto understandadirectorystructure,especiallybeforeremovingit.Thisutilitymaynot beinstalledbydefaultinyourLinuxdistribution.SeeChapter9forlearningabout installingsoftware In the last few sections, you looked at managing both files and directories. So far we coveredeverythingyouneedtoknowaboutfiles,exceptforhowtopeekinsideofthem. ViewingFileContents Youcanuseseveralcommandsforlookinginsidefileswithouthavingtopulloutatext editorutility(seeChapter10).Thissectiondemonstratesafewofthecommandsyouhave availabletohelpyouexaminefiles. Viewingthefiletype Beforeyougochargingofftryingtodisplayafile,trytogetahandleonwhattypeoffile itis.Ifyoutrytodisplayabinaryfile,yougetlotsofgibberishonyourmonitorandmay evenlockupyourterminalemulator. Thefilecommandisahandylittleutilitytohavearound.Itcanpeekinsideofafileand determinejustwhatkindoffileitis: $filemy_file my_file:ASCIItext $ Thefileintheprecedingexampleisa textfile.The filecommanddeterminednotonly thatthefilecontainstextbutalsothecharactercodeformatofthetextfile,ASCII. Thisfollowingexampleshowsafilethatissimplyadirectory.Thus,the filecommand givesyouanothermethodtodistinguishadirectory: $fileNew_Dir New_Dir:directory $ Thisthird filecommandexampleshowsafile,whichisasymboliclink.Notethatthe filecommandeventellsyoutowhichfileitissymbolicallylinked: $filesl_data_file sl_data_file:symboliclinkto‘data_file’ $ Thefollowingexampleshowswhatthe filecommandreturnsforascriptfile.Although thefileisASCIItext,becauseit’sascriptfile,youcanexecute(run)itonthesystem: $filemy_script my_script:Bourne-Againshellscript,ASCIItextexecutable $ The final example is a binary executable program. The file command determines the platformthattheprogramwascompiledforandwhattypesoflibrariesitrequires.Thisis an especially handy feature if you have a binary executable program from an unknown source: $file/bin/ls /bin/ls:ELF64-bitLSBexecutable,x86-64,version1(SYSV), dynamicallylinked(usessharedlibs),forGNU/Linux2.6.24, […] $ Nowthatyouknowaquickmethodforviewingafile’stype,youcanstartdisplayingand viewingfiles. Viewingthewholefile Ifyouhavealargetextfileonyourhands,youmaywanttobeabletoseewhat’sinsideof it.Linuxhasthreedifferentcommandsthatcanhelpyouhere. Usingthecatcommand Thecatcommandisahandytoolfordisplayingallthedatainsideatextfile: $cattest1 hello Thisisatestfile. Thatwe’llusetotestthecatcommand. $ Nothingtooexciting,justthecontentsofthetextfile.However,the catcommandhasa fewparametersthatcanhelpyouout. The-nparameternumbersallthelinesforyou: $cat-ntest1 1hello 2 3Thisisatestfile. 4 5 6Thatwe’llusetotestthecatcommand. $ That feature will come in handy when you’re examining scripts. If you just want to numberthelinesthathavetextinthem,the-bparameterisforyou: $cat-btest1 1hello 2Thisisatestfile. 3Thatwe’llusetotestthecatcommand. $ Finally,ifyoudon’twanttabcharacterstoappear,usethe-Tparameter: $cat-Ttest1 hello Thisisatestfile. Thatwe’lluseto∧Itestthecatcommand. $ The-Tparameterreplacesanytabsinthetextwiththe∧Icharactercombination. For large files, the cat command can be somewhat annoying. The text in the file just quickly scrolls off the display without stopping. Fortunately, we have a simple way to solvethisproblem. Usingthemorecommand Themaindrawbackofthe catcommandisthatyoucan’tcontrolwhat’shappeningafter you start it. To solve that problem, developers created the more command. The more commanddisplaysatextfile,butstopsafteritdisplayseachpageofdata.Wetypedthe commandmore/etc/bash.bashrctoproducethesamplemorescreenshowninFigure3.3. Figure3.3Usingthemorecommandtodisplayatextfile Notice at the bottom of the screen in Figure3.3 that the more command displays a tag showingthatyou’restillinthe moreapplicationandhowfaralong(56%)inthetextfile youare.Thisisthepromptforthemorecommand. Themorecommandisapagerutility.Rememberfromearlierinthischapterapagerutility displays selected bash manual pages when you use the man command. Similarly to navigating through the man pages, you can use more to navigate through a text file by pressingthespacebaroryoucangoforwardlinebylineusingtheEnterkey.Whenyou arefinishednavigatingthroughthefileusingmore,presstheqkeytoquit. The more command allows some rudimentary movement through the text file. For more advancedfeatures,trythelesscommand. Usingthelesscommand Fromitsname,itsoundslikeitshouldn’tbeasadvancedasthemorecommand.However, the less command name is actually a play on words and is an advanced version of the more command (the less command name comes from the phrase “less is more”). It providesseveralveryhandyfeaturesforscrollingbothforwardandbackwardthrougha textfile,aswellassomeprettyadvancedsearchingcapabilities. The lesscommandcanalsodisplayafile’scontentsbeforeitfinishesreadingtheentire file.Thecatandmorecommandscannotdothis. Thelesscommandoperatesmuchthesameasthe morecommand,displayingonescreen oftextfromafileatatime.Itsupportsthesamecommandsetasthemorecommand,plus manymoreoptions. Tip Toseealltheoptionsavailableforthelesscommand,viewitsmanpagesbytyping manless.Youcandothesameforthemorecommandtoseethereferencematerial concerningitsvariousoptionsaswell. Onesetoffeaturesisthatthe lesscommandrecognizestheupanddownarrowkeysas wellasthePageUpandPageDownkeys(assumingthatyou’reusingaproperlydefined terminal).Thisgivesyoufullcontrolwhenviewingafile. Viewingpartsofafile Oftenthedatayouwanttoviewislocatedeitherrightatthetoporburiedatthebottomof atextfile.Iftheinformationisatthetopofalargefile,youstillneedtowaitforthe cat or more commands to load the entire file before you can view it. If the information is locatedatthebottomofafile(suchasalogfile),youneedtowadethroughthousandsof lines of text just to get to the last few entries. Fortunately, Linux has specialized commandstosolvebothoftheseproblems. Usingthetailcommand The tailcommanddisplaysthelastlinesinafile(thefile’s“tail”).Bydefault,itshows thelast10linesinthefile. Fortheseexamples,wecreatedatextfilecontaining20textlines.Itisdisplayedherein itsentiretyusingthecatcommand: $catlog_file line1 line2 line3 line4 line5 HelloWorld-line6 line7 line8 line9 line10 line11 Helloagain-line12 line13 line14 line15 Sweet-line16 line17 line18 line19 Lastline-line20 $ Nowthatyouhaveseentheentiretextfile,youcanseetheeffectofusing tailtoview thefile’slast10lines: $taillog_file line11 Helloagain-line12 line13 line14 line15 Sweet-line16 line17 line18 line19 Lastline-line20 $ Youcanchangethenumberoflinesshownusing tailbyincludingthe -nparameter.In thisexample,onlythelasttwolinesofthefilearedisplayed,byadding -n2tothe tail command: $tail-n2log_file line19 Lastline-line20 $ The -f parameter is a pretty cool feature of the tail command. It allows you to peek insideafileasthefileisbeingusedbyotherprocesses.The tailcommandstaysactive andcontinuestodisplaynewlinesastheyappearinthetextfile.Thisisagreatwayto monitorthesystemlogfilesinreal-timemode. Usingtheheadcommand The head command does what you’d expect; it displays a file’s first group of lines (the file’s“head”).Bydefault,itdisplaysthefirst10linesoftext: $headlog_file line1 line2 line3 line4 line5 HelloWorld-line6 line7 line8 line9 line10 $ Similartothe tailcommand,the headcommandsupportsthe -nparametersoyoucan alterwhat’sdisplayed.Bothcommandsalsoallowyoutosimplytypeadashalongwith thenumberoflinestodisplay,asshownhere: $head-5log_file line1 line2 line3 line4 line5 $ Usuallythebeginningofafiledoesn’tchange,sotheheadcommanddoesn’tsupportthefparameterfeatureasthetailcommanddoes.Theheadcommandisahandywaytojust peekatthebeginningofafile. Summary ThischaptercoveredthebasicsofworkingwiththeLinuxfilesystemfromashellprompt. We began with a discussion of the bash shell and showed you how to interact with the shell.Thecommandlineinterface(CLI)usesapromptstringtoindicatewhenit’sready foryoutoentercommands. Theshellprovidesawealthofutilitiesyoucanusetocreateandmanipulatefiles.Before youstartplayingwithfiles,youshouldunderstandhowLinuxstoresthem.Thischapter discussedthebasicsoftheLinuxvirtualdirectoryandshowedyouhowLinuxreferences storage media devices. After describing the Linux filesystem, the chapter walked you throughusingthecdcommandtomovearoundthevirtualdirectory. Aftershowingyouhowtogettoadirectory,thechapterdemonstratedhowtousethe ls commandtolistthefilesandsubdirectories.Lotsofparameterscancustomizetheoutput ofthe lscommand.Youcanobtaininformationonfilesanddirectoriesbyusingthe ls command. The touch command is useful for creating empty files and for changing the access or modificationtimesonanexistingfile.Thechapteralsodiscussedusingthe cpcommand tocopyexistingfilesfromonelocationtoanother.Itwalkedyouthroughtheprocessof linkingfilesinsteadofcopyingthem,providinganeasywaytohavethesamefileintwo locationswithoutmakingaseparatecopy.Thelncommandprovidesthislinkingability. Next, you learned how to rename files (called moving) in Linux using the mv command and saw how to delete files (called removing) using the rm command. This chapter also showedyouhowtoperformthesametaskswithdirectories,usingthe mkdirand rmdir commands. Finally, the chapter closed with a discussion on viewing the contents of files. The cat, more,and lesscommandsprovideeasymethodsforviewingtheentirecontentsofafile, whilethe tailand headcommandsaregreatforpeekinginsideafiletojustseeasmall portionofit. The next chapter continues the discussion on bash shell commands. We’ll look at more advanced administrator commands that come in handy as you administer your Linux system. Chapter4 MorebashShellCommands InThisChapter 1. Managingprocesses 2. Gettingdiskstatistics 3. Mountingnewdisks 4. Sortingdata 5. Archivingdata Chapter3coveredthebasicsofwalkingthroughtheLinuxfilesystemandworking withfilesanddirectories.Fileanddirectorymanagementisamajorfeatureofthe Linuxshell;however,weshouldlookatsomeotherthingsbeforewestartourscript programming.ThischapterdigsintotheLinuxsystemmanagementcommands, showingyouhowtopeekinsideyourLinuxsystemusingcommandlinecommands. Afterthat,weshowyouafewhandycommandsthatyoucanusetoworkwithdata filesonthesystem. MonitoringPrograms OneofthetoughestjobsofbeingaLinuxsystemadministratoriskeepingtrackofwhat’s running on the system — especially now, when graphical desktops take a handful of programsjusttoproduceasingledesktop.Youalwayshavelotsofprogramsrunningon thesystem. Fortunately,afewcommandlinetoolsareavailabletohelpmakelifeeasierforyou.This sectioncoversafewofthebasictoolsyouneedtoknowhowtousetomanageprograms onyourLinuxsystem. Peekingattheprocesses When a program runs on the system, it’s referred to as a process. To examine these processes,youneedtobecomefamiliarwiththe pscommand,theSwissArmyknifeof utilities.Itcanproducelotsofinformationaboutalltheprogramsrunningonyoursystem. Unfortunately, with this robustness comes complexity — in the form of numerous parameters—makingthe pscommandprobablyoneofthemostdifficultcommandsto master. Most system administrators find a subset of these parameters that provide the informationtheywant,andtheystickwithusingonlythose. That said, however, the basic ps command doesn’t really provide all that much information: $ps PIDTTYTIMECMD 3081pts/000:00:00bash 3209pts/000:00:00ps $ Nottooexciting.Bydefault,thepscommandshowsonlytheprocessesthatbelongtothe current user and that are running on the current terminal. In this case, we had only our bash shell running (remember, the shell is just another program running on the system) and,ofcourse,thepscommanditself. The basic output shows the process ID (PID) of the programs, the terminal (TTY) that theyarerunningfrom,andtheCPUtimetheprocesshasused. Note Thetrickyfeatureofthepscommand(andthepartthatmakesitsocomplicated)is thatatonetimethereweretwoversionsofit.Eachversionhaditsownsetof commandlineparameterscontrollingwhatinformationitdisplayedandhow. Recently,Linuxdevelopershavecombinedthetwopscommandformatsintoasingle psprogram(andofcourseaddedtheirowntouches). The GNU ps command that’s used in Linux systems supports three different types of commandlineparameters: Unix-styleparameters,whichareprecededbyadash BSD-styleparameters,whicharenotprecededbyadash GNUlongparameters,whichareprecededbyadoubledash Thefollowingsectionsexaminethethreedifferentparametertypesandshowexamplesof howtheywork. Unix-styleparameters TheUnix-styleparametersoriginatedwiththeoriginalpscommandthatranontheAT&T UnixsystemsinventedbyBellLabs.Table4.1showstheseparameters. Table4.1ThepsCommandUnixParameters Parameter -Ggrplist Description Showsallprocesses Showstheoppositeofthespecifiedparameters Showsallprocessesexceptsessionheadersandprocesseswithouta terminal Showsallprocessesexceptsessionheaders Showsallprocesses Showsprocessescontainedinthelistcmdlist ShowsprocesseswithagroupIDlistedingrplist -U userlist Showsprocessesownedbyauseridlistedinuserlist -ggrplist -ppidlist Showsprocessesbysessionorbygroupidcontainedingrplist ShowsprocesseswithPIDsinthelistpidlist -s sesslist ShowsprocesseswithsessionIDinthelistsesslist -tttylist ShowsprocesseswithterminalIDinthelistttylist -A -N -a -d -e -Ccmslist -u userlist Showsprocessesbyeffectiveuseridinthelistuserlist Usesextrafulloutput -Oformat Displaysspecificcolumnsinthelistformat,alongwiththedefaultcolumns -M Displayssecurityinformationabouttheprocess -c Showsadditionalschedulerinformationabouttheprocess -f Displaysafullformatlisting -j Showsjobinformation -l Displaysalonglisting -oformat Displaysonlyspecificcolumnslistedinformat -y Preventsdisplayofprocessflags -Z Displaysthesecuritycontextinformation -H Displaysprocessesinahierarchicalformat(showingparentprocesses) -F -n namelist DefinesthevaluestodisplayintheWCHANcolumn -w Useswideoutputformat,forunlimitedwidthdisplays Showsprocessthreads Displaystheversionofps -L -V That’salotofparameters,andtherearestillmore!Thekeytousingthe pscommandis not to memorize all the available parameters — only those you find most useful. Most Linuxsystemadministratorshavetheirownsetsofparametersthattheyuseforextracting pertinentinformation.Forexample,ifyouneedtoseeeverythingrunningonthesystem, use the -ef parameter combination (the ps command lets you combine parameters like this): $ps-ef UIDPIDPPIDCSTIMETTYTIMECMD root10011:29?00:00:01init[5] root20011:29?00:00:00[kthreadd] root32011:29?00:00:00[migration/0] root42011:29?00:00:00[ksoftirqd/0] root52011:29?00:00:00[watchdog/0] root62011:29?00:00:00[events/0] root72011:29?00:00:00[khelper] root472011:29?00:00:00[kblockd/0] root482011:29?00:00:00[kacpid] 6823491011:30?00:00:00hald root30781981012:00?00:00:00sshd:rich[priv] rich30803078012:00?00:00:00sshd:rich@pts/0 rich30813080012:00pts/000:00:00-bash rich44453081313:48pts/000:00:00ps-ef $ Quiteafewlineshavebeencutfromtheoutputtosavespace,butyoucanseethatlotsof processes are running on a Linux system. This example uses two parameters: the -e parameter, which shows all the processes running on the system, and the -f parameter, whichexpandstheoutputtoshowafewusefulcolumnsofinformation: UID:Theuserresponsibleforlaunchingtheprocess PID:TheprocessIDoftheprocess PPID:ThePIDoftheparentprocess(ifaprocessisstartedbyanotherprocess) C:Processorutilizationoverthelifetimeoftheprocess STIME:Thesystemtimewhentheprocessstarted TTY:Theterminaldevicefromwhichtheprocesswaslaunched TIME:ThecumulativeCPUtimerequiredtoruntheprocess CMD:Thenameoftheprogramthatwasstarted This produces a reasonable amount of information, which is what many system administrators want to see. For even more information, you can use the -l parameter, whichproducesthelongformatoutput: $ps-l FSUIDPIDPPIDCPRINIADDRSZWCHANTTYTIMECMD 0S500308130800800-1173waitpts/000:00:00bash 0R500446330811800-1116-pts/000:00:00ps $ Noticetheextracolumnsthatappearwhenyouusethe-lparameter: F:Systemflagsassignedtotheprocessbythekernel S:Thestateoftheprocess(O=runningonprocessor;S=sleeping;R=runnable, waitingtorun;Z=zombie,processterminatedbutparentnotavailable;T=process stopped) PRI:Thepriorityoftheprocess(highernumbersmeanlowerpriority) NI:Thenicevalue,whichisusedfordeterminingpriorities ADDR:Thememoryaddressoftheprocess SZ:Approximateamountofswapspacerequirediftheprocesswasswappedout WCHAN:Addressofthekernelfunctionwheretheprocessissleeping BSD-styleparameters Now that you’ve seen the Unix parameters, let’s look at the BSD-style parameters. The BerkeleySoftwareDistribution(BSD)wasaversionofUnixdevelopedat(ofcourse)the University of California, Berkeley. It had many subtle differences from the AT&T Unix system,thussparkingmanyUnixwarsovertheyears.Table4.2showstheBSDversionof thepscommandparameters. Table4.2ThepsCommandBSDParameters Parameter Description x Showsallprocessesassociatedwiththisterminal Showsallprocessesassociatedwithanyterminal Showsallprocessesincludingsessionheaders Showsonlyrunningprocesses Showsallprocesses,eventhosewithoutaterminaldeviceassigned U userlist Showsprocessesownedbyauseridlistedinuserlist T a g r ppidlist ShowsprocesseswithaPIDlistedinpidlist tttylist Showsprocessesassociatedwithaterminallistedinttylist Oformat Listsspecificcolumnsinformattodisplayalongwiththestandardcolumns X Displaysdataintheregisterformat Z Includessecurityinformationintheoutput j Showsjobinformation l Usesthelongformat oformat Displaysonlycolumnsspecifiedinformat s Usesthesignalformat u Usestheuser-orientedformat v Usesthevirtualmemoryformat N namelist DefinesthevaluestouseintheWCHANcolumn Oorder Definestheorderinwhichtodisplaytheinformationcolumns Sumsnumericalinformation,suchasCPUandmemoryusage,forchild processesintotheparentprocess Displaysthetruecommandname(thenameoftheprogramusedtostartthe process) Displaysanyenvironmentvariablesusedbythecommand Displaysprocessesinahierarchicalformat,showingwhichprocessesstarted whichprocesses Preventsdisplayoftheheaderinformation Definesthecolumn(s)touseforsortingtheoutput UsesnumericvaluesforuserandgroupIDs,alongwithWCHANinformation Produceswideoutputforwiderterminals Displaysthreadsasiftheywereprocesses Displaysthreadsaftertheirprocesses Listsallformatspecifiers Displaystheversionofps S c e f h ksort n w H m L V Asyoucansee,theUnixandBSDtypesofparametershavelotsofoverlap.Mostofthe informationyoucangetfromoneyoucanalsogetfromtheother.Mostofthetime,you choose a parameter type based on which format you’re more comfortable with (for example,ifyouwereusedtoaBSDenvironmentbeforeusingLinux). When you use the BSD-style parameters, the ps command automatically changes the outputtosimulatetheBSDformat.Here’sanexampleusingthelparameter: $psl FUIDPIDPPIDPRINIVSZRSSWCHANSTATTTYTIMECOMMAND 05003081308020046921432waitSspts/00:00-bash 0500510430812004468844-R+pts/00:00psl $ Notice that while many of the output columns are the same as when we used the Unixstyleparameters,somedifferentonesappearaswell: VSZ:Thesizeinkilobytesoftheprocessinmemory RSS:Thephysicalmemorythataprocesshasusedthatisn’tswappedout STAT:Atwo-characterstatecoderepresentingthecurrentprocessstate Many system administrators like the BSD-style l parameter because it produces a more detailed state code for processes (the STAT column). The two-character code more precisely defines exactly what’s happening with the process than the single-character Unix-styleoutput. ThefirstcharacterusesthesamevaluesastheUnix-styleSoutputcolumn,showingwhen a process is sleeping, running, or waiting. The second character further defines the process’sstatus: <:Theprocessisrunningathighpriority. N:Theprocessisrunningatlowpriority. L:Theprocesshaspageslockedinmemory. s:Theprocessisasessionleader. l:Theprocessismulti-threaded. +:Theprocessisrunningintheforeground. From the simple example shown previously, you can see that the bash command is sleeping, but it is a session leader (it’s the main process in my session), whereas the ps commandwasrunningintheforegroundonthesystem. TheGNUlongparameters Finally,theGNUdevelopersputtheirowntouchesonthenew,improvedpscommandby addingafewmoreoptionstotheparametermix.SomeoftheGNUlongparameterscopy existingUnix-orBSD-styleparameters,whileothersprovidenewfeatures.Table4.3lists theavailableGNUlongparameters. Table4.3ThepsCommandGNUParameters Parameter —deselect —Groupgrplist —Useruserlist —groupgrplist —pidpidlist —ppidpidlist —sidsidlist —ttyttylist —useruserlist —formatformat —context —colsn —columnsn —cumulative —forest —headers —no-headers —linesn —rowsn —sortorder —widthn —help —info —version Description Showsallprocessesexceptthoselistedinthecommandline ShowsprocesseswhosegroupIDislistedingrplist ShowsprocesseswhoseuserIDislistedinuserlist ShowsprocesseswhoseeffectivegroupIDislistedingrplist ShowsprocesseswhoseprocessIDislistedinpidlist ShowsprocesseswhoseparentprocessIDislistedinpidlist ShowsprocesseswhosesessionIDislistedinsidlist ShowsprocesseswhoseterminaldeviceIDislistedinttylist ShowsprocesseswhoseeffectiveuserIDislistedinuserlist Displaysonlycolumnsspecifiedintheformat Displaysadditionalsecurityinformation Setsscreenwidthtoncolumns Setsscreenwidthtoncolumns Includesstoppedchildprocessinformation Displaysprocessesinahierarchicallistingshowingparentprocesses Repeatscolumnheadersoneachpageofoutput Preventsdisplayofcolumnheaders Setsthescreenheighttonlines Setsthescreenheighttonrows Definesthecolumn(s)touseforsortingtheoutput Setsthescreenwidthtoncolumns Displaysthehelpinformation Displaysdebugginginformation Displaystheversionofthepsprogram You can combine GNU long parameters with either Unix- or BSD-style parameters to really customize your display. One cool feature of GNU long parameters that we really likeisthe —forestparameter.Itdisplaysthehierarchicalprocessinformation,butusing ASCIIcharacterstodrawcutecharts: 1981?00:00:00sshd 3078?00:00:00\_sshd 3080?00:00:00\_sshd 3081pts/000:00:00\_bash 16676pts/000:00:00\_ps Thisformatmakestracingchildandparentprocessesasnap! Real-timeprocessmonitoring Thepscommandisgreatforgleaninginformationaboutprocessesrunningonthesystem, but it has one drawback. The ps command can display information only for a specific pointintime.Ifyou’retryingtofindtrendsaboutprocessesthatarefrequentlyswappedin andoutofmemory,it’shardtodothatwiththepscommand. Instead, the top command can solve this problem. The top command displays process informationsimilarlytothe pscommand,butitdoesitinreal-timemode.Figure4.1isa snapshotofthetopcommandinaction. Figure4.1Theoutputofthetopcommandwhileitisrunning Thefirstsectionoftheoutputshowsgeneralsysteminformation.Thefirstlineshowsthe current time, how long the system has been up, the number of users logged in, and the loadaverageonthesystem. Theloadaverageappearsasthreenumbers:the1-minute,5-minute,and15-minuteload averages. The higher the values, the more load the system is experiencing. It’s not uncommon for the 1-minute load value to be high for short bursts of activity. If the 15minuteloadvalueishigh,yoursystemmaybeintrouble. Note ThetrickinLinuxsystemadministrationisdefiningwhatexactlyahighloadaverage valueis.Thisvaluedependsonwhat’snormallyrunningonyoursystemandthe hardwareconfiguration.What’shighforonesystemmightbenormalforanother. Usually,ifyourloadaveragesstartgettingover2,thingsaregettingbusyonyour system. The second line shows general process information (called tasks in top): how many processes are running, sleeping, stopped, and zombie (have finished but their parent processhasn’tresponded). The next line shows general CPU information. The top display breaks down the CPU utilization into several categories depending on the owner of the process (user versus systemprocesses)andthestateoftheprocesses(running,idle,orwaiting). Following that are two lines that detail the status of the system memory. The first line showsthestatusofthephysicalmemoryinthesystem,howmuchtotalmemorythereis, howmuchiscurrentlybeingused,andhowmuchisfree.Thesecondmemorylineshows the status of the swap memory area in the system (if any is installed), with the same information. Finally, the next section shows a detailed list of the currently running processes, with someinformationcolumnsthatshouldlookfamiliarfromthepscommandoutput: PID:TheprocessIDoftheprocess USER:Theusernameoftheowneroftheprocess PR:Thepriorityoftheprocess NI:Thenicevalueoftheprocess VIRT:Thetotalamountofvirtualmemoryusedbytheprocess RES:Theamountofphysicalmemorytheprocessisusing SHR:Theamountofmemorytheprocessissharingwithotherprocesses S:Theprocessstatus(D=interruptiblesleep,R=running,S=sleeping,T=tracedor stopped,orZ=zombie) %CPU:TheshareofCPUtimethattheprocessisusing %MEM:Theshareofavailablephysicalmemorytheprocessisusing TIME+:ThetotalCPUtimetheprocesshasusedsincestarting COMMAND:Thecommandlinenameoftheprocess(programstarted) Bydefault,whenyoustart top, it sorts the processes based on the %CPUvalue.Youcan changethesortorderbyusingoneofseveralinteractivecommandswhile topisrunning. Eachinteractivecommandisasinglecharacterthatyoucanpresswhile top isrunning andchangesthebehavioroftheprogram.Pressingfallowsyoutoselectthefieldtouseto sorttheoutput,andpressingdallowsyoutochangethepollinginterval.Pressqtoexitthe topdisplay. You have lots of control over the output of the top command. Using this tool, you can oftenfindoffendingprocessesthathavetakenoveryoursystem.Ofcourse,afteryoufind one,thenextjobistostopit,whichbringsustothenexttopic. Stoppingprocesses Acrucialpartofbeingasystemadministratorisknowingwhenandhowtostopaprocess. Sometimes,aprocessgetshungupandneedsagentlenudgetoeithergetgoingagainor stop. Other times, a process runs away with the CPU and refuses to give it up. In both cases,youneedacommandthatallowsyoutocontrolaprocess.LinuxfollowstheUnix methodofinterprocesscommunication. In Linux, processes communicate with each other using signals. A process signal is a predefined message that processes recognize and may choose to ignore or act on. The developers program how a process handles signals. Most well-written applications have theabilitytoreceiveandactonthestandardUnixprocesssignals.Table4.4showsthese signals. Table4.4LinuxProcessSignals Signal 1 2 3 9 11 15 17 18 19 Name Description HUP Hangsup INT Interrupts QUIT Stopsrunning KILL Unconditionallyterminates SEGV Producessegmentviolation TERM Terminatesifpossible STOP Stopsunconditionally,butdoesn’tterminate TSTP Stopsorpauses,butcontinuestoruninbackground CONT ResumesexecutionafterSTOPorTSTP TwocommandsavailableinLinuxallowyoutosendprocesssignalstorunningprocesses. Thekillcommand The kill command allows you to send signals to processes based on their process ID (PID). By default, the kill command sends a TERM signal to all the PIDs listed on the commandline.Unfortunately,youcanonlyusetheprocessPIDinsteadofitscommand name,makingthekillcommanddifficulttousesometimes. Tosendaprocesssignal,youmusteitherbetheowneroftheprocessorbeloggedinas therootuser. $kill3940 -bash:kill:(3940)-Operationnotpermitted $ The TERM signal tells the process to kindly stop running. Unfortunately, if you have a runawayprocess,mostlikelyitignorestherequest.Whenyouneedtogetforceful,the -s parameterallowsyoutospecifyothersignals(eitherusingtheirnameorsignalnumber). As you can see from the following example, no output is associated with the kill command. #kill-sHUP3940 # Toseeifthecommandwaseffective,youmustperformanotherpsortopcommandtosee iftheoffendingprocessstopped. Thekillallcommand The killall command is a powerful way to stop processes by using their names rather thanthePIDnumbers.The killall command allows you to use wildcard characters as well,makingitaveryusefultoolwhenyouhaveasystemthat’sgoneawry: #killallhttp* # Thisexamplekillsalltheprocessesthatstartwithhttp,suchasthehttpdservicesforthe Apachewebserver. Caution Beextremelycarefulusingthekillallcommandwhenloggedinastherootuser.It’s easytogetcarriedawaywithwildcardcharactersandaccidentallystopimportant systemprocesses.Thiscouldleadtoadamagedfilesystem. MonitoringDiskSpace Anotherimportanttaskofthesystemadministratoristokeeptrackofthediskusageon thesystem.Whetheryou’rerunningasimpleLinuxdesktoporalargeLinuxserver,you needtoknowhowmuchspaceyouhaveforyourapplications. Some command line commands can help you manage the media environment on your Linuxsystem.Thissectiondescribesthecorecommandsyou’lllikelyrunintoduringyour systemadministrationduties. Mountingmedia As discussed in Chapter 3, the Linux filesystem combines all media disks into a single virtualdirectory.Beforeyoucanuseanewmediadiskonyoursystem,youmustplaceit inthevirtualdirectory.Thistaskiscalledmounting. In today’s graphical desktop world, most Linux distributions have the ability to automatically mount specific types of removablemedia. A removable media device is a mediumthat(obviously)canbeeasilyremovedfromthePC,suchasCD-ROMsandUSB memorysticks. If you’re not using a distribution that automatically mounts and unmounts removable media, you have to do it yourself. This section describes the Linux command line commandstohelpyoumanageyourremovablemediadevices. Themountcommand Oddlyenough,thecommandusedtomountmediaiscalled mount.Bydefault,the mount commanddisplaysalistofmediadevicescurrentlymountedonthesystem: $mount /dev/mapper/VolGroup00-LogVol00on/typeext3(rw) procon/proctypeproc(rw) sysfson/systypesysfs(rw) devptson/dev/ptstypedevpts(rw,gid=5,mode=620) /dev/sda1on/boottypeext3(rw) tmpfson/dev/shmtypetmpfs(rw) noneon/proc/sys/fs/binfmt_misctypebinfmt_misc(rw) sunrpcon/var/lib/nfs/rpc_pipefstyperpc_pipefs(rw) /dev/sdb1on/media/disktypevfat (rw,nosuid,nodev,uhelper=hal,shortname=lower,uid=503) $ Themountcommandprovidesfourpiecesofinformation: Thedevicefilenameofthemedia Themountpointinthevirtualdirectorywherethemediaismounted Thefilesystemtype Theaccessstatusofthemountedmedia ThelastentryintheprecedingexampleisaUSBmemorystickthattheGNOMEdesktop automaticallymountedatthe /media/diskmountpoint.Thevfatfilesystemtypeshows thatitwasformattedonaMicrosoftWindowsPC. Tomanuallymountamediadeviceinthevirtualdirectory,youmustbeloggedinasthe rootuserorusethesudocommandtorunthecommandastherootuser.Thefollowingis thebasiccommandformanuallymountingamediadevice: mount-ttypedevicedirectory Thetypeparameterdefinesthefilesystemtypeunderwhichthediskwasformatted.Linux recognizeslotsofdifferentfilesystemtypes.Ifyoushareremovablemediadeviceswith yourWindowsPCs,youaremostlikelytorunintothesetypes: vfat:Windowslongfilesystem ntfs:WindowsadvancedfilesystemusedinWindowsNT,XP,andVista iso9660:ThestandardCD-ROMfilesystem MostUSBmemorysticksandfloppiesareformattedusingthevfatfilesystem.Ifyouneed tomountadataCD,youmustusetheiso9660filesystemtype. Thenexttwoparametersdefinethelocationofthedevicefileforthemediadeviceandthe locationinthevirtualdirectoryforthemountpoint.Forexample,tomanuallymountthe USBmemorystickatdevice /dev/sdb1atlocation /media/disk,youusethefollowing command: mount-tvfat/dev/sdb1/media/disk Afteramediadeviceismountedinthevirtualdirectory,therootuserhasfullaccesstothe device, but access by other users is restricted. You can control who has access to the deviceusingdirectorypermissions(discussedinChapter7). Incaseyouneedtousesomeofthemoreexoticfeaturesofthemountcommand,Table4.5 showstheavailableparameters. Table4.5ThemountCommandParameters Parameter -a -f -F -v -I -l -n Description Mountsallfilesystemsspecifiedinthe/etc/fstabfile Causesthemountcommandtosimulatemountingadevice,butnotactually mountit Mountsallfilesystemsatthesametimewhenusedwiththe-aparameter Explainsallthestepsrequiredtomountthedevice;standsforverbosemode Tellsyounottouseanyfilesystemhelperfilesunder/sbin/mount .filesystem Addsthefilesystemlabelsautomaticallyforext2,ext3,orXFSfilesystems Mountsthedevicewithoutregisteringitinthe/etc/mstabmounteddevice file -pnum -s -r -w -Llabel -Uuuid -O -o Forencryptedmounting,readsthepassphrasefromthefiledescriptornum Ignoresmountoptionsnotsupportedbythefilesystem Mountsthedeviceasread-only Mountsthedeviceasread-write(thedefault) Mountsthedevicewiththespecifiedlabel Mountsthedevicewiththespecifieduuid Whenusedwiththe-aparameter,limitsthesetoffilesystemsapplied Addsspecificoptionstothefilesystem The -o option allows you to mount the filesystem with a comma-separated list of additionaloptions.Thesearepopularoptionstouse: ro:Mountsasread-only rw:Mountsasread-write user:Allowsanordinaryusertomountthefilesystem check=none:Mountsthefilesystemwithoutperforminganintegritycheck loop:Mountsafile Theunmountcommand Toremovearemovablemediadevice,youshouldneverjustremoveitfromthesystem. Instead,youshouldalwaysunmountitfirst. Tip Linuxdoesn’tallowyoutoejectamountedCD.Ifyoueverhavetroubleremovinga CDfromthedrive,mostlikelyitmeanstheCDisstillmountedinthevirtual directory.Unmountitfirst,andthentrytoejectit. Thecommandusedtounmountdevicesis umount (yes, there’s no “n” in the command, whichgetsconfusingsometimes).Theformatfortheumountcommandisprettysimple: umount[directory|device] The umount command gives you the choice of defining the media device by either its devicelocationoritsmounteddirectoryname.Ifanyprogramhasafileopenonadevice, thesystemwon’tletyouunmountit. [root@testboxmnt]#umount/home/rich/mnt umount:/home/rich/mnt:deviceisbusy umount:/home/rich/mnt:deviceisbusy [root@testboxmnt]#cd/home/rich [root@testboxrich]#umount/home/rich/mnt [root@testboxrich]#ls-lmnt total0 [root@testboxrich]# In this example, the command prompt was still in a directory within the filesystem structure,sothe umount command couldn’t unmount the image file. After the command prompt was moved out of the image file filesystem, the umount command successfully unmountedtheimagefile. Usingthedfcommand Sometimes, you need to see how much disk space is available on an individual device. Thedfcommandallowsyoutoeasilyseewhat’shappeningonallthemounteddisks: $df Filesystem1K-blocksUsedAvailableUse%Mountedon /dev/sda2182510687703964960502445%/ /dev/sda1101086186807718720%/boot tmpfs11953601195360%/dev/shm /dev/sdb11274621138921357090%/media/disk $ Thedfcommandshowseachmountedfilesystemthatcontainsdata.Asyoucanseefrom themountcommandearlier,somemounteddevicesareusedforinternalsystempurposes. Thecommanddisplaysthefollowing: Thedevicelocationofthedevice Howmany1024-byteblocksofdataitcanhold Howmany1024-byteblocksareused Howmany1024-byteblocksareavailable Theamountofusedspaceasapercentage Themountpointwherethedeviceismounted A few different command line parameters are available with the df command, most of which you’ll never use. One popular parameter is -h, which shows the disk space in human-readableform,usuallyasanMformegabytesoraGforgigabytes: $df-h FilesystemSizeUsedAvailUse%Mountedon /dev/sdb218G7.4G9.2G45%/ /dev/sda199M19M76M20%/boot tmpfs117M0117M0%/dev/shm /dev/sdb1125M112M14M90%/media/disk $ Nowinsteadofhavingtodecodethoseuglyblocknumbers,allthedisksizesareshown using “normal” sizes. The df command is invaluable in troubleshooting disk space problemsonthesystem. Note RememberthattheLinuxsystemalwayshasprocessesrunninginthebackground thathandlefiles.ThevaluesfromthedfcommandreflectwhattheLinuxsystem thinksarethecurrentvaluesatthatpointintime.It’spossiblethatyouhaveaprocess runningthathascreatedordeletedafilebuthasnotreleasedthefileyet.Thisvalue isnotincludedinthefreespacecalculation. Usingtheducommand Withthe dfcommand,youcaneasilyseewhenadiskisrunningoutofspace.Thenext problemforthesystemadministratoristoknowwhattodowhenthathappens. Anotherusefulcommandtohelpyouistheducommand.Theducommandshowsthedisk usage for a specific directory (by default, the current directory). This is a quick way to determineifyouhaveanyobviousdiskhogsonthesystem. Bydefault,theducommanddisplaysallthefiles,directories,andsubdirectoriesunderthe currentdirectory,anditshowshowmanydiskblockseachfileordirectorytakes.Fora standard-sizeddirectory,thiscanbequitealisting.Here’sapartiallistingofusingthe du command: $du 484./.gstreamer-0.10 8./Templates 8./Download 8./.ccache/7/0 24./.ccache/7 368./.ccache/a/d 384./.ccache/a 424./.ccache 8./Public 8./.gphpedit/plugins 32./.gphpedit 72./.gconfd 128./.nautilus/metafiles 384./.nautilus 72./.bittorrent/data/metainfo 20./.bittorrent/data/resume 144./.bittorrent/data 152./.bittorrent 8./Videos 8./Music 16./.config/gtk-2.0 40./.config 8./Documents Thenumberattheleftofeachlineisthenumberofdiskblocksthateachfileordirectory takes. Notice that the listing starts at the bottom of a directory and works its way up throughthefilesandsubdirectoriescontainedwithinthedirectory. The ducommandbyitselfcanbesomewhatuseless.It’snicetobeabletoseehowmuch diskspaceeachindividualfileanddirectorytakesup,butitcanbemeaninglesswhenyou havetowadethroughpagesandpagesofinformationbeforeyoufindwhatyou’relooking for. Youcanuseafewcommandlineparameterswiththe ducommandtomakethingsalittle morelegible: -c:Producesagrandtotalofallthefileslisted -h:Printssizesinhuman-readableform,usingKforkilobyte,Mformegabyte,and Gforgigabyte -s:Summarizeseachargument The next step for the system administrator is to use some file-handling commands for manipulatinglargeamountsofdata.That’sexactlywhatthenextsectioncovers. WorkingwithDataFiles Whenyouhavealargeamountofdata,handlingtheinformationandmakingitusefulcan bedifficult.Asyousawwiththeducommandintheprevioussection,it’seasytogetdata overloadwhenworkingwithsystemcommands. TheLinuxsystemprovidesseveralcommandlinetoolstohelpyoumanagelargeamounts of data. This section covers the basic commands that every system administrator — as wellasanyeverydayLinuxuser—shouldknowhowtousetomaketheirliveseasier. Sortingdata The sortcommandisapopularfunctionthatcomesinhandywhenworkingwithlarge amountsofdata.Thesortcommanddoeswhatitsays:Itsortsdata. Bydefault,thesortcommandsortsthedatalinesinatextfileusingstandardsortingrules forthelanguageyouspecifyasthedefaultforthesession. $catfile1 one two three four five $sortfile1 five four one three two $ It’sprettysimple,butthingsaren’talwaysaseasyastheyappear.Lookatthisexample: $catfile2 1 2 100 45 3 10 145 75 $sortfile2 1 10 100 145 2 3 45 75 $ Ifyouwereexpectingthenumberstosortinnumericalorder,youweredisappointed.By default, the sort command interprets numbers as characters and performs a standard charactersort,producingoutputthatmightnotbewhatyouwant.Tosolvethisproblem, use the -n parameter, which tells the sort command to recognize numbers as numbers insteadofcharactersandtosortthembasedontheirnumericalvalues: $sort-nfile2 1 2 3 10 45 75 100 145 $ Now, that’s much better! Another common parameter that’s used is -M, the month sort. Linuxlogfilesusuallycontainatimestampatthebeginningofthelinetoindicatewhen theeventoccurred: Sep1307:10:09testboxsmartd[2718]:Device:/dev/sda,opened Ifyousortafilethatusestimestampdatesusingthedefaultsort,yougetsomethinglike this: $sortfile3 Apr Aug Dec Feb Jan Jul Jun Mar May Nov Oct Sep $ It’s not exactly what you wanted. If you use the -M parameter, the sort command recognizesthethree-charactermonthnomenclatureandsortsappropriately: $sort-Mfile3 Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec $ Table4.6showsotherhandysortparametersyoucanuse. Table4.6ThesortCommandParameters Single DoubleDash Dash —ignore-b leading-blanks —check= -C quiet -c -d -f -g -i -k -M -m -n -o -R -r -S -s -T —check —dictionaryorder —ignore-case —generalnumeric-sort —ignorenonprinting —key= POS1[,POS2] —month-sort —merge —numericsort —output= file —random-sort —randomsource=FILE —reverse —buffer-size =SIZE —stable —temporarydirection= Description Ignoresleadingblankswhensorting Doesn’tsort,butdoesn’treportifdataisoutofsortorder Doesn’tsort,butchecksiftheinputdataisalreadysorted,and reportsifnotsorted Considersonlyblanksandalphanumericcharacters;doesn’t considerspecialcharacters Bydefault,sortorderscapitalizedlettersfirst;ignorescase Usesgeneralnumericalvaluetosort Ignoresnonprintablecharactersinthesort SortsbasedonpositionPOS1,andendsatPOS2ifspecified Sortsbymonthorderusingthree-charactermonthnames Mergestwoalreadysorteddatafiles Sortsbystringnumericalvalue Writesresultstofilespecified Sortsbyarandomhashofkeys Specifiesthefileforrandombytesusedbythe-Rparameter Reversesthesortorder(descendinginsteadofascending Specifiestheamountofmemorytouse Disableslast-resortcomparison Specifiesalocationtostoretemporaryworkingfiles DIR -t —fieldseparator= SEP -u —unique Specifiesthecharacterusedtodistinguishkeypositions Withthe-cparameter,checksforstrictordering;withoutthe-c parameter,outputsonlythefirstoccurrenceoftwosimilarlines -z —zeroterminated EndsalllineswithaNULLcharacterinsteadofanewline The -k and -t parameters are handy when sorting data that uses fields, such as the /etc/passwdfile.Usethe-tparametertospecifythefieldseparatorcharacter,andusethe -kparametertospecifywhichfieldtosorton.Forexample,tosortthepasswordfilebased onnumericaluserid,justdothis: $sort-t‘:’-k3-n/etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin news:x:9:13:news:/etc/news: uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin gopher:x:13:30:gopher:/var/gopher:/sbin/nologin ftp:x:14:50:FTPUser:/var/ftp:/sbin/nologin Now the data is perfectly sorted based on the third field, which is the numerical userid value. The -n parameter is great for sorting numerical outputs, such as the output of the du command: $du-sh*|sort-nr 1008kmrtg-2.9.29.tar.gz 972kbldg1 888kfbs2.pdf 760kPrinttest 680krsync-2.6.6.tar.gz 660kcode 516kfig1001.tiff 496ktest 496kphp-common-4.0.4pl1-6mdk.i586.rpm 448kMesaGLUT-6.5.1.tar.gz 400kplp Noticethatthe -roptionalsosortsthevaluesindescendingorder,soyoucaneasilysee whatfilesaretakingupthemostspaceinyourdirectory. Note Thepipecommand(|)usedinthisexampleredirectstheoutputoftheducommandto thesortcommand.That’sdiscussedinmoredetailinChapter11. Searchingfordata Often in a large file, you must look for a specific line of data buried somewhere in the middle of the file. Instead of manually scrolling through the entire file, you can let the grepcommandsearchforyou.Thecommandlineformatforthegrepcommandis: grep[options]pattern[file] The grepcommandsearcheseithertheinputorthefileyouspecifyforlinesthatcontain charactersthatmatchthespecifiedpattern.Theoutputfrom grepisthelinesthatcontain thematchingpattern. Herearetwosimpleexamplesofusingthe grepcommandwiththe file1fileusedinthe “Sortingdata”section: $grepthreefile1 three $greptfile1 two three $ The first example searches the file file1 for text matching the pattern three. The grep commandproducesthelinethatcontainsthematchingpattern.Thenextexamplesearches the file file1 for the text matching the pattern t. In this case, two lines matched the specifiedpattern,andbotharedisplayed. Because of the popularity of the grep command, it has undergone lots of development changesoveritslifetime.Lotsoffeatureshavebeenaddedtothe grepcommand.Ifyou lookoverthemanpagesforthegrepcommand,you’llseehowversatileitreallyis. If you want to reverse the search (output lines that don’t match the pattern), use the -v parameter: $grep-vtfile1 one four five $ If you need to find the line numbers where the matching patterns are found, use the -n parameter: $grep-ntfile1 2:two 3:three $ Ifyoujustneedtoseeacountofhowmanylinescontainthematchingpattern,usethe -c parameter: $grep-ctfile1 2 $ If you need to specify more than one matching pattern, use the -e parameter to specify eachindividualpattern: $grep-et-effile1 two three four five $ Thisexampleoutputslinesthatcontaineitherthestringtorthestringf. By default, the grep command uses basic Unix-style regular expressions to match patterns.AUnix-styleregularexpressionusesspecialcharacterstodefinehowtolookfor matchingpatterns. Foramoredetailedexplanationofregularexpressions,seeChapter20. Here’sasimpleexampleofusingaregularexpressioninagrepsearch: $grep[tf]file1 two three four five $ Thesquarebracketsintheregularexpressionindicatethat grepshouldlookformatches thatcontaineitheratoranfcharacter.Withouttheregularexpression, grepwouldsearch fortextthatwouldmatchthestringtf. Theegrepcommandisanoffshootofgrep,whichallowsyoutospecifyPOSIXextended regular expressions, which contain more characters for specifying the matching pattern (again, see Chapter 20 for more details). The fgrep command is another version that allows you to specify matching patterns as a list of fixed-string values, separated by newlinecharacters.Thisallowsyoutoplacealistofstringsinafileandthenusethatlist inthefgrepcommandtosearchforthestringsinalargerfile. Compressingdata Ifyou’vedoneanyworkintheMicrosoftWindowsworld,nodoubtyou’veusedzipfiles. It became such a popular feature that Microsoft eventually incorporated it into the WindowsoperatingsystemstartingwithXP.Theziputilityallowsyoutoeasilycompress largefiles(bothtextandexecutable)intosmallerfilesthattakeuplessspace. Linuxcontainsseveralfilecompressionutilities.Althoughthismaysoundgreat,itoften leads to confusion and chaos when trying to download files. Table 4.7 lists the file compressionutilitiesavailableforLinux. Table4.7LinuxFileCompressionUtilities Utility File Extension bzip2 .bz2 compress .Z gzip .gz zip .zip Description UsestheBurrows-Wheelerblocksortingtextcompression algorithmandHuffmancoding OriginalUnixfilecompressionutility;startingtofadeawayinto obscurity TheGNUProject’scompressionutility;usesLempel-Zivcoding TheUnixversionofthePKZIPprogramforWindows The compress file compression utility is not often found on Linux systems. If you downloadafilewitha .Zextension,youcanusuallyinstallthecompresspackage(called ncompressinmanyLinuxdistributions)usingthesoftwareinstallationmethodsdiscussed inChapter9andthenuncompressthefilewiththeuncompresscommand.Thegziputility isthemostpopularcompressiontoolusedinLinux. ThegzippackageisacreationoftheGNUProject,intheirattempttocreateafreeversion oftheoriginalUnixcompressutility.Thispackageincludesthesefiles: gzipforcompressingfiles gzcatfordisplayingthecontentsofcompressedtextfiles gunzipforuncompressingfiles Theseutilitiesworkthesamewayasthebzip2utilities: $gzipmyprog $ls-lmy* -rwxrwxr-x1richrich21972007-09-1311:29myprog.gz $ The gzipcommandcompressesthefileyouspecifyonthecommandline.Youcanalso specifymorethanonefilenameorevenusewildcardcharacterstocompressmultiplefiles atonce: $gzipmy* $ls-lmy* -rwxr—r—1richrich103Sep613:43myprog.c.gz -rwxr-xr-x1richrich5178Sep613:43myprog.gz -rwxr—r—1richrich59Sep613:46myscript.gz -rwxr—r—1richrich60Sep613:44myscript2.gz $ The gzip command compresses every file in the directory that matches the wildcard pattern. Archivingdata Althoughthe zipcommandworksgreatforcompressingandarchivingdataintoasingle file, it’s not the standard utility used in the Unix and Linux worlds. By far the most populararchivingtoolusedinUnixandLinuxisthetarcommand. The tar command was originally used to write files to a tape device for archiving. However,itcanalsowritetheoutputtoafile,whichhasbecomeapopularwaytoarchive datainLinux. Thefollowingistheformatofthetarcommand: tarfunction[options]object1object2… Thefunctionparameterdefineswhatthetarcommandshoulddo,asshowninTable4.8. Table4.8ThetarCommandFunctions Function LongName -A -c -d -r -t -u -x Description — Appendsanexistingtararchivefiletoanotherexistingtar concatenate archivefile —create Createsanewtararchivefile Checksthedifferencesbetweenatararchivefileandthe —diff filesystem —delete Deletesfromanexistingtararchivefile —append Appendsfilestotheendofanexistingtararchivefile —list Liststhecontentsofanexistingtararchivefile Appendsfilestoanexistingtararchivefilethatarenewerthana —update filewiththesamenameintheexistingarchive —extract Extractsfilesfromanexistingarchivefile Eachfunctionusesoptionstodefineaspecificbehaviorforthetararchivefile.Table4.9 liststhecommonoptionsthatyoucanusewiththetarcommand. Table4.9ThetarCommandOptions Option -Cdir -ffile -j -p -v -z Description Changestothespecifieddirectory Outputsresultstofile(ordevice)file Redirectsoutputtothebzip2commandforcompression Preservesallfilepermissions Listsfilesastheyareprocessed Redirectstheoutputtothegzipcommandforcompression Theseoptionsareusuallycombinedtocreatethefollowingscenarios.First,youwantto createanarchivefileusingthiscommand: tar-cvftest.tartest/test2/ The above command creates an archive file called test.tar containing the contents of boththetestdirectoryandthetest2directory.Next,thiscommand: tar-tftest.tar lists(butdoesn’textract)thecontentsofthetarfiletest.tar.Finally,thiscommand: tar-xvftest.tar extractsthecontentsofthetarfile test.tar.Ifthetarfilewascreatedfromadirectory structure,theentiredirectorystructureisre-createdstartingatthecurrentdirectory. Asyoucansee,usingthe tarcommandisasimplewaytocreatearchivefilesofentire directorystructures.Thisisacommonmethodfordistributingsourcecodefilesforopen sourceapplicationsintheLinuxworld. Tip Ifyoudownloadopensourcesoftware,oftenyouseefilenamesthatendin.tgz. Thesearegzippedtarfiles,whichcanbeextractedusingthecommandtar-zxvf filename.tgz. Summary ThischapterdiscussedsomeofthemoreadvancedbashcommandsusedbyLinuxsystem administratorsandprogrammers.The psand topcommandsarevitalindeterminingthe status of the system, allowing you to see what applications are running and how many resourcestheyareconsuming. In this day of removable media, another popular topic for system administrators is mounting storage devices. The mount command allows you to mount a physical storage device into the Linux virtual directory structure. To remove the device, use the umount command. Finally, the chapter discussed various utilities used for handling data. The sort utility easilysortslargedatafilestohelpyouorganizedata,andthe greputilityallowsyouto quickly scan through large data files looking for specific information. Several file compressionutilitiesareavailableinLinux,including gzipandzip.Eachoneallowsyou tocompresslargefilestohelpsavespaceonyourfilesystem.TheLinux tarutilityisa popularwaytoarchivedirectorystructuresintoasinglefilethatcaneasilybeportedto anothersystem. ThenextchapterdiscussesLinuxshellsandhowtointeractwiththem.Linuxallowsyou tocommunicatebetweenshells,whichcancomeinhandywhencreatingsubshellsinyour scripts. Chapter5 UnderstandingtheShell InThisChapter 1. InvestigatingShellTypes 2. UnderstandingtheParent/ChildShellRelationship 3. UsingSubshellsCreatively 4. InvestigatingBuilt-inShellCommands Nowthatyouknowafewshellbasics,suchasreachingtheshellandrudimentary shellcommands,itistimetoexploretheactualshellprocess.Tounderstandtheshell, youneedtounderstandafewCLIbasics. AshellisnotjustaCLI.Itisacomplicatedinteractiverunningprogram.Entering commandsandusingtheshelltorunscriptscanraisesomeinterestingandconfusing issues.Understandingtheshellprocessanditsrelationshipshelpsyouresolvethese issuesoravoidthemaltogether. Thischaptertakesyouthroughlearningabouttheshellprocess.Youseehow subshellsarecreatedandtheirrelationshiptotheparentshell.Thevariedcommands thatcreatechildprocessesareexploredaswellasbuilt-incommands.Youevenread aboutsomeshelltipsandtrickstotry. ExploringShellTypes The shell program that the system starts depends on your user ID configuration. In the /etc/passwdfile,theuserIDhasitsdefaultshellprogramlistedinfield#7ofitsrecord. Thedefaultshellprogramisstartedwhenevertheuserlogsintoavirtualconsoleterminal orstartsaterminalemulatorintheGUI. In the following example, user christine has the GNU bash shell as her default shell program: $cat/etc/passwd […] Christine:x:501:501:ChristineB:/home/Christine:/bin/bash $ Thebashshellprogramresidesinthe/bindirectory.Alonglistingreveals/bin/bash(the bashshell)isanexecutableprogram: $ls-lF/bin/bash -rwxr-xr-x.1rootroot938832Jul182013/bin/bash* $ Several other shell programs are on this particular CentOS distribution. They include tcsh,whichisbasedofftheoriginalCshell: $ls-lF/bin/tcsh -rwxr-xr-x.1rootroot387328Feb212013/bin/tcsh* $ Also,theDebianbasedversionoftheashshell,dash,isincluded: $ls-lF/bin/dash -rwxr-xr-x.1rootroot109672Oct172012/bin/dash* $ Finally,asoftlink(seeChapter3)oftheCshellpointstothetcshshell: $ls-lF/bin/csh lrwxrwxrwx.1rootroot4Mar1815:16/bin/csh->tcsh* $ Eachofthesedifferentshellprogramscouldbesetasauser’sdefaultshell.However,due tothebashshell’spopularity,it’sraretouseanyothershellasadefaultshell. Note AbriefdescriptionofvariousshellswasincludedinChapter1.Youmaybe interestedinlearningevenmoreaboutshellsotherthantheGNUbashshell. AdditionalalternativeshellinformationisinChapter23. Thedefaultinteractiveshellstartswheneverauserlogsintoavirtualconsoleterminalor starts a terminal emulator in the GUI. However, another default shell, /bin/sh, is the defaultsystemshell.Thedefaultsystemshellisusedforsystemshellscripts,suchasthose neededatstartup. Often,youseeadistributionwithitsdefaultsystemshellsettothebashshellusingasoft linkasshownhereonthisCentOSdistribution: $ls-l/bin/sh lrwxrwxrwx.1rootroot4Mar1815:05/bin/sh->bash $ However,beawarethatonsomedistributions,thedefaultsystemshellisdifferentthanthe defaultinteractiveshell,suchasonthisUbuntudistribution: $cat/etc/passwd […] christine:x:1000:1000:Christine,,,:/home/christine:/bin/bash $ $ls-l/bin/sh lrwxrwxrwx1rootroot4Apr2212:33/bin/sh->dash $ Notethattheuser, christine,hasherdefaultinteractiveshellsetto /bin/bash,thebash shell.Butthedefaultsystemshell,/bin/sh,issettothedashshell. Tip Forbashshellscripts,thesetwodifferentshells,defaultinteractiveshellanddefault systemshell,cancauseproblems.Besuretoreadabouttheimportantsyntaxneeded forabashshellscript’sfirstlineinChapter11toavoidtheseissues. You are not forced to stick with your default interactive shell. You can start any shell availableonyourdistribution,simplybytypingitsfilename.Forexample,tostartthedash shell,youcanrunitdirectlybytypingthecommand/bin/dash: $/bin/dash $ Itdoesn’tlooklikeanythinghappened,butthedashshellprogramstarted.The$promptis a CLI prompt for the dash shell. You can leave the dash shell program by typing the commandexit: $exit exit $ Again, it looks like nothing happened. However, the dash shell program was exited. To understand this process, the next section explores the relationship between a login shell programandanewlystartedshellprogram. ExploringParentandChildShellRelationships The default interactive shell started when a user logs into a virtual console terminal or starts a terminal emulator in the GUI is a parentshell. As you have read so far in this book,aparentshellprocessprovidesaCLIpromptandwaitsforcommandstobeentered. When the /bin/bash command or the equivalent bash command is entered at the CLI prompt,anewshellprogramiscreated.Thisisachildshell.AchildshellalsohasaCLI promptandwaitsforcommandstobeentered. Because you do not see any relevant messages when you type bash and spawn a child shell,anothercommandcanhelpbringclarity.The pscommandwascoveredinChapter 4.Usingthiswiththe-foptionbeforeandafterenteringachildshellisuseful: $ps-f UIDPIDPPIDCSTIMETTYTIMECMD 50118411840011:50pts/000:00:00-bash 50124291841413:44pts/000:00:00ps-f $ $bash $ $ps-f UIDPIDPPIDCSTIMETTYTIMECMD 50118411840011:50pts/000:00:00-bash 50124301841013:44pts/000:00:00bash 50124442430113:44pts/000:00:00ps-f $ The first use of ps -f shows two processes. One process has a process ID of 1841 (secondcolumn)andisrunningthebashshellprogram(lastcolumn).Thesecondprocess (processID2429)istheactualps-fcommandrunning. Note Aprocessisarunningprogram.Thebashshellisaprogram,andwhenitruns,itisa process.Arunningshellissimplyonetypeofprocess.Therefore,whenreading aboutrunningabashshell,youoftenseetheword“shell”andtheword“process” usedinterchangeably Afterthecommandbashisentered,achildshelliscreated.Thesecond ps-fisexecuted fromwithinthechildshell.Fromthisdisplay,youcanseethattwobashshellprograms arerunning.Thefirstbashshellprogram,theparentshellprocess,hastheoriginalprocess ID (PID) of 1841. The second bash shell program, the child shell process, has a PID of 2430.NotethatthechildshellhasaparentprocessID(PPID)of1841,denotingthatthe parentshellprocessisitsparent.Figure5.1diagramsthisrelationship. Figure5.1Parentandchildbashshellprocesses Whenachildshellprocessisspawned,onlysomeoftheparent’senvironmentiscopiedto thechildshellenvironment.Thiscancauseproblemswithitemssuchasvariables,andit iscoveredinChapter6. Achildshellisalsocalledasubshell.Asubshellcanbecreatedfromaparentshell,anda subshellcanbecreatedfromanothersubshell: $ps-f UIDPIDPPIDCSTIMETTYTIMECMD 50118411840011:50pts/000:00:00-bash 50125321841114:22pts/000:00:00ps-f $ $bash $ $bash $ $bash $ $ps—forest PIDTTYTIMECMD 1841pts/000:00:00bash 2533pts/000:00:00\_bash 2546pts/000:00:00\_bash 2562pts/000:00:00\_bash 2576pts/000:00:00\_ps $ Intheprecedingexample,the bash shellcommandwasenteredthreetimes.Effectively, this created three subshells. The ps —forest command shows the nesting of these subshells.Figure5.2alsoshowsthissubshellnesting. Figure5.2Subshellnesting The ps-fcommandcanbeusefulinsubshellnesting,becauseitdisplayswhoiswhose parentviathePPIDcolumn: $ps-f UIDPIDPPIDCSTIMETTYTIMECMD 50118411840011:50pts/000:00:00-bash 50125331841014:22pts/000:00:00bash 50125462533014:22pts/000:00:00bash 50125622546014:24pts/000:00:00bash 50125852562114:29pts/000:00:00ps-f $ Thebashshellprogramcanusecommandlineparameterstomodifytheshellstart.Table 5.1liststhecommandlineparametersavailableinbash. Table5.1ThebashCommandLineParameters Parameter Description -cstring Readscommandsfromstringandprocessesthem -i Startsaninteractiveshell,allowinginputfromtheuser -l Actsasifinvokedasaloginshell -r Startsarestrictedshell,limitingtheusertothedefaultdirectory -s Readscommandsfromthestandardinput Youcanfindmorehelponthe bashcommandandevenmorecommandlineparameters bytypingmanbash.Thebash—helpcommandprovidesadditionalassistanceaswell. Youcangracefullyexitoutofeachsubshellbyenteringtheexitcommand: $exit exit $ $ps—forest PIDTTYTIMECMD 1841pts/000:00:00bash 2533pts/000:00:00\_bash 2546pts/000:00:00\_bash 2602pts/000:00:00\_ps $ $exit exit $ $exit exit $ $ps—forest PIDTTYTIMECMD 1841pts/000:00:00bash 2604pts/000:00:00\_ps $ Notonlydoestheexitcommandallowyoutoleavechildsubshells,butyoucanalsolog out of your current virtual console terminal or terminal emulation software as well. Just typeexitintheparentshell,andyougracefullyexittheCLI. Another time a subshell can be created is when you run a shell script. You learn more aboutthattopicinChapter11. Also,youcanspawnsubshellswithoutusingthe bashshellcommandorrunningashell script.Onewayisbyusingaprocesslist. Lookingatprocesslists Onasingleline,youcandesignatealistofcommandstoberunoneafteranother.Thisis donebyenteringacommandlistusingasemicolon(;)betweencommands: $pwd;ls;cd/etc;pwd;cd;pwd;ls /home/Christine DesktopDownloadsMusicPublicVideos Documentsjunk.datPicturesTemplates /etc /home/Christine DesktopDownloadsMusicPublicVideos Documentsjunk.datPicturesTemplates $ Intheprecedingexample,thecommandsallexecutedoneafteranotherwithnoproblems. However,thisisnotaprocesslist.Foracommandlisttobeconsideredaprocesslist,the commandsmustbeencasedinparentheses: $(pwd;ls;cd/etc;pwd;cd;pwd;ls) /home/Christine DesktopDownloadsMusicPublicVideos Documentsjunk.datPicturesTemplates /etc /home/Christine DesktopDownloadsMusicPublicVideos Documentsjunk.datPicturesTemplates $ Though the parentheses addition may not appear to be a big difference, they do cause a verydifferenteffect.Addingparenthesesandturningthecommandlistintoaprocesslist createdasubshelltoexecutethecommands. Note Aprocesslistisacommandgroupingtype.Anothercommandgroupingtypeputsthe commandsbetweencurlybracketsandendsthecommandlistwithasemicolon(;). Thesyntaxisasfollows:{command;}.Usingcurlybracketsforcommandgrouping doesnotcreateasubshellasaprocesslistdoes. To indicate if a subshell was spawned, a command using an environment variable is needed here. (Environment variables are covered in detail in Chapter 6). The command neededis echo$BASH_SUBSHELL.Ifitreturnsa0,thenthereisnosubshell.Ifitreturns1 ormore,thenthereisasubshell. First,theexampleusingjustacommandlistisexecutedwiththe echo $BASH_SUBSHELL tackedontotheend: $pwd;ls;cd/etc;pwd;cd;pwd;ls;echo$BASH_SUBSHELL /home/Christine DesktopDownloadsMusicPublicVideos Documentsjunk.datPicturesTemplates /etc /home/Christine DesktopDownloadsMusicPublicVideos Documentsjunk.datPicturesTemplates 0 Attheveryendofthecommands’output,youcanseethenumberzero(0)isdisplayed. Thisindicatesasubshellwasnotcreatedtoexecutethesecommands. The results are different using a process list. The list is executed with echo $BASH_SUBSHELLtackedontotheend: $(pwd;ls;cd/etc;pwd;cd;pwd;ls;echo$BASH_SUBSHELL) /home/Christine DesktopDownloadsMusicPublicVideos Documentsjunk.datPicturesTemplates /etc /home/Christine DesktopDownloadsMusicPublicVideos Documentsjunk.datPicturesTemplates 1 In this case, the number one (1) displayed at the output’s end. This indicates a subshell wasindeedcreatedandusedforexecutingthesecommands. Thus, a processlist is a command grouping enclosed with parentheses, which creates a subshell to execute the command(s). You can even create a grandchild subshell by embeddingparentheseswithinaprocesslist: $(pwd;echo$BASH_SUBSHELL) /home/Christine 1 $(pwd;(echo$BASH_SUBSHELL)) /home/Christine 2 Noticeinthefirstprocesslist,thenumberone(1)isdisplayedindicatingachildsubshell asyouwouldexpect.Howeverintheexample’ssecondprocesslist,additionalparentheses were added around the echo $BASH_SUBSHELL command. These additional parentheses causedagrandchildsubshelltobecreatedforthecommand’sexecution.Thus,anumber two(2)wasdisplayedindicatingasubshellwithinasubshell. Subshells are often used for multi-processing in shell scripts. However, entering into a subshell is an expensive method and can significantly slow down processing. Subshell issues exist also for an interactive CLI shell session. It is not truly multi-processing, becausetheterminalgetstiedupwiththesubshell’sI/O. Creativelyusingsubshells At the interactive shell CLI, you have more productive ways to use subshells. Process lists,co-processes,andpipes(coveredinChapter11)usesubshells.Theyallcanbeused effectivelywithintheinteractiveshell. One productive subshell method in the interactive shell uses background mode. Before discussing how to use background mode and subshells together, you need to understand backgroundmodeitself. Investigatingbackgroundmode Runningacommandinbackgroundmodeallowsthecommandtobeprocessedandfrees up your CLI for other use. A classic command to demonstrate background mode is the sleepcommand. The sleepcommandacceptsasaparameterthenumberofsecondsyouwanttheprocess to wait (sleep). This command is often used to introduce pauses in shell scripts. The commandsleep10causesthesessiontopausefor10secondsandthenreturnashellCLI prompt: $sleep10 $ Toputacommandintobackgroundmode,the &characteristackedontoitsend.Putting the sleep command into background mode allows a little investigation with the ps command: $sleep3000& [1]2396 $ps-f UIDPIDPPIDCSTIMETTYTIMECMD christi+23382337010:13pts/900:00:00-bash christi+23962338010:17pts/900:00:00sleep3000 christi+23972338010:17pts/900:00:00ps-f $ The sleepcommandwastoldtosleepfor 3000seconds(50minutes)inthebackground (&).Whenitwasputintothebackground,twoinformationalitemsweredisplayedbefore the shell CLI prompt was returned. The first informational item is the background job’s number (1) displayed in brackets. The second item is the background job’s process ID (2396). The ps command was used to display the various processes. Notice that the sleep 3000 commandislisted.AlsonotethatitsprocessID(PID)inthesecondcolumnisthesame PIDdisplayedwhenthecommandwentintothebackground,2396. Inadditiontothepscommand,youcanusethejobscommandtodisplaybackgroundjob information.Thejobscommanddisplaysanyuser’sprocesses(jobs)currentlyrunningin backgroundmode: $jobs [1]+Runningsleep3000& $ Thejobscommandshowsthejobnumber(1)inbrackets.Italsodisplaysthejob’scurrent status(running)aswellasthecommanditself,(sleep3000&). Youcanseeevenmoreinformationbyusingthe -l(lowercaseL)parameteronthe jobs command. The -l parameter displays the command’s PID in addition to the other information: $jobs-l [1]+2396Runningsleep3000& $ Whenthebackgroundjobisfinished,itscompletionstatusisdisplayed: [1]+Donesleep3000& $ Tip Beawarethatabackgroundjob’scompletionstatuswon’tnecessarilywaittilla convenienttimetodisplayitself.Don’tletitsurpriseyouwhenajob’scompletion statusjustsuddenlyappearsonyourscreen. Backgroundmodeisveryhandy.Anditprovidesamethodforcreatingusefulsubshellsat theCLI. Puttingprocesslistsintothebackground As stated earlier, a process list is a command or series of commands executed within a subshell. Using a process list including sleep commands and displaying the BASH_SUBSHELLvariableoperatesasyouwouldexpect: $(sleep2;echo$BASH_SUBSHELL;sleep2) 1 $ In the preceding example, a two-second pause occurs, the number one (1) is displayed indicating a single subshell level (child subshell), and then another two-second pause occursbeforethepromptreturns.Nothingtoodramatichere. Putting the same process list into background mode can cause a slightly different effect withcommandoutput: $(sleep2;echo$BASH_SUBSHELL;sleep2)& [2]2401 $1 [2]+Done(sleep2;echo$BASH_SUBSHELL;sleep2) $ PuttingtheprocesslistintothebackgroundcausesajobnumberandprocessIDtoappear, and the prompt returns. However, the odd event is that the displayed number one (1), indicatingasingle-levelsubshell,isdisplayedbytheprompt!Don’tletthisconfuseyou. SimplypresstheEnterkey,andyougetanotherpromptback. Usingaprocesslistinbackgroundmodeisonecreativemethodforusingsubshellsatthe CLI.Youcandolargeamountsofprocessingwithinasubshellandnothaveyourterminal tiedupwiththesubshell’sI/O. Of course, the process list of sleepand echo commands are just for example purposes. Creating backup files with tar (see Chapter 4) is a more practical example of using backgroundprocesslistseffectively: $(tar-cfRich.tar/home/rich;tar-cfMy.tar/home/christine)& [3]2423 $ Puttingaprocesslistinbackgroundmodeisnottheonlywaytousesubshellscreativelyat theCLI.Co-processingisanothermethod. Lookingatco-processing Co-processingdoestwothingsatthesametime.Itspawnsasubshellinbackgroundmode andexecutesacommandwithinthatsubshell. To perform co-processing, the coproc command is used along with the command to be executedinthesubshell: $coprocsleep10 [1]2544 $ Co-processing performs almost identically to putting a command in background mode, exceptforthefactthatitcreatesasubshell.You’llnoticethatwhenthe coproccommand and its parameters were entered, a background job was started. The background job number(1)andprocessID(2544)weredisplayedonthescreen. Thejobscommandallowsyoutodisplaytheco-processingstatus: $jobs [1]+RunningcoprocCOPROCsleep10& $ From the preceding example, you can see the background command executing in the subshellis coprocCOPROCsleep10.The COPROCisanamegiventotheprocessbythe coproc command. You can set the name yourself by using extended syntax for the command: $coprocMy_Job{sleep10;} [1]2570 $ $jobs [1]+RunningcoprocMy_Job{sleep10;}& $ Byusingtheextendedsyntax,theco-processingnamewassetto My_Job.Becarefulhere, becausetheextendedsyntaxisalittletricky.Youhavetomakesurethataspaceappears afterthefirstcurlybracket({)andbeforethestartofyourcommand.Also,youhaveto makesurethecommandendswithasemicolon(;).Andyouhavetoensurethataspace appearsafterthesemicolonandbeforetheclosingcurlybracket(}). Note Co-processingallowsyoutogetveryfancyandsend/receiveinformationtothe processrunninginthesubshell.Theonlytimeyouneedtonameaco-processis whenyouhavemultipleco-processesrunning,andyouneedtocommunicatewith themall.Otherwise,justletthecoproccommandsetthenametothedefault,COPROC. You can be really clever and combine co-processing with process lists creating nested subshells.Justtypeyourprocesslistandputthecommandcoprocinfrontofit: $coproc(sleep10;sleep2) [1]2574 $ $jobs [1]+RunningcoprocCOPROC(sleep10;sleep2)& $ $ps—forest PIDTTYTIMECMD 2483pts/1200:00:00bash 2574pts/1200:00:00\_bash 2575pts/1200:00:00|\_sleep 2576pts/1200:00:00\_ps $ Just remember that spawning a subshell can be expensive and slow. Creating nested subshellsisevenmoreso! Using subshells can provide flexibility at the command line as well as convenience. Understanding their behavior is important to obtaining this flexibility and convenience. Command behavior is also important to understand. In the next section, the behavior differencesbetweenbuilt-inandexternalcommandsareexplored. UnderstandingShellBuilt-InCommands While learning about the GNU bash shell, you likely have heard the term built-in command. It is important to understand both shell built-in and non-built-in (external) commands.Built-incommandsandnon-built-incommandsoperateverydifferently. Lookingatexternalcommands Anexternalcommand,sometimescalledafilesystemcommand,isaprogramthatexists outsideofthebashshell.Theyarenotbuiltintotheshellprogram.Anexternalcommand programistypicallylocatedin/bin,/usr/bin,/sbin,or/usr/sbin. The ps command is an external command. You can find its filename by using both the whichandthetypecommands: $whichps /bin/ps $ $type-aps psis/bin/ps $ $ls-l/bin/ps -rwxr-xr-x1rootroot93232Jan618:32/bin/ps $ Whenever an external command is executed, a child process is created. This action is termedforking.Conveniently,theexternalcommand psdisplaysitscurrentparentaswell asitsownforkedchildprocesses: $ps-f UIDPIDPPIDCSTIMETTYTIMECMD christi+27432742017:09pts/900:00:00-bash christi+28012743017:16pts/900:00:00ps-f $ Because it is an external command, when the ps command executes, a child process is created.Inthiscase,the pscommand’sPIDis 2801andtheparentPIDis 2743.Thebash shellprocess,whichistheparent,hasaPIDof2743.Figure5.3illustratestheforkingthat occurswhenanexternalcommandisexecuted. Figure5.3Externalcommandforking Wheneveraprocessmustfork,ittakestimeandefforttosetupthenewchildprocess’s environment.Thus,externalcommandscanbealittleexpensive. Note Ifyouforkachildprocessorcreateasubshell,youcanstillcommunicatewithitvia signaling,whichisextremelyhelpfulinboththecommandlineandinwritingshell scripts.Signalingallowsprocesscommunicationviasignals.Signalsandsignaling arecoveredinChapter16. Whenusingabuilt-incommand,noforkingisrequired.Therefore,built-incommandsare lessexpensive. Lookingatbuilt-incommands Built-incommandsaredifferentinthattheydonotneedachildprocesstoexecute.They werecompiledintotheshellandthusarepartoftheshell’stoolkit.Noexternalprogram fileexiststorunthem. Boththe cdand exitcommandsarebuiltintothebashshell.Youcantellacommandis built-inbyusingthetypecommand: $typecd cdisashellbuiltin $ $typeexit exitisashellbuiltin $ Becausetheydonotneedtoforkachildprocesstoexecuteoropenaprogramfile,built-in commands are faster and more efficient. A list of GNU bash shell built-in commands is providedinAppendixA. Be aware that some commands have multiple flavors. For example, both echo and pwd haveabuilt-incommandflavoraswellasanexternalcommandflavor.Theseflavorsare slightly different. To see multiple flavors for commands, use the -a option on the type command: $type-aecho echoisashellbuiltin echois/bin/echo $ $whichecho /bin/echo $ $type-apwd pwdisashellbuiltin pwdis/bin/pwd $ $whichpwd /bin/pwd $ Usingthe type-acommandshowsbothtypesforeachofthetwocommands.Notethat thewhichcommandshowsonlytheexternalcommandfile. Tip Tousetheexternalcommandforacommandthathasmultipleflavors,directly referencethefile.Forexample,tousethepwdexternalcommand,type/bin/pwd. Usingthehistorycommand A useful built-in command is the history command. The bash shell keeps track of the commandsyouhaveused.Youcanrecallthesecommandsandevenreusethem. Toseearecentlyusedcommandslist,justtypethehistorycommandwithnooptions: $history 1ps-f 2pwd 3ls 4coproc(sleep10;sleep2) 5jobs 6ps—forest 7ls 8ps-f 9pwd 10ls-l/bin/ps 11history 12cd/etc 13pwd 14ls 15cd 16typepwd 17whichpwd 18typeecho 19whichecho 20type-apwd 21type-aecho 22pwd 23history Inthisexample,onlythelast23commandsareshown.Typically,thelast1,000commands arekeptinhistory.Thatislotsofcommands! Tip Youcansetthenumberofcommandstokeepinthebashhistory.Todoso,youneed tomodifyanenvironmentvariablecalledHISTSIZE(seeChapter6). You can recall and reuse the last command in your history list. This can save time and typing.Torecallandreuseyourlastcommand,type!!andpresstheEnterkey: $ps—forest PIDTTYTIMECMD 2089pts/000:00:00bash 2744pts/000:00:00\_ps $ $!! ps—forest PIDTTYTIMECMD 2089pts/000:00:00bash 2745pts/000:00:00\_ps $ When!!wasentered,thebashshellfirstdisplayedthecommanditwasrecallingfromthe shell’shistory.Afterthecommandwasdisplayed,itwasexecuted. Commandhistoryiskeptinthehidden .bash_historyfile,whichislocatedintheuser’s homedirectory.Becarefulhere.Thebashcommandhistoryisstoredinmemoryandthen writtenoutintothehistoryfilewhentheshellisexited: $history […] 25ps—forest 26history 27ps—forest 28history $ $cat.bash_history pwd ls history exit $ Noticewhenthe historycommandisrun,28commandsarelisted.Intheexample,the listing is snipped for brevity. However, when the .bash_history file is displayed, only fourcommandsarelisted,andtheydon’tmatchthehistorycommand’slist. Youcanforcethecommandhistorytobewrittentothe.bash_historyfilebeforeleaving ashellsession.Inordertoforcethiswrite,usethe-aoptiononthehistorycommand: $history-a $ $history […] 25ps—forest 26history 27ps—forest 28history 29ls-a 30cat.bash_history 31history-a 32history $ $cat.bash_history […] ps—forest history ps—forest history ls-a cat.bash_history history-a Thistimebothlistingsneedtobesnippedbecausetheyaresolong.Noticethatcontents fromboththe historycommandandthe .bash_historyfilematch,exceptforthevery last command listed for the history command, because it came after the history -a commandwasissued. Note Ifyouhavemultipleterminalsessionsopen,youcanstillappendthe.bash_history ineachopensessionusingthehistory-acommand.However,thehistoriesarenot automaticallyupdatedforyourotheropenterminalsessions.Thisisbecausethe .bash_historyfileisreadonlywhenaterminalsessionisfirststarted.Toforcethe .bash_historyfiletoberereadandaterminalsession’shistorytobeupdated,use thehistory-ncommand. Youcanrecallanycommandfromthehistorylist.Justenteranexclamationpointandthe command’snumberfromthehistorylist: $history […] 13pwd 14ls 15cd 16typepwd 17whichpwd 18typeecho 19whichecho 20type-apwd 21type-aecho […] 32history-a 33history 34cat.bash_history 35history $ $!20 type-apwd pwdisashellbuiltin pwdis/bin/pwd $ Commandnumber20waspulledfromcommandhistory.Noticethatsimilartoexecuting thelastcommandinhistory,thebashshellfirstdisplaysthecommanditisrecallingfrom theshell’shistory.Afterthecommandisdisplayed,itisexecuted. Usingbashshellcommandhistorycanbeagreattimesaver.Youcandoevenmorewith thebuilt-in history command. Be sure to view the bash manual pages for history,by typingmanhistory. Usingcommandaliases The aliascommandisanothershellbuilt-incommand.Acommandaliasallowsyouto create an alias name for common commands (along with their parameters) to help keep yourtypingtoaminimum. Mostlikely,yourLinuxdistributionhasalreadysetsomecommoncommandaliasesfor you.Toseealistoftheactivealiases,usethealiascommandwiththe-pparameter: $alias-p […] aliasegrep=‘egrep—color=auto’ aliasfgrep=‘fgrep—color=auto’ aliasgrep=‘grep—color=auto’ aliasl=‘ls-CF’ aliasla=‘ls-A’ aliasll=‘ls-alF’ aliasls=‘ls—color=auto’ $ Noticethat,onthisUbuntuLinuxdistribution,analiasisusedtooverridethestandard ls command. It automatically provides the —color parameter, indicating that the terminal supportscolormodelistings. Youcancreateyourownaliasesusingthealiascommand: $aliasli=‘ls-li’ $ $li total36 529581drwxr-xr-x.2ChristineChristine4096May1918:17Desktop 529585drwxr-xr-x.2ChristineChristine4096Apr2516:59Documents 529582drwxr-xr-x.2ChristineChristine4096Apr2516:59Downloads 529586drwxr-xr-x.2ChristineChristine4096Apr2516:59Music 529587drwxr-xr-x.2ChristineChristine4096Apr2516:59Pictures 529584drwxr-xr-x.2ChristineChristine4096Apr2516:59Public 529583drwxr-xr-x.2ChristineChristine4096Apr2516:59Templates 532891-rwxrw-r—.1ChristineChristine36May3007:21test.sh 529588drwxr-xr-x.2ChristineChristine4096Apr2516:59Videos $ Afteryoudefineanaliasvalue,youcanuseitatanytimeinyourshell,includinginshell scripts.Beawarethatbecausecommandaliasesarebuilt-incommands,analiasisvalid onlyfortheshellprocessinwhichitisdefined: $aliasli=‘ls-li’ $ $bash $ $li bash:li:commandnotfound $ $exit exit $ Fortunately, you can make an alias value permanent across subshells. The next chapter covershowtodothat,alongwithenvironmentvariables. Summary This chapter discussed the complicated interactive program, the GNU bash shell. It coveredunderstandingtheshellprocessanditsrelationships,includinghowsubshellsare spawnedandtheirrelationshiptotheparentshell.Wealsoexploredcommandsthatcreate childprocessesandcommandsthatdon’t. Thedefaultinteractiveshellisnormallystartedwheneverauserlogsintoaterminal.The shell that the system starts depends upon a user ID configuration. Typically, it is /bin/bash. The default system shell, /bin/sh, is used for system shell scripts, such as thoseneededatstartup. Asubshellorchildshellcanbespawnedusingthe bashcommand.Theyarealsocreated whenaprocesslistorthe coproccommandisused.Usingsubshellsatthecommandline canallowforcreativeandproductiveuseoftheCLI.Subshellscanbenested,spawning grandchildshellsandgreat-grandchildshells.Creatingasubshellisanexpensiveprocess asanewenvironmentfortheshellmustbecreatedaswell. Finally,thechapterlookedattwodifferenttypesofshellcommands:built-inandexternal commands. External commands create a child process with a new environment, but a built-incommanddoesnot.Thiscausesexternalcommandstobemoreexpensivetouse. Becauseanewenvironmentisnotneeded,built-incommandsaremoreefficientandnot affectedbyanyenvironmentchanges. Shells, subshells, processes, and forked processes are all affected by environment variables. How the variables affect and can be used within these different contexts are exploredinthenextchapter. Chapter6 UsingLinuxEnvironmentVariables InThisChapter 1. Lookingatenvironmentvariables 2. Creatingyourownlocalvariables 3. Removingvariables 4. Exploringdefaultshellenvironmentvariables 5. SettingthePATHenvironmentvariable 6. Locatingenvironmentfiles 7. Usingvariablearrays LinuxenvironmentvariableshelpdefineyourLinuxshellexperience.Many programsandscriptsuseenvironmentvariablestoobtainsysteminformationand storetemporarydataandconfigurationinformation.Environmentvariablesaresetin lotsofplacesontheLinuxsystem,andyoushouldknowwheretheseplacesare. ThischapterwalksyouthroughtheworldofLinuxenvironmentvariables,showing wheretheyare,howtousethem,andevenhowtocreateyourown.Thechapter finishesoffwithhowtousevariablearrays. ExploringEnvironmentVariables Thebashshellusesafeaturecalledenvironmentvariablestostoreinformationaboutthe shell session and the working environment (thus the name environment variables). This feature also allows you to store data in memory that can be easily accessed by any programorscriptrunningfromtheshell.Itisahandywaytostoreneededpersistentdata. Therearetwoenvironmentvariabletypesinthebashshell: Globalvariables Localvariables Thissectiondescribeseachtypeofenvironmentvariableandshowshowtoseeanduse them. Note Eventhoughthebashshellusesspecificenvironmentvariablesthatareconsistent, differentLinuxdistributionsoftenaddtheirownenvironmentvariables.The environmentvariableexamplesyouseeinthischaptermaydifferslightlyfrom what’savailableonyourspecificdistribution.Ifyourunintoanenvironmentvariable notcoveredhere,checkyourLinuxdistribution’sdocumentation Lookingatglobalenvironmentvariables Global environment variables are visible from the shell session and from any spawned child subshells. Local variables are available only in the shell that creates them. This makes global environment variables useful in applications that create child subshells, whichrequireparentshellinformation. The Linux system sets several global environment variables when you start your bash session.(Formoredetailsaboutwhatvariablesarestartedatthattime,seethe“Locating System Environment Variables” section later in this chapter.) The system environment variables almost always use all capital letters to differentiate them from normal user environmentvariables. Toviewglobalenvironmentvariables,usetheenvortheprintenvcommand: $printenv HOSTNAME=server01.class.edu SELINUX_ROLE_REQUESTED= TERM=xterm SHELL=/bin/bash HISTSIZE=1000 […] HOME=/home/Christine LOGNAME=Christine […] G_BROKEN_FILENAMES=1 _=/usr/bin/printenv Somanyglobalenvironmentvariablesgetsetforthebashshellthatthedisplayhadtobe snipped. Not only are many set during the login process, but how you log in can affect whichonesaresetaswell. To display an individual environment variable’s value, you can use the printenv command,butnottheenvcommand: $printenvHOME /home/Christine $ $envHOME env:HOME:Nosuchfileordirectory $ Youcanalsousethe echo command to display a variable’s value. When referencing an environmentvariableinthiscase,youmustplaceadollarsign($)beforetheenvironment variablename: $echo$HOME /home/Christine $ Usingthedollarsignalongwiththevariablenamedoesmorethanjustdisplayitscurrent definition when used with the echo command. The dollar sign before a variable name allowsthevariabletobepassedasacommandparameter: $ls$HOME DesktopDownloadsMusicPublictest.sh Documentsjunk.datPicturesTemplatesVideos $ $ls/home/Christine DesktopDownloadsMusicPublictest.sh Documentsjunk.datPicturesTemplatesVideos $ As mentioned earlier, global environment variables are also available to any process’s subshells: $bash $ $ps-f UIDPIDPPIDCSTIMETTYTIMECMD 50120172016016:00pts/000:00:00-bash 50120822017016:08pts/000:00:00bash 50120952082016:08pts/000:00:00ps-f $ $echo$HOME /home/Christine $ $exit exit $ Inthisexample,afterspawningasubshellusingthebashcommand,theHOMEenvironment variable’scurrentvalueisshown.Itissettotheexactsamevalue,/home/Christine,asit wasintheparentshell. Lookingatlocalenvironmentvariables Localenvironmentvariables,astheirnameimplies,canbeseenonlyinthelocalprocess inwhichtheyaredefined.Eventhoughtheyarelocal,theyarejustasimportantasglobal environmentvariables.Infact,theLinuxsystemalsodefinesstandardlocalenvironment variables for you by default. However, you can also define your own local variables. These,asyouwouldassume,arecalleduser-definedlocalvariables. TryingtoseethelocalvariableslistisalittletrickyattheCLI.Unfortunately,thereisn’ta command that displays only these variables. The set command displays all variables definedforaspecificprocess,includingbothlocalandglobalenvironmentvariablesand user-definedvariables: $set BASH=/bin/bash […] BASH_ALIASES=() BASH_ARGC=() BASH_ARGV=() BASH_CMDS=() BASH_LINENO=() BASH_SOURCE=() […] colors=/etc/DIR_COLORS my_variable=‘HelloWorld’ […] $ Allglobalenvironmentvariablesdisplayedusingthe envor printenvcommandsappear in the set command’s output. The additional environment variables are the local environmentanduser-definedvariables. Note Thedifferencesbetweenthecommandsenv,printenv,andsetaresubtle.Theset commanddisplaysbothglobalandlocalenvironmentvariablesanduser-defined variables.Italsosortsthedisplayalphabetically.Theenvandprintenvaredifferent fromsetinthattheydonotsortthevariables,nordotheyincludelocalenvironment orlocaluser-definedvariables.Usedinthiscontext,envandprintenvproduce duplicatelistings.However,theenvcommandhasadditionalfunctionalitythat printenvdoesnothave,makingittheslightlymorepowerfulcommand. SettingUser-DefinedVariables Youcansetyourownvariablesdirectlyfromthebashshell.Thissectionshowsyouhow tocreateyourownvariablesandreferencethemfromaninteractiveshellorshellscript program. Settinglocaluser-definedvariables Afteryoustartabashshell(orspawnashellscript),you’reallowedtocreatelocaluserdefined variables that are visible within your shell process. You can assign either a numericorastringvaluetoanenvironmentvariablebyassigningthevariabletoavalue usingtheequalsign: $echo$my_variable $my_variable=Hello $ $echo$my_variable Hello That was simple! Now, any time you need to reference the my_variable user-defined variable’svalue,justreferenceitbythename$my_variable. Ifyouneedtoassignastringvaluethatcontainsspaces,youneedtouseasingleordouble quotationmarktodelineatethebeginningandtheendofthestring: $my_variable=HelloWorld -bash:World:commandnotfound $ $my_variable=“HelloWorld” $ $echo$my_variable HelloWorld $ Without the quotation marks, the bash shell assumes that the next word is another commandtoprocess.Noticethatforthelocalvariableyoudefined,youusedlowercase letters,whilethesystemenvironmentvariablesyou’veseensofarhavealluseduppercase letters. Tip Thestandardbashshellconventionisforallenvironmentvariablestouseuppercase letters.Ifyouarecreatingalocalvariableforyourselfandyourownshellscripts,use lowercaseletters.Variablesarecasesensitive.Bykeepingyouruser-definedlocal variableslowercase,youavoidthepotentialdisasterofredefiningasystem environmentvariable. It’s extremely important that you not use spaces between the variable name, the equal sign,andthevalue.Ifyouputanyspacesintheassignment,thebashshellinterpretsthe valueasaseparatecommand: $my_variable=“HelloWorld” -bash:my_variable:commandnotfound $ After you set a local variable, it’s available for use anywhere within your shell process. However,ifyouspawnanothershell,it’snotavailableinthechildshell: $my_variable=“HelloWorld” $ $bash $ $echo$my_variable $exit exit $ $echo$my_variable HelloWorld $ In this example, a child shell was spawned. The user-defined my_variable was not availableinthechildshell.Thisisdemonstratedbytheblanklinereturnedafterthe echo $my_variable command. After the child shell was exited and returned to the original shell,thelocalvariablewasstillavailable. Similarly,ifyousetalocalvariableinachildprocess,afteryouleavethechildprocess, thelocalvariableisnolongeravailable: $echo$my_child_variable $bash $ $my_child_variable=“HelloLittleWorld” $ $echo$my_child_variable HelloLittleWorld $ $exit exit $ $echo$my_child_variable $ Thelocalvariablesetwithinthechildshelldoesn’texistafterareturntotheparentshell. You can change this behavior by turning your local user-defined variable into a global environmentvariable. Settingglobalenvironmentvariables Global environment variables are visible from any child processes created by the parent processthatsetsthevariable.Themethodusedtocreateaglobalenvironmentvariableis tofirstcreatealocalvariableandthenexportittotheglobalenvironment. Thisisdonebyusingtheexportcommandandthevariablenameminusthedollarsign: $my_variable=“IamGlobalnow” $ $exportmy_variable $ $echo$my_variable IamGlobalnow $ $bash $ $echo$my_variable IamGlobalnow $ $exit exit $ $echo$my_variable IamGlobalnow $ Afterdefiningandexportingthelocalvariable my_variable,achildshellwasstartedby the bash command. The child shell was able to properly display the my_variable variable’s value. The variable kept its value, because the export command made it a globalenvironmentvariable. Changingaglobalenvironmentvariablewithinachildshelldoesnotaffectthevariable’s valueintheparentshell: $my_variable=“IamGlobalnow” $exportmy_variable $ $echo$my_variable IamGlobalnow $ $bash $ $echo$my_variable IamGlobalnow $ $my_variable=“Null” $ $echo$my_variable Null $ $exit exit $ $echo$my_variable IamGlobalnow $ Afterdefiningandexportingthevariablemy_variable,asubshellwasstartedbythebash command. The subshell properly displayed the value of the my_variable global environmentvariable.Thevariable’svaluewasthenchangedbythechildshell.However, thevariable’svaluewasmodifiedonlywithinthechildshellandnotintheparent’sshell. A child shell cannot even use the export command to change the parent shell’s global environmentvariable’svalue: $my_variable=“IamGlobalnow” $exportmy_variable $ $echo$my_variable IamGlobalnow $ $bash $ $echo$my_variable IamGlobalnow $ $my_variable=“Null” $ $exportmy_variable $ $echo$my_variable Null $ $exit exit $ $echo$my_variable IamGlobalnow $ Eventhoughthechildshellredefinedandexportedthevariable my_variable,theparent shell’smy_variablevariablekeptitsoriginalvalue. RemovingEnvironmentVariables Ofcourse,ifyoucancreateanewenvironmentvariable,itmakessensethatyoucanalso removeanexistingenvironmentvariable.Youcandothiswiththeunsetcommand.When referencing the environment variable in the unset command, remember not to use the dollarsign: $echo$my_variable IamGlobalnow $ $unsetmy_variable $ $echo$my_variable $ Tip Itcanbeconfusingtorememberwhentouseandwhennottousethedollarsignwith environmentvariables.Justrememberthis:Ifyouaredoinganythingwiththe variable,usethedollarsign.Ifyouaredoinganythingtothevariable,don’tusethe dollarsign.Theexceptiontothisruleisusingprintenvtodisplayavariable’svalue. Whendealingwithglobalenvironmentvariables,thingsgetalittletricky.Ifyou’reina childprocessandunsetaglobalenvironmentvariable,itappliesonlytothechildprocess. Theglobalenvironmentvariableisstillavailableintheparentprocess: $my_variable=“IamGlobalnow” $ $exportmy_variable $ $echo$my_variable IamGlobalnow $ $bash $ $echo$my_variable IamGlobalnow $ $unsetmy_variable $ $echo$my_variable $exit exit $ $echo$my_variable IamGlobalnow $ Justaswithmodifyingavariable,youcannotunsetitinachildshellandhavethevariable beunsetintheparent’sshell. UncoveringDefaultShellEnvironmentVariables The bash shell uses specific environment variables by default to define the system environment.Youcanalwayscountonthesevariablesbeingsetoravailabletobeseton your Linux system. Because the bash shell is a derivative of the original Unix Bourne shell,italsoincludesenvironmentvariablesoriginallydefinedinthatshell. Table6.1showstheenvironmentvariablesthatthebashshellprovidesthatarecompatible withtheoriginalUnixBourneshell. Table6.1ThebashShellBourneVariables Variable CDPATH HOME IFS MAIL MAILPATH OPTARG OPTIND PATH PS1 PS2 Description Acolon-separatedlistofdirectoriesusedasasearchpathforthecdcommand Thecurrentuser’shomedirectory Alistofcharactersthatseparatefieldsusedbytheshelltosplittextstrings Thefilenameforthecurrentuser’smailbox(Thebashshellchecksthisfilefor newmail.) Acolon-separatedlistofmultiplefilenamesforthecurrentuser’smailbox (Thebashshellcheckseachfileinthislistfornewmail.) Thevalueofthelastoptionargumentprocessedbythegetoptcommand Theindexvalueofthelastoptionargumentprocessedbythegetoptcommand Acolon-separatedlistofdirectorieswheretheshelllooksforcommands Theprimaryshellcommandlineinterfacepromptstring Thesecondaryshellcommandlineinterfacepromptstring Besides the default Bourne environment variables, the bash shell also provides a few variablesofitsown,asshowninTable6.2. Table6.2ThebashShellEnvironmentVariables Variable BASH BASH_ALIASES BASH_ARGC BASH_ARCV BASH_CMDS BASH_COMMAND Description Thefullpathnametoexecutethecurrentinstanceofthebash shell Anassociativearrayofcurrentlysetaliases Avariablearraythatcontainsthenumberofparametersbeing passedtoasubroutineorshellscript Avariablearraythatcontainstheparametersbeingpassedtoa subroutineorshellscript Anassociativearrayoflocationsofcommandstheshellhas executed Theshellcommandcurrentlybeingorabouttobeexecuted Whenset,eachbashscriptattemptstoexecuteastartupfile definedbythisvariablebeforerunning. BASH_EXECUTION_STRING Thecommand(s)passedusingthebash-coption Avariablearraycontainingthesourcecodelinenumberofthe BASH_LINENO currentlyexecutingshellfunction Aread-onlyvariablearraycontainingpatternsandtheirsubBASH_REMATCH patternsforpositivematchesusingtheregularexpression comparisonoperator,=∼ Avariablearraycontainingthesourcecodefilenameofthe BASH_SOURCE currentlyexecutingshellfunction Thecurrentnestinglevelofasubshellenvironment(Theinitial BASH_SUBSHELL valueis0.) Avariablearraythatcontainstheindividualmajorandminor BASH_VERSINFO versionnumbersofthecurrentinstanceofthebashshell BASH_VERSION Theversionnumberofthecurrentinstanceofthebashshell Ifsettoavalidfiledescriptor(0,1,2),traceoutputgenerated BASH_XTRACEFD fromthe‘set-x’debuggingoptioncanberedirected.Thisis oftenusedtoseparatetraceoutputintoafile. BASHOPTS Alistofbashshelloptionsthatarecurrentlyenabled BASHPID ProcessIDofthecurrentbashprocess Containstheterminalwidthoftheterminalusedforthecurrent COLUMNS instanceofthebashshell AnindexintothevariableCOMP_WORDS,whichcontainsthe COMP_CWORD currentcursorposition COMP_LINE Thecurrentcommandline Theindexofthecurrentcursorpositionrelativetothe COMP_POINT beginningofthecurrentcommand Thefinalkeyusedtoinvokethecurrentcompletionofashell COMP_KEY function Anintegervaluerepresentingthetypeofcompletionattempted COMP_TYPE thatcausedacompletionshellfunctiontobeinvoked TheReadlinelibrarywordseparatorcharactersforperforming COMP_WORDBREAKS wordcompletion Anarrayvariablethatcontainstheindividualwordsonthe COMP_WORDS currentcommandline Anarrayvariablethatcontainsthepossiblecompletioncodes COMPREPLY generatedbyashellfunction BASH_ENV Variable COPROC Description Anarrayvariablethatholdsanunnamedcoprocess’I/Ofile descriptors DIRSTACK EMACS ENV EUID FCEDIT FIGNORE FUNCNAME FUNCNEST GLOBIGNORE GROUPS histchars HISTCMD HISTCONTROL HISTFILE HISTFILESIZE HISTTIMEFORMAT HISTIGNORE HISTSIZE HOSTFILE HOSTNAME HOSTTYPE IGNOREEOF INPUTRC LANG Anarrayvariablethatcontainsthecurrentcontentsofthe directorystack Indicatestheemacsshellbufferisexecutingandlineeditingis disabled,whensetto‘t’ Whenset,executesthestartupfiledefinedbeforeabashshell scriptruns(Itisusedonlywhenthebashshellhasbeen invokedinPOSIXmode.) ThenumericeffectiveuserIDofthecurrentuser Thedefaulteditorusedbythefccommand Acolon-separatedlistofsuffixestoignorewhenperforming filenamecompletion Thenameofthecurrentlyexecutingshellfunction Setsthemaximumallowedfunctionnestinglevel,whensetto anumbergreaterthanzero(Ifitisexceeded,thecurrent commandaborts.) Acolon-separatedlistofpatternsdefiningthesetoffilenames tobeignoredbyfilenameexpansion Avariablearraycontainingthelistofgroupsofwhichthe currentuserisamember Uptothreecharacters,whichcontrolhistoryexpansion Thehistorynumberofthecurrentcommand Controlswhatcommandsareenteredintheshellhistorylist Thenameofthefileinwhichtosavetheshellhistorylist (.bash_historybydefault) Themaximumnumberoflinestosaveinthehistoryfile Usedasaformattingstringtoprinteachcommand’stimestamp inbashhistory,ifsetandnotnull Acolon-separatedlistofpatternsusedtodecidewhich commandsareignoredforthehistoryfile Themaximumnumberofcommandsstoredinthehistoryfile Containsthenameofthefilethatshouldbereadwhentheshell needstocompleteahostname Thenameofthecurrenthost Astringdescribingthemachinethebashshellisrunningon ThenumberofconsecutiveEOFcharacterstheshellmust receivebeforeexiting(Ifthisvaluedoesn’texist,thedefaultis 1.) ThenameoftheReadlineinitializationfile(Thedefaultis .inputrc.) Thelocalecategoryfortheshell LC_ALL LC_COLLATE LC_CTYPE LC_MESSAGES LC_NUMERIC LINENO LINES MACHTYPE MAPFILE MAILCHECK OLDPWD OPTERR OSTYPE PIPESTATUS POSIXLY_CORRECT PPID PROMPT_COMMAND PROMPT_DIRTRIM PS3 PS4 PWD Variable RANDOM READLINE_LINE READLINE_POINT OverridestheLANGvariable,definingalocalecategory Setsthecollationorderusedwhensortingstringvalues Determinestheinterpretationofcharactersusedinfilename expansionandpatternmatching Determinesthelocalesettingusedwheninterpretingdoublequotedstringsprecededbyadollarsign Determinesthelocalesettingusedwhenformattingnumbers Thelinenumberinascriptcurrentlyexecuting Definesthenumberoflinesavailableontheterminal Astringdefiningthesystemtypeincpu-company-system format Anarrayvariablethatholdsread-intextfromthemapfile commandwhennoarrayvariablenameisgiven Howoften(inseconds)theshellshouldcheckfornewmail (Thedefaultis60.) Thepreviousworkingdirectoryusedintheshell Ifsetto1,thebashshelldisplayserrorsgeneratedbythe getoptscommand. Astringdefiningtheoperatingsystemtheshellisrunningon Avariablearraycontainingalistofexitstatusvaluesfromthe processesintheforegroundprocess Ifset,bashstartsinPOSIXmode. TheprocessID(PID)ofthebashshell’sparentprocess Ifset,thecommandtoexecutebeforedisplayingtheprimary prompt Anintegerusedtoindicatethenumberoftrailingdirectory namestodisplaywhenusingthe∖wand∖Wpromptstring escapes(Thedirectorynamesremovedarereplacedwithone setofellipses.) Theprompttousefortheselectcommand Thepromptdisplayedbeforethecommandlineisechoedifthe bash-xparameterisused Thecurrentworkingdirectory Description Returnsarandomnumberbetween0and32767(Assigninga valuetothisvariableseedsthepseudo-randomnumber generator.) Readlinebuffercontentswhenusingbind-xcommand Readlinebuffercontentinsertionpoint’scurrentpositionwhen REPLY SECONDS SHELL SHELLOPTS SHLVL TIMEFORMAT TMOUT TMPDIR UID usingbind-xcommand Thedefaultvariableforthereadcommand Thenumberofsecondssincetheshellwasstarted(Assigninga valueresetsthetimertothevalue.) Thefullpathnametothebashshell Acolon-separatedlistofenabledbashshelloptions Indicatestheshelllevel,incrementedbyoneeachtimeanew bashshellisstarted Aformatspecifyinghowtheshelldisplaystimevalues Thevalueofhowlong(inseconds)theselectandread commandsshouldwaitforinput(Thedefaultofzeroindicates towaitindefinitely.) Directorynamewherethebashshellcreatestemporaryfilesfor itsuse ThenumericrealuserIDofthecurrentuser You may notice that not all default environment variables are shown when the set commandisused.Whennotinuse,thedefaultenvironmentvariablesarenotallrequired tocontainavalue. SettingthePATHEnvironmentVariable Whenyouenteranexternalcommand(seeChapter5)intheshellcommandlineinterface (CLI), the shell must search the system to find the program. The PATH environment variabledefinesthedirectoriesitsearcheslookingforcommandsandprograms.Onthis UbuntuLinuxsystem,thePATHenvironmentvariablelookslikethis: $echo$PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin: /sbin:/bin:/usr/games:/usr/local/games $ This shows that there are eight directories where the shell looks for commands and programs.ThedirectoriesinthePATHareseparatedbycolons. Ifacommand’sorprogram’slocationisnotincludedinthePATHvariable,theshellcannot find it without an absolute directory reference. If the shell cannot find the command or program,itproducesanerrormessage: $myprog -bash:myprog:commandnotfound $ Theproblemisthatoftenapplicationsplacetheirexecutableprogramsindirectoriesthat aren’tinthe PATHenvironmentvariable.Thetrickisensuringthatyour PATHenvironment variableincludesallthedirectorieswhereyourapplicationsreside. You can add new search directories to the existing PATH environment variable without havingtorebuilditfromscratch.TheindividualdirectorieslistedinthePATHareseparated by colons. All you need to do is reference the original PATH value and add any new directoriestothestring.Thislookssomethinglikethis: $echo$PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin: /sbin:/bin:/usr/games:/usr/local/games $ $PATH=$PATH:/home/christine/Scripts $ $echo$PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/ games:/usr/local/games:/home/christine/Scripts $ $myprog Thefactorialof5is120. $ By adding the directory to the PATH environment variable, you can now execute your programfromanywhereinthevirtualdirectorystructure: $cd/etc $ $myprog Thefactorialof5is120 $ Tip Ifyouwantyourprogram’slocationtobeavailabletosubshells,besuretoexport yourmodifiedPATHenvironmentvariable. A common trick for programmers is to include the single dot symbol in their PATH environmentvariable.Thesingledotsymbolrepresentsthecurrentdirectory(seeChapter 3): $PATH=$PATH:. $ $cd/home/christine/Old_Scripts $ $myprog2 Thefactorialof6is720 $ ChangestothePATHvariablelastonlyuntilyouexitthesystemorthesystemreboots. Thechangesarenotpersistent.Inthenextsection,youseehowyoucanmakechangesto environmentvariablespermanent. LocatingSystemEnvironmentVariables TheLinuxsystemusesenvironmentvariablesformanypurposes.Youknownowhowto modifysystemenvironmentvariablesandcreateyourownvariables.Thetrickisinhow theseenvironmentvariablesaremadepersistent. When you start a bash shell by logging in to the Linux system, by default bash checks severalfilesforcommands.Thesefilesarecalledstartupfilesorenvironmentfiles.The startupfilesthatbashprocessesdependonthemethodyouusetostartthebashshell.You canstartabashshellinthreeways: Asadefaultloginshellatlogintime Asaninteractiveshellthatisstartedbyspawningasubshell Asanon-interactiveshelltorunascript Thefollowingsectionsdescribethestartupfilesthebashshellexecutesineachofthese startupmethods. Understandingtheloginshellprocess WhenyoulogintotheLinuxsystem,thebashshellstartsasaloginshell.Theloginshell typicallylooksforfivedifferentstartupfilestoprocesscommandsfrom: /etc/profile $HOME/.bash_profile $HOME/.bashrc $HOME/.bash_login $HOME/.profile The/etc/profilefileisthemaindefaultstartupfileforthebashshellonthesystem.All usersonthesystemexecutethisstartupfilewhentheylogin. Note BeawarethatsomeLinuxdistributionsusePluggableAuthenticationModules (PAM).Inthiscase,beforethebashshellisstarted,PAMfilesareprocessed, includingonesthatmaycontainenvironmentvariables.PAMfileexamplesinclude the/etc/environmentfileandthe$HOME/.pam_environmentfile.Findmore informationaboutPAMathttp://linux-pam.org. The other four startup files are specific for each user and can be customized for an individualuser’srequirements.Let’slookcloseratthesefiles. Viewingthe/etc/profilefile The/etc/profilefileisthemaindefaultstartupfileforthebashshell.Wheneveryoulog intotheLinuxsystem,bashexecutesthecommandsinthe/etc/profilestartupfilefirst. DifferentLinuxdistributionsplacedifferentcommandsinthisfile.OnthisUbuntuLinux system,thefilelookslikethis: $cat/etc/profile #/etc/profile:system-wide.profilefilefortheBourneshell(sh(1)) #andBournecompatibleshells(bash(1),ksh(1),ash(1),…). if[”$PS1”];then if[”$BASH”]&&[”$BASH”!=”/bin/sh”];then #Thefilebash.bashrcalreadysetsthedefaultPS1. #PS1=’\h:\w\$‘ if[-f/etc/bash.bashrc];then ./etc/bash.bashrc fi else if[”`id-u`”-eq0];then PS1=’#‘ else PS1=’$‘ fi fi fi #Thedefaultumaskisnowhandledbypam_umask. #Seepam_umask(8)and/etc/login.defs. if[-d/etc/profile.d];then foriin/etc/profile.d/*.sh;do if[-r$i];then .$i fi done unseti fi $ Most of the commands and syntax you see in this file are covered in more detail in Chapter12andbeyond.Eachdistribution’s /etc/profile file has different settings and commands. For example, notice that a file is mentioned in this Ubuntu distribution’s /etc/profile file above, called /etc/bash.bashrc. It contains system environment variables. However, in this CentOS distribution’s /etc/profile file listed below, no /etc/bash.bashrc file is called. Also note that it sets and exports some system environmentvariableswithinitself: $cat/etc/profile #/etc/profile #Systemwideenvironmentandstartupprograms,forloginsetup #Functionsandaliasesgoin/etc/bashrc #It’sNOTagoodideatochangethisfileunlessyouknowwhatyou #aredoing.It’smuchbettertocreateacustom.shshellscriptin #/etc/profile.d/tomakecustomchangestoyourenvironment,to #preventtheneedformerginginfutureupdates. pathmunge(){ case“:${PATH}:”in *:”$1”:*) ;; *) if[“$2”=“after”];then PATH=$PATH:$1 else PATH=$1:$PATH fi esac } if[-x/usr/bin/id];then if[-z“$EUID”];then #kshworkaround EUID=`id-u` UID=`id-ru` fi USER=”`id-un`” LOGNAME=$USER MAIL=”/var/spool/mail/$USER” fi #Pathmanipulation if[“$EUID”=“0”];then pathmunge/sbin pathmunge/usr/sbin pathmunge/usr/local/sbin else pathmunge/usr/local/sbinafter pathmunge/usr/sbinafter pathmunge/sbinafter fi HOSTNAME=`/bin/hostname2>/dev/null` HISTSIZE=1000 if[“$HISTCONTROL”=“ignorespace”];then exportHISTCONTROL=ignoreboth else exportHISTCONTROL=ignoredups fi exportPATHUSERLOGNAMEMAILHOSTNAMEHISTSIZEHISTCONTROL #Bydefault,wewantumasktogetset.Thissetsitforloginshell #Currentthresholdforsystemreserveduid/gidsis200 #Youcouldcheckuidgidreservationvalidityin #/usr/share/doc/setup-*/uidgidfile if[$UID-gt199]&&[“`id-gn`”=“`id-un`”];then umask002 else umask022 fi foriin/etc/profile.d/*.sh;do if[-r“$i”];then if[“${-#*i}”!=“$-”];then .“$i” else .“$i”>/dev/null2>&1 fi fi done unseti unset-fpathmunge $ Both distributions’ /etc/profile files use a certain feature. It is a for statement that iterates through any files located in the /etc/profile.d directory. (for statements are discussed in detail in Chapter 13.) This provides a place for the Linux system to place application-specific startup files that is executed by the shell when you log in. On this UbuntuLinuxsystem,thefollowingfilesareintheprofile.ddirectory: $ls-l/etc/profile.d total12 -rw-r—r—1rootroot40Apr1506:26appmenu-qt5.sh -rw-r—r—1rootroot663Apr710:10bash_completion.sh -rw-r—r—1rootroot1947Nov222013vte.sh $ YoucanseethatthisCentOssystemhasquiteafewmorefilesin/etc/profile.d: $ls-l/etc/profile.d total80 -rw-r—r—.1rootroot1127Mar507:17colorls.csh -rw-r—r—.1rootroot1143Mar507:17colorls.sh -rw-r—r—.1rootroot92Nov222013cvs.csh -rw-r—r—.1rootroot78Nov222013cvs.sh -rw-r—r—.1rootroot192Feb2409:24glib2.csh -rw-r—r—.1rootroot192Feb2409:24glib2.sh -rw-r—r—.1rootroot58Nov222013gnome-ssh-askpass.csh -rw-r—r—.1rootroot70Nov222013gnome-ssh-askpass.sh -rwxr-xr-x.1rootroot373Sep232009kde.csh -rwxr-xr-x.1rootroot288Sep232009kde.sh -rw-r—r—.1rootroot1741Feb2005:44lang.csh -rw-r—r—.1rootroot2706Feb2005:44lang.sh -rw-r—r—.1rootroot122Feb72007less.csh -rw-r—r—.1rootroot108Feb72007less.sh -rw-r—r—.1rootroot976Sep232011qt.csh -rw-r—r—.1rootroot912Sep232011qt.sh -rw-r—r—.1rootroot2142Mar1315:37udisks-bash-completion.sh -rw-r—r—.1rootroot97Apr52012vim.csh -rw-r—r—.1rootroot269Apr52012vim.sh -rw-r—r—.1rootroot169May202009which2.sh $ Notice that several files are related to specific applications on the system. Most applicationscreatetwostartupfiles—oneforthebashshell(usingthe.shextension)and oneforthecshell(usingthe.cshextension). The lang.cshand lang.sh files attempt to determine the default language character set usedonthesystemandsettheLANGenvironmentvariableappropriately. Viewingthe$HOMEstartupfiles Theremainingstartupfilesareallusedforthesamefunction—toprovideauser-specific startupfilefordefininguser-specificenvironmentvariables.MostLinuxdistributionsuse onlyoneortwoofthesefourstartupfiles: $HOME/.bash_profile $HOME/.bashrc $HOME/.bash_login $HOME/.profile Noticethatallfourfilesstartwithadot,makingthemhiddenfiles(theydon’tappearina normal lscommandlisting).Becausetheyareintheuser’s HOMEdirectory,eachusercan editthefilesandaddhisorherownenvironmentvariablesthatareactiveforeverybash shellsessiontheystart. Note EnvironmentfilesareoneareawhereLinuxdistributionsvarygreatly.Notevery $HOMEfilelistedinthissectionexistsforeveryuser.Forexample,someusersmay haveonlythe$HOME/.bash_profilefile.Thisisnormal. Thefirstfilefoundinthefollowingorderedlistisrun,andtherestareignored: $HOME/.bash_profile $HOME/.bash_login $HOME/.profile Noticethat$HOME/.bashrcisnotinthislist.Thisisbecauseitistypicallyrunfromoneof theotherfiles. Tip Rememberthat$HOMErepresentsauser’shomedirectory.Also,thetilde(∼)isusedto representauser’shomedirectory. ThisCentOSLinuxsystemcontainsthefollowing.bash_profilefile: $cat$HOME/.bash_profile #.bash_profile #Getthealiasesandfunctions if[-f~/.bashrc];then .~/.bashrc fi #Userspecificenvironmentandstartupprograms PATH=$PATH:$HOME/bin exportPATH $ The.bash_profilestartupfilefirstcheckstoseeifthestartupfile,.bashrc,ispresentin theHOMEdirectory.Ifit’sthere,thestartupfileexecutesthecommandsinit. Understandingtheinteractiveshellprocess If you start a bash shell without logging into a system (if you just type bash at a CLI prompt, for example), you start what’s called an interactive shell. The interactive shell doesn’t act like the login shell, but it still provides a CLI prompt for you to enter commands. Ifbashisstartedasaninteractiveshell,itdoesn’tprocessthe /etc/profilefile.Instead, itonlychecksforthe.bashrcfileintheuser’sHOMEdirectory. OnthisLinuxCentOSdistribution,thisfilelookslikethis: $cat.bashrc #.bashrc #Sourceglobaldefinitions if[-f/etc/bashrc];then ./etc/bashrc fi #Userspecificaliasesandfunctions $ The .bashrcfiledoestwothings.First,itchecksforacommon bashrcfileinthe /etc directory. Second, it provides a place for the user to enter personal command aliases (discussedinChapter5)andprivatescriptfunctions(describedinChapter17). Understandingthenon-interactiveshellprocess Thelasttypeofshellisanon-interactivesubshell.Thisistheshellwherethesystemcan starttoexecuteashellscript.Thisisdifferentinthatthereisn’taCLIprompttoworry about. However, you may want to run specific startup commands each time you start a scriptonyoursystem. Tip Scriptscanbeexecutedindifferentways.Onlysomeexecutionmethodsstarta subshell.YoulearnaboutthedifferentshellexecutionmethodsinChapter11. To accommodate that situation, the bash shell provides the BASH_ENV environment variable. When the shell starts a non-interactive subshell process, it checks this environment variable for the startup file name to execute. If one is present, the shell executesthefile’scommands,whichtypicallyincludevariablessetfortheshellscripts. OnthisCentOSLinuxdistribution,thisenvironmentvalueisnotsetbydefault.Whena variableisnotset,theprintenvcommandsimplyreturnstheCLIprompt: $printenvBASH_ENV $ OnthisUbuntudistribution,theBASH_ENVvariableisn’tseteither.Rememberthat,whena variableisnotset,theechocommanddisplaysablanklineandreturnstheCLIprompt: $echo$BASH_ENV $ So if the BASH_ENV variable isn’t set, how do the shell scripts get their environment variables?Rememberthatsomeshellscriptexecutionmethodsstartasubshell,alsocalled achildshell(seeChapter5).Achildshellinheritsitsparentshell’sexportedvariables. Forexample,iftheparentshellwasaloginshellandhadvariablessetandexportedinthe /etc/profile file, /etc/profile.d/*.sh files, and the $HOME/.bashrc file, the child shellforthescriptinheritsthesevariables. However,rememberthatanyvariablessetbutnotexportedbytheparentshellarelocal variables.Localvariablesarenotinheritedbyasubshell. For scripts that do not start a subshell, the variables are already available in the current shell.Thus,evenif BASH_ENVisnotset,boththecurrentshell’slocalandglobalvariables arepresenttobeused. Makingenvironmentvariablespersistent Now that you know you way around the various shell process types and their various environmentfiles,locatingthepermanentenvironmentvariablesismucheasier.Youcan alsosetyourownpermanentglobalorlocalvariablesusingthesefiles. For global environment variables (those variables needed by all the users on a Linux system),itmaybetemptingtoputnewormodifiedvariablesettingsinthe/etc/profile, butthisisabadidea.Thefilecouldbechangedwhenyourdistributionisupgraded,and youwouldloseallthecustomizedvariablesettings. Itisabetterideatocreateafileendingwith .shinthe /etc/profile.ddirectory.Inthat file,placeallyournewormodifiedglobalenvironmentvariablesettings. On most distributions, the best place to store an individual user’s persistent bash shell variablesisinthe $HOME/.bashrcfile.Thisistrueforallshellprocesstypes.However,if theBASH_ENVvariableisset,keepinmindthatunlessitpointsto$HOME/.bashrc,youmay needtostoreauser’svariablesfornon-interactiveshelltypeselsewhere. Note Keepinmindthatuserenvironmentvariablesforgraphicalinterfaceelements,such astheGUIclient,mayneedtobesetindifferentconfigurationfilesthanwherebash shellenvironmentvariablesareset RecallbackinChapter5thatcommandaliassettingsarealsonotpersistent.Youcanalso store your personal alias settings in the $HOME/.bashrc startup file to make them permanent. LearningaboutVariableArrays Areallycoolfeatureofenvironmentvariablesisthattheycanbeusedasarrays.Anarray isavariablethatcanholdmultiplevalues.Valuescanbereferencedeitherindividuallyor asawholefortheentirearray. To set multiple values for an environment variable, just list them in parentheses, with valuesseparatedbyspaces: $mytest=(onetwothreefourfive) $ Not much excitement there. If you try to display the array as a normal environment variable,you’llbedisappointed: $echo$mytest one $ Only the first value in the array appears. To reference an individual array element, you must use a numerical index value, which represents its place in the array. The numeric valueisenclosedinsquarebrackets: $echo${mytest[2]} three $ Tip Environmentvariablearraysstartwithanindexvalueofzero.Thiscanbeconfusing. To display an entire array variable, you use the asterisk wildcard character as the index value: $echo${mytest[*]} onetwothreefourfive $ Youcanalsochangethevalueofanindividualindexposition: $mytest[2]=seven $ $echo${mytest[*]} onetwosevenfourfive $ Youcanevenusethe unsetcommandtoremoveanindividualvaluewithinthearray,but becareful,becausethisgetstricky.Watchthisexample: $unsetmytest[2] $ $echo${mytest[*]} onetwofourfive $ $echo${mytest[2]} $echo${mytest[3]} four $ Thisexampleusesthe unsetcommandtoremovethevalueatindexvalue2.Whenyou displaythearray,itappearsthattheotherindexvaluesjustdroppeddownone.However, ifyouspecificallydisplaythedataatindexvalue2,youseethatthatlocationisempty. Finally, you can remove the entire array just by using the array name in the unset command: $unsetmytest $ $echo${mytest[*]} $ Sometimesvariablearraysjustcomplicatematters,sothey’reoftennotusedinshellscript programming.They’renotveryportabletoothershellenvironments,whichisadownside ifyoudolotsofshellprogrammingfordifferentshells.Somebashsystemenvironment variables use arrays (such as BASH_VERSINFO), but overall you probably won’t run into themveryoften. Summary This chapter examined the world of Linux environment variables. Global environment variables can be accessed from any child shell spawned by the parent shell in which they’re defined. Local environment variables can be accessed only from the process in whichthey’redefined. TheLinuxsystemusesbothglobalandlocalenvironmentvariablestostoreinformation aboutthesystemenvironment.Youcanaccessthisinformationfromtheshellcommand lineinterface,aswellaswithinshellscripts.Thebashshellusesthesystemenvironment variables defined in the original Unix Bourne shell, as well as lots of new environment variables.ThePATHenvironmentvariabledefinesthesearchpatternthebashshelltakesto findanexecutablecommand.Youcanmodifythe PATHenvironmentvariabletoaddyour own directories, or even the current directory symbol, to make running your programs easier. Youcanalsocreateyourownglobalandlocalenvironmentvariablesforyourownuse. After you create an environment variable, it’s accessible for the entire duration of your shellsession. The bash shell executes several startup files when it starts up. These startup files can contain environment variable definitions to set standard environment variables for each bash session. When you log in to the Linux system, the bash shell accesses the /etc/profile startup file and three local startup files for each user, $HOME/.bash_profile, $HOME/.bash_login, and $HOME/.profile. Users can customize thesefilestoincludeenvironmentvariablesandstartupscriptsfortheirownuse. Finally,thischapterdiscussedtheuseofenvironmentvariablearrays.Theseenvironment variablescancontainmultiplevaluesinasinglevariable.Youcanaccessthevalueseither individually by referencing an index value or as a whole by referencing the entire environmentvariablearrayname. ThenextchapterdivesintotheworldofLinuxfilepermissions.Thisispossiblythemost difficult topic for novice Linux users. However, to write good shell scripts, you need to understandhowfilepermissionsworkandbeabletousethemonyourLinuxsystem. Chapter7 UnderstandingLinuxFilePermissions InThisChapter 1. UnderstandingLinuxsecurity 2. Decodingthepermissions 3. WorkingwithLinuxgroups Nosystemiscompletewithoutsomeformofsecurity.Theremustbeamechanism availabletoprotectfilesfromunauthorizedviewingormodification.TheLinux systemfollowstheUnixmethodoffilepermissions,allowingindividualusersand groupsaccesstofilesbasedonasetofsecuritysettingsforeachfileanddirectory. ThischapterdiscusseshowtousetheLinuxfilesecuritysystemtoprotectdatawhen necessaryandsharedatawhendesired. LinuxSecurity ThecoreoftheLinuxsecuritysystemistheuseraccount.Eachindividualwhoaccessesa Linux system should have a unique user account assigned. The users’ permissions to objectsonthesystemdependontheuseraccounttheyloginwith. UserpermissionsaretrackedusingauserID(oftencalledaUID),whichisassignedtoan accountwhenit’screated.TheUIDisanumericalvalue,uniqueforeachuser.However, youdon’tlogintoaLinuxsystemusingyourUID.Instead,youusealoginname.The loginnameisanalphanumerictextstringofeightcharactersorfewerthattheuserusesto logintothesystem(alongwithanassociatedpassword). TheLinuxsystemusesspecialfilesandutilitiestotrackandmanageuseraccountsonthe system. Before we can discuss file permissions, we need to discuss how Linux handles user accounts. This section describes the files and utilities required for user accounts so thatyoucanunderstandhowtousethemwhenworkingwithfilepermissions. The/etc/passwdfile The Linux system uses a special file to match the login name to a corresponding UID value.Thisfileisthe /etc/passwdfile.The /etc/passwdfilecontainsseveralpiecesof informationabouttheuser.Here’swhatatypical /etc/passwdfilelookslikeonaLinux system: $cat/etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin sync:x:5:0:sync:/sbin:/bin/sync shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown halt:x:7:0:halt:/sbin:/sbin/halt mail:x:8:12:mail:/var/spool/mail:/sbin/nologin news:x:9:13:news:/etc/news: uucp:x:10:14:uucp:/var/spool/uucp:/sbin/nologin operator:x:11:0:operator:/root:/sbin/nologin games:x:12:100:games:/usr/games:/sbin/nologin gopher:x:13:30:gopher:/var/gopher:/sbin/nologin ftp:x:14:50:FTPUser:/var/ftp:/sbin/nologin nobody:x:99:99:Nobody:/:/sbin/nologin rpm:x:37:37::/var/lib/rpm:/sbin/nologin vcsa:x:69:69:virtualconsolememoryowner:/dev:/sbin/nologin mailnull:x:47:47::/var/spool/mqueue:/sbin/nologin smmsp:x:51:51::/var/spool/mqueue:/sbin/nologin apache:x:48:48:Apache:/var/www:/sbin/nologin rpc:x:32:32:RpcbindDaemon:/var/lib/rpcbind:/sbin/nologin ntp:x:38:38::/etc/ntp:/sbin/nologin nscd:x:28:28:NSCDDaemon:/:/sbin/nologin tcpdump:x:72:72::/:/sbin/nologin dbus:x:81:81:Systemmessagebus:/:/sbin/nologin avahi:x:70:70:Avahidaemon:/:/sbin/nologin hsqldb:x:96:96::/var/lib/hsqldb:/sbin/nologin sshd:x:74:74:Privilege-separatedSSH:/var/empty/sshd:/sbin/nologin rpcuser:x:29:29:RPCServiceUser:/var/lib/nfs:/sbin/nologin nfsnobody:x:65534:65534:AnonymousNFSUser:/var/lib/nfs:/sbin/nologin haldaemon:x:68:68:HALdaemon:/:/sbin/nologin xfs:x:43:43:XFontServer:/etc/X11/fs:/sbin/nologin gdm:x:42:42::/var/gdm:/sbin/nologin rich:x:500:500:RichBlum:/home/rich:/bin/bash mama:x:501:501:Mama:/home/mama:/bin/bash katie:x:502:502:katie:/home/katie:/bin/bash jessica:x:503:503:Jessica:/home/jessica:/bin/bash mysql:x:27:27:MySQLServer:/var/lib/mysql:/bin/bash $ The root user account is the administrator for the Linux system and is always assigned UID 0. As you can see, the Linux system creates lots of user accounts for various functionsthataren’tactualusers.Thesearecalledsystemaccounts.Asystemaccountisa specialaccountthatservicesrunningonthesystemusetogainaccesstoresourcesonthe system.AllservicesthatruninbackgroundmodeneedtobeloggedintotheLinuxsystem underasystemuseraccount. Beforesecuritybecameabigissue,theseservicesoftenjustloggedinusingthe rootuser account. Unfortunately, if an unauthorized person broke into one of these services, he instantly gained access to the system as the root user. To prevent this, now just about everyservicethatrunsinbackgroundonaLinuxserverhasitsownuseraccounttologin with. This way, if a troublemaker compromises a service, he still can’t necessarily get accesstothewholesystem. LinuxreservesUIDsbelow500forsystemaccounts.Someservicesevenrequirespecific UIDstoworkproperly.Whenyoucreateaccountsfornormalusers,mostLinuxsystems assignthefirstavailableUIDstartingat500(althoughthisisnotnecessarilytrueforall Linuxdistributions). You probably noticed that the /etc/passwd file contains much more than just the login name and UID for the user. The fields of the /etc/passwd file contain the following information: Theloginusername Thepasswordfortheuser ThenumericalUIDoftheuseraccount ThenumericalgroupID(GID)oftheuseraccount Atextdescriptionoftheuseraccount(calledthecommentfield) ThelocationoftheHOMEdirectoryfortheuser Thedefaultshellfortheuser Thepasswordfieldinthe /etc/passwdfileissettoan x.Thisdoesn’tmeanthatallthe user accounts have the same password. In the old days of Linux, the /etc/passwd file containedanencryptedversionoftheuser’spassword.However,becauselotsofprograms needtoaccessthe /etc/passwdfileforuserinformation,thisbecameasecurityproblem. Withtheadventofsoftwarethatcouldeasilydecryptencryptedpasswords,thebadguys had a field day trying to break user passwords stored in the /etc/passwd file. Linux developersneededtorethinkthatpolicy. Now,mostLinuxsystemsholduserpasswordsinaseparatefile(calledtheshadowfile, locatedat /etc/shadow).Onlyspecialprograms(suchastheloginprogram)areallowed accesstothisfile. The /etc/passwd file is a standard text file. You can use any text editor to manually perform user management functions (such as adding, modifying, or removing user accounts) directly in the /etc/passwd file. However, this is an extremely dangerous practice.Ifthe/etc/passwdfilebecomescorrupt,thesystemcan’treadit,anditprevents anyone(eventherootuser)fromloggingin.Instead,it’ssafertousethestandardLinux usermanagementutilitiestoperformallusermanagementfunctions. The/etc/shadowfile The /etc/shadow file provides more control over how the Linux system manages passwords.Onlytherootuserhasaccesstothe /etc/shadowfile,makingitmoresecure thanthe/etc/passwdfile. The /etc/shadowfilecontainsonerecordforeachuseraccountonthesystem.Arecord lookslikethis: rich:$1$.FfcK0ns$f1UgiyHQ25wrB/hykCn020:11627:0:99999:7::: Thereareninefieldsineach/etc/shadowfilerecord: Theloginnamecorrespondingtotheloginnameinthe/etc/passwdfile Theencryptedpassword ThenumberofdayssinceJanuary1,1970,thatthepasswordwaslastchanged Theminimumnumberofdaysbeforethepasswordcanbechanged Thenumberofdaysbeforethepasswordmustbechanged Thenumberofdaysbeforepasswordexpirationthattheuseriswarnedtochangethe password Thenumberofdaysafterapasswordexpiresbeforetheaccountwillbedisabled Thedate(storedasthenumberofdayssinceJanuary1,1970)sincetheuseraccount wasdisabled Afieldreservedforfutureuse Using the shadow password system, the Linux system has much finer control over user passwords.Itcancontrolhowoftenausermustchangehisorherpasswordandwhento disabletheaccountifthepasswordhasn’tbeenchanged. Addinganewuser TheprimarytoolusedtoaddnewuserstoyourLinuxsystemis useradd.Thiscommand provides an easy way to create a new user account and set up the user’s HOME directory structureallatonce.The useraddcommandusesacombinationofsystemdefaultvalues andcommandlineparameterstodefineauseraccount.Thesystemdefaultsaresetinthe /etc/default/useradd file. To see the system default values used on your Linux distribution,entertheuseraddcommandwiththe-Dparameter: #/usr/sbin/useradd-D GROUP=100 HOME=/home INACTIVE=-1 EXPIRE= SHELL=/bin/bash SKEL=/etc/skel CREATE_MAIL_SPOOL=yes # Note SomeLinuxdistributionsplacetheLinuxuserandgrouputilitiesinthe/usr/sbin directory,whichmaynotbeinyourPATHenvironmentvariable.Ifthat’sthecasein yourLinuxdistribution,eitheraddthedirectorytoyourPATHorusetheabsolutefile pathtorunit. The -D parameter shows what defaults the useradd command uses if you don’t specify them in the command line when creating a new user account. This example shows the followingdefaultvalues: ThenewuserisaddedtoacommongroupwithgroupID100. ThenewuserhasaHOMEaccountcreatedinthedirectory/home/loginname. Theaccountcan’tbedisabledwhenthepasswordexpires. Thenewaccountcan’tbesettoexpireatasetdate. Thenewaccountusesthebashshellasthedefaultshell. Thesystemcopiesthecontentsofthe/etc/skeldirectorytotheuser’sHOME directory. Thesystemcreatesafileinthemaildirectoryfortheuseraccounttoreceivemail. The penultimate value is interesting. The useradd command allows an administrator to createadefault HOMEdirectoryconfigurationandthenusesthatasatemplatetocreatethe newuser’s HOMEdirectory.Thisallowsyoutoplacedefaultfilesforthesysteminevery new user’s HOME directory automatically. In the Ubuntu Linux system, the /etc/skel directoryhasthefollowingfiles: $ls-al/etc/skel total32 drwxr-xr-x2rootroot40962010-04-2908:26. drwxr-xr-x135rootroot122882010-09-2318:49.. -rw-r—r—1rootroot2202010-04-1821:51.bash_logout -rw-r—r—1rootroot31032010-04-1821:51.bashrc -rw-r—r—1rootroot1792010-03-2608:31examples.desktop -rw-r—r—1rootroot6752010-04-1821:51.profile $ YoushouldrecognizethesefilesfromChapter6.Thesearethestandardstartupfilesfor thebashshellenvironment.Thesystemautomaticallycopiesthesedefaultfilesintoevery user’sHOMEdirectoryyoucreate. Youcantestthisbycreatinganewuseraccountusingthedefaultsystemparametersand thenlookingattheHOMEdirectoryforthenewuser: #useradd-mtest #ls-al/home/test total24 drwxr-xr-x2testtest40962010-09-2319:01. drwxr-xr-x4rootroot40962010-09-2319:01.. -rw-r—r—1testtest2202010-04-1821:51.bash_logout -rw-r—r—1testtest31032010-04-1821:51.bashrc -rw-r—r—1testtest1792010-03-2608:31examples.desktop -rw-r—r—1testtest6752010-04-1821:51.profile # By default, the useradd command doesn’t create a HOME directory, but the –m command lineoptiontellsittocreatetheHOMEdirectory.Asyoucanseeintheexample,theuseradd command created the new HOME directory, using the files contained in the /etc/skel directory. Note Toruntheuseraccountadministrationcommandsinthischapter,youeitherneedto beloggedinasthespecialrootuseraccountorusethesudocommandtorunthe commandsastherootuseraccount. Ifyouwanttooverrideadefaultvalueorbehaviorwhencreatinganewuser,youcando thatwithcommandlineparameters.TheseareshowninTable7.1. Table7.1TheuseraddCommandLineParameters Parameter -ccomment Description Addstexttothenewuser’scommentfield SpecifiesadifferentnamefortheHOMEdirectoryotherthanthelogin -dhome_dir name -e Specifiesadate,inYYYY-MM-DDformat,whentheaccountwill expire_date expire Specifiesthenumberofdaysafterapasswordexpireswhentheaccount -f willbedisabled.Avalueof0disablestheaccountassoonasthe inactive_days passwordexpires;avalueof-1disablesthisfeature. -g initial_group SpecifiesthegroupnameorGIDoftheuser’slogingroup -Ggroup… Specifiesoneormoresupplementarygroupstheuserbelongsto Copiesthe/etc/skeldirectorycontentsintotheuser’sHOMEdirectory (mustuse-maswell) Createstheuser’sHOMEdirectory Doesn’tcreateauser’sHOMEdirectory(usedifthedefaultsettingisto createone) Createsanewgroupusingthesamenameastheuser’sloginname Createsasystemaccount Specifiesadefaultpasswordfortheuseraccount Specifiesthedefaultloginshell SpecifiesauniqueUIDfortheaccount -k -m -M -n -r -ppasswd -sshell -uuid Asyoucansee,youcanoverrideallthesystemdefaultvalueswhencreatinganewuser accountjustbyusingcommandlineparameters.However,ifyoufindyourselfhavingto overrideavalueallthetime,it’seasiertojustchangethesystemdefaultvalue. Youcanchangethesystemdefaultnewuservaluesbyusingthe -Dparameter,alongwith a parameter representing the value you need to change. These parameters are shown in Table7.2. Table7.2TheuseraddChangeDefaultValuesParameters Parameter -bdefault_home Description Changesthelocationwhereusers’HOMEdirectoriesarecreated -e expiration_date Changestheexpirationdateonnewaccounts -finactive -ggroup -sshell Changesthenumberofdaysafterapasswordhasexpiredbeforethe accountisdisabled ChangesthedefaultgroupnameorGIDused Changesthedefaultloginshell Changingthedefaultvaluesisasnap: #useradd-D-s/bin/tsch #useradd-D GROUP=100 HOME=/home INACTIVE=-1 EXPIRE= SHELL=/bin/tsch SKEL=/etc/skel CREATE_MAIL_SPOOL=yes # Now,theuseraddcommandusesthe tschshellasthedefaultloginshellforallnewuser accountsyoucreate. Removingauser Ifyouwanttoremoveauserfromthesystem,the userdelcommandiswhatyouneed. By default, the userdel command removes only the user information from the /etc/passwdfile.Itdoesn’tremoveanyfilestheaccountownsonthesystem. If you use the -r parameter, userdel removes the user’s HOME directory, along with the user’smaildirectory.However,otherfilesownedbythedeleteduseraccountmaystillbe onthesystem.Thiscanbeaprobleminsomeenvironments. Here’sanexampleofusingtheuserdelcommandtoremoveanexistinguseraccount: #/usr/sbin/userdel-rtest #ls-al/home/test ls:cannotaccess/home/test:Nosuchfileordirectory # Afterusingthe-rparameter,theuser’sold/home/testdirectorynolongerexists. Caution Becarefulwhenusingthe-rparameterinanenvironmentwithlotsofusers.You neverknowifauserhadimportantfilesstoredinhisorherHOMEdirectorythatare usedbysomeoneelseoranotherprogram.Alwayscheckbeforeremovingauser’s HOMEdirectory! Modifyingauser Linux provides a few different utilities for modifying the information for existing user accounts.Table7.3showstheseutilities. Table7.3UserAccountModificationUtilities Command Description Editsuseraccountfields,aswellasspecifyingprimaryandsecondarygroup usermod membership passwd Changesthepasswordforanexistinguser chpasswd Readsafileofloginnameandpasswordpairs,andupdatesthepasswords chage Changesthepassword’sexpirationdate chfn Changestheuseraccount’scommentinformation chsh Changestheuseraccount’sdefaultshell Each of these utilities provides a specific function for changing information about user accounts.Thefollowingsectionsdescribeeachoftheseutilities. usermod The usermod command is the most robust of the user account modification utilities. It providesoptionsforchangingmostofthefieldsinthe /etc/passwdfile.Todothat,you just need to use the command line parameter that corresponds to the value you want to change. The parameters are mostly the same as the useradd parameters (such as -c to changethecommentfield, -etochangetheexpirationdate,and -gtochangethedefault logingroup).However,acoupleofadditionalparametersmightcomeinhandy: -lchangestheloginnameoftheuseraccount. -Llockstheaccountsotheusercan’tlogin. -pchangesthepasswordfortheaccount. -Uunlockstheaccountsotheusercanlogin. The -Lparameterisespeciallyhandy.Usethistolockanaccountsoausercan’tlogin withouthavingtoremovetheaccountandtheuser’sdata.Toreturntheaccounttonormal, justusethe-Uparameter. passwdandchpasswd Aquickwaytochangejustthepasswordforauseristhepasswdcommand: #passwdtest Changingpasswordforusertest. NewUNIXpassword: RetypenewUNIXpassword: passwd:allauthenticationtokensupdatedsuccessfully. # Ifyoujustusethe passwdcommandbyitself,itchangesyourownpassword.Anyuserin the system can change his or her own password, but only the root user can change someoneelse’spassword. The -eoptionisahandywaytoforceausertochangethepasswordonthenextlogin. Thisallowsyoutosettheuser’spasswordtoasimplevalueandforcesthemtochangeit tosomethingharderthattheycanremember. If you ever need to do a mass password change for lots of users on the system, the chpasswdcommandcanbealifesaver.The chpasswdcommandreadsalistofloginname andpasswordpairs(separatedbyacolon)fromthestandardinput,automaticallyencrypts thepassword,andsetsitfortheuseraccount.Youcanalsousetheredirectioncommand toredirectafileofuserid:passwordpairsintothecommand: #chpasswd<users.txt # chsh,chfn,andchage The chsh, chfn, and chage utilities are specialized for specific account modification functions.The chshcommandallowsyoutoquicklychangethedefaultloginshellfora user.Youmustusethefullpathnamefortheshell,andnotjusttheshellname: #chsh-s/bin/cshtest Changingshellfortest. Shellchanged. # The chfncommandprovidesastandardmethodforstoringinformationinthecomments field in the /etc/passwd file. Instead of just inserting random text, such as names or nicknames,orevenjustleavingthecommentfieldblank,thechfncommandusesspecific informationusedintheUnix fingercommandtostoreinformationinthecommentfield. The fingercommandallowsyoutoeasilyfindinformationaboutpeopleonyourLinux system: #fingerrich Login:richName:RichBlum Directory:/home/richShell:/bin/bash OnsinceThuSep2018:03(EDT)onpts/0from192.168.1.2 Nomail. NoPlan. # Note Becauseofsecurityconcerns,manyLinuxsystemadministratorsdisablethefinger commandontheirsystems,andmanyLinuxdistributionsdon’teveninstallitby default. Ifyouusethechfncommandwithnoparameters,itqueriesyoufortheappropriatevalues toenterintothecommentfield: #chfntest Changingfingerinformationfortest. Name[]:ImaTest Office[]:DirectorofTechnology OfficePhone[]:(123)555-1234 HomePhone[]:(123)555-9876 Fingerinformationchanged. #fingertest Login:testName:ImaTest Directory:/home/testShell:/bin/csh Office:DirectorofTechnologyOfficePhone:(123)555-1234 HomePhone:(123)555-9876 Neverloggedin. Nomail. NoPlan. # Ifyounowlookattheentryinthe/etc/passwdfile,itlookslikethis: #greptest/etc/passwd test:x:504:504:ImaTest,DirectorofTechnology,(123)5551234,(123)555-9876:/home/test:/bin/csh # Allthefingerinformationisneatlystoredawayinthe/etc/passwdfileentry. Finally, the chage command helps you manage the password aging process for user accounts.Youneedtosetseveralparameterstoindividualvalues,showninTable7.4. Table7.4ThechageCommandParameters Parameter -d -E -I -m -W Description Setsthenumberofdayssincethepasswordwaslastchanged Setsthedatethepasswordexpires Setsthenumberofdaysofinactivityafterthepasswordexpirestolockthe account Setstheminimumnumberofdaysbetweenpasswordchanges Setsthenumberofdaysbeforethepasswordexpiresthatawarningmessage appears Thechagedatevaluescanbeexpressedusingoneoftwomethods: AdateinYYYY-MM-DDformat AnumericalvaluerepresentingthenumberofdayssinceJanuary1,1970 Oneneatfeatureofthe chagecommandisthatitallowsyoutosetanexpirationdatefor anaccount.Usingthisfeature,youcancreatetemporaryuseraccountsthatautomatically expireonasetdate,withoutyourhavingtoremembertodeletethem!Expiredaccounts aresimilartolockedaccounts.Theaccountstillexists,buttheusercan’tloginwithit. UsingLinuxGroups User accounts are great for controlling security for individual users, but they aren’t so goodatallowinggroupsofuserstoshareresources.Toaccomplishthis,theLinuxsystem usesanothersecurityconcept,calledgroups. Group permissions allow multiple users to share a common set of permissions for an object on the system, such as a file, directory, or device (more on that later in the “DecodingFilePermissions”section). Linux distributions differ somewhat on how they handle default group memberships. Some Linux distributions create just one group that contains all the user accounts as members.YouneedtobecarefulifyourLinuxdistributiondoesthis,becauseyourfiles may be readable by all other users on the system. Other distributions create a separate groupaccountforeachusertoprovidealittlemoresecurity. Each group has a unique GID, which, like UIDs, is a unique numerical value on the system. Along with the GID, each group has a unique group name. You can use some grouputilitiestocreateandmanageyourowngroupsontheLinuxsystem.Thissection discusseshowgroupinformationisstoredandhowtousethegrouputilitiestocreatenew groupsandmodifyexistinggroups. The/etc/groupfile Just like user accounts, group information is stored in a file on the system. The /etc/group file contains information about each group used on the system. These are examplesfromatypical/etc/groupfileonaLinuxsystem: root:x:0:root bin:x:1:root,bin,daemon daemon:x:2:root,bin,daemon sys:x:3:root,bin,adm adm:x:4:root,adm,daemon rich:x:500: mama:x:501: katie:x:502: jessica:x:503: mysql:x:27: test:x:504: LikeUIDs,GIDsareassignedusingaspecialformat.Groupsusedforsystemaccountsare assigned GIDs below 500, and user groups are assigned GIDs starting at 500. The /etc/groupfileusesfourfields: Thegroupname Thegrouppassword TheGID Thelistofuseraccountsthatbelongtothegroup Thegrouppasswordallowsanon-groupmembertotemporarilybecomeamemberofthe groupbyusingthepassword.Thisfeatureisnotusedallthatcommonly,butitdoesexist. You should never add users to groups by editing the /etc/group file. Instead, use the usermod command (discussed earlier in the “Linux Security” section) to add a user account to a group. Before you can add users to different groups, you must create the groups. Note Thelistofuseraccountsissomewhatmisleading.You’llnoticethatthereareseveral groupsinthelistthatdon’thaveanyuserslisted.Thisisn’tbecausetheydon’thave anymembers.Whenauseraccountusesagroupasthedefaultgroupinthe /etc/passwdfile,theuseraccountdoesn’tappearinthe/etc/groupfileasa member.Thishascausedconfusionformorethanonesystemadministratoroverthe years! Creatingnewgroups Thegroupaddcommandallowsyoutocreatenewgroupsonyoursystem: #/usr/sbin/groupaddshared #tail/etc/group haldaemon:x:68: xfs:x:43: gdm:x:42: rich:x:500: mama:x:501: katie:x:502: jessica:x:503: mysql:x:27: test:x:504: shared:x:505: # When you create a new group, no users are assigned to it by default. The groupadd commanddoesn’tprovideanoptionforaddinguseraccountstothegroup.Instead,toadd newusers,usetheusermodcommand: #/usr/sbin/usermod-Gsharedrich #/usr/sbin/usermod-Gsharedtest #tail/etc/group haldaemon:x:68: xfs:x:43: gdm:x:42: rich:x:500: mama:x:501: katie:x:502: jessica:x:503: mysql:x:27: test:x:504: shared:x:505:rich,test # The shared group now has two members, test and rich. The -G parameter in usermod appendsthenewgrouptothelistofgroupsfortheuseraccount. Note Ifyouchangetheusergroupsforanaccountthatiscurrentlyloggedintothesystem, theusermustlogoutandthenlogbackinforthegroupchangestotakeeffect. Caution Becarefulwhenassigninggroupsforuseraccounts.Ifyouusethe-gparameter,the groupnameyouspecifyreplacesthedefaultgroupfortheuseraccount.The-G parameteraddsthegrouptothelistofgroupstheuserbelongsto,keepingthedefault groupintact. Modifyinggroups As you can see from the /etc/group file, you don’t need to modify much information about a group. The groupmod command allows you to change the GID (using the -g parameter)orthegroupname(usingthe-nparameter)ofanexistinggroup: #/usr/sbin/groupmod-nsharingshared #tail/etc/group haldaemon:x:68: xfs:x:43: gdm:x:42: rich:x:500: mama:x:501: katie:x:502: jessica:x:503: mysql:x:27: test:x:504: sharing:x:505:test,rich # Whenchangingthenameofagroup,theGIDandgroupmembersremainthesame,only thegroupnamechanges.BecauseallsecuritypermissionsarebasedontheGID,youcan changethenameofagroupasoftenasyouwishwithoutadverselyaffectingfilesecurity. DecodingFilePermissions Nowthatyouknowaboutusersandgroups,it’stimetodecodethecrypticfilepermissions you’ve seen when using the ls command. This section describes how to decipher the permissionsandwheretheycomefrom. Usingfilepermissionsymbols IfyourememberfromChapter3,the lscommandallowsyoutoseethefilepermissions forfiles,directories,anddevicesontheLinuxsystem: $ls-l total68 -rw-rw-r—1richrich502010-09-1307:49file1.gz -rw-rw-r—1richrich232010-09-1307:50file2 -rw-rw-r—1richrich482010-09-1307:56file3 -rw-rw-r—1richrich342010-09-1308:59file4 -rwxrwxr-x1richrich48822010-09-1813:58myprog -rw-rw-r—1richrich2372010-09-1813:58myprog.c drwxrwxr-x2richrich40962010-09-0315:12test1 drwxrwxr-x2richrich40962010-09-0315:12test2 $ Thefirstfieldintheoutputlistingisacodethatdescribesthepermissionsforthefilesand directories.Thefirstcharacterinthefielddefinesthetypeoftheobject: -forfiles dfordirectories lforlinks cforcharacterdevices bforblockdevices nfornetworkdevices Afterthat,youseethreesetsofthreecharacters.Eachsetofthreecharactersdefinesan accesspermissiontriplet: rforreadpermissionfortheobject wforwritepermissionfortheobject xforexecutepermissionfortheobject If a permission is denied, a dash appears in the location. The three sets relate the three levelsofsecurityfortheobject: Theowneroftheobject Thegroupthatownstheobject Everyoneelseonthesystem ThisisbrokendowninFigure7.1. Figure7.1TheLinuxfilepermissions Theeasiestwaytodiscussthisistotakeanexampleanddecodethefilepermissionsone byone: -rwxrwxr-x1richrich48822010-09-1813:58myprog Thefilemyproghasthefollowingsetsofpermissions: rwxforthefileowner(settotheloginnamerich) rwxforthefilegroupowner(settothegroupnamerich) r-xforeveryoneelseonthesystem Thesepermissionsindicatethattheuserloginnamerichcanread,write,andexecutethe file (considered full permissions). Likewise, members in the group rich can also read, write,andexecutethefile.However,anyoneelsenotintherichgroupcanonlyreadand executethefile;the w is replaced with a dash, indicating that write permissions are not assignedtothissecuritylevel. Defaultfilepermissions You may be wondering about where these file permissions come from. The answer is umask. The umask command sets the default permissions for any file or directory you create: $touchnewfile $ls-alnewfile -rw-r—r—1richrich0Sep2019:16newfile $ The touch command created the file using the default permissions assigned to my user account.Theumaskcommandshowsandsetsthedefaultpermissions: $umask 0022 $ Unfortunately, the umask command setting isn’t overtly clear, and trying to understand exactly how it works makes things even muddier. The first digit represents a special securityfeaturecalledthe stickybit.We’lltalkmoreaboutthatlateroninthischapter inthe“SharingFiles”section. The next three digits represent the octal values of the umask for a file or directory. To understandhowumaskworks,youfirstneedtounderstandoctalmodesecuritysettings. Octalmodesecuritysettingstakethethree rwxpermissionvaluesandconvertthemintoa 3-bitbinaryvalue,representedbyasingleoctalvalue.Inthebinaryrepresentation,each positionisabinarybit.Thus,ifthereadpermissionistheonlypermissionset,thevalue becomes r—, relating to a binary value of 100, indicating the octal value of 4. Table 7.5 showsthepossiblecombinationsyou’llruninto. Table7.5LinuxFilePermissionCodes Permissions Binary Octal Description – 000 0 Nopermissions —x 001 1 Execute-onlypermission -w010 2 Write-onlypermission -wx 011 3 Writeandexecutepermissions r— 100 4 Read-onlypermission r-x 101 5 Readandexecutepermissions rw110 6 Readandwritepermissions rwx 111 7 Read,write,andexecutepermissions Octal mode takes the octal permissions and lists three of them in order for the three securitylevels(user,group,andeveryone).Thus,theoctalmodevalue664representsread andwritepermissionsfortheuserandgroup,butread-onlypermissionforeveryoneelse. Nowthatyouknowaboutoctalmodepermissions,the umaskvaluebecomesevenmore confusing.TheoctalmodeshownforthedefaultumaskonmyLinuxsystemis0022,but thefileIcreatedhadanoctalmodepermissionof644.Howdidthathappen? Theumaskvalueisjustthat,amask.Itmasksoutthepermissionsyoudon’twanttogive tothesecuritylevel.Nowwehavetodiveintosomeoctalarithmetictofigureouttherest ofthestory. The umask value is subtracted from the full permission set for an object. The full permission for a file is mode 666 (read/write permission for all), but for a directory it’s 777(read/write/executepermissionforall). Thus, in the example, the file starts out with permissions 666, and the umask of 022 is applied,leavingafilepermissionof644. The umask value is normally set in the /etc/profile startup file in most Linux distributions(seeChapter6),butsomeprefertosetitinthe/etc/login.defsfile(suchas inUbuntu).Youcanspecifyadifferentdefaultumasksettingusingtheumaskcommand: $umask026 $touchnewfile2 $ls-lnewfile2 -rw-r–—1richrich0Sep2019:46newfile2 $ Bysettingtheumaskvalueto026,thedefaultfilepermissionsbecome640,sothenew filenowisrestrictedtoread-onlyforthegroupmembers,andeveryoneelseonthesystem hasnopermissionstothefile. Theumaskvaluealsoappliestomakingnewdirectories: $mkdirnewdir $ls-l drwxr-x—x2richrich4096Sep2020:11newdir/ $ Becausethedefaultpermissionsforadirectoryare777,theresultingpermissionsfromthe umaskaredifferentfromthoseofanewfile.The026umaskvalueissubtractedfrom777, leavingthe751directorypermissionsetting. ChangingSecuritySettings Ifyou’vealreadycreatedafileordirectoryandneedtochangethesecuritysettingsonit, Linuxhasafewdifferentutilitiesavailableforthis.Thissectionshowsyouhowtochange the existing permissions, the default owner, and the default group settings for a file or directory. Changingpermissions The chmodcommandallowsyoutochangethesecuritysettingsforfilesanddirectories. Theformatofthechmodcommandis: chmodoptionsmodefile The modeparameterallowsyoutosetthesecuritysettingsusingeitheroctalorsymbolic mode.Theoctalmodesettingsareprettystraightforward;justusethestandardthree-digit octalcodeyouwantthefiletohave: $chmod760newfile $ls-lnewfile -rwxrw–-1richrich0Sep2019:16newfile $ The octal file permissions are automatically applied to the file indicated. The symbolic modepermissionsarenotsoeasytoimplement. Instead of using the normal string of three sets of three characters, the chmod command takes a different approach. The following is the format for specifying a permission in symbolicmode: [ugoa…][[+-=][rwxXstugo…] Makesperfectlygoodsense,doesn’tit?Thefirstgroupofcharactersdefinestowhomthe newpermissionsapply: ufortheuser gforthegroup oforothers(everyoneelse) aforalloftheabove Next,asymbolisusedtoindicatewhetheryouwanttoaddthepermissiontotheexisting permissions (+), subtract the permission from the existing permission (−), or set the permissionstothevalue(=). Finally,thethirdsymbolisthepermissionusedforthesetting.Youmaynoticethatthere aremorethanthenormalrwxvalueshere.Thesearetheadditionalsettings: Xassignsexecutepermissionsonlyiftheobjectisadirectoryorifitalreadyhad executepermissions. ssetstheUIDorGIDonexecution. tsavesprogramtext. usetsthepermissionstotheowner’spermissions. gsetsthepermissionstothegroup’spermissions. osetsthepermissionstotheother’spermissions. Usingthesepermissionslookslikethis: $chmodo+rnewfile $ls-lFnewfile -rwxrw-r—1richrich0Sep2019:16newfile* $ The o+r entry adds the read permission to whatever permissions the everyone security levelalreadyhad. $chmodu-xnewfile $ls-lFnewfile -rw-rw-r—1richrich0Sep2019:16newfile $ The u-xentryremovestheexecutepermissionthattheuseralreadyhad.Notethatthe –F optionforthe lscommandindicateswhetherafilehasexecutionpermissionsbyadding anasterisktothefilename. Theoptionsparametersprovideafewadditionalfeaturestoaugmentthebehaviorofthe chmod command. The -R parameter performs the file and directory changes recursively. Youcanusewildcardcharactersforthefilenamespecified,changingthepermissionson multiplefileswithjustonecommand. Changingownership Sometimes, you need to change the owner of a file, such as when someone leaves an organization or a developer creates an application that needs to be owned by a system accountwhenit’sinproduction.Linuxprovidestwocommandsfordoingthat.The chown commandmakesiteasytochangetheownerofafile,andthechgrpcommandallowsyou tochangethedefaultgroupofafile. Theformatofthechowncommandis: chownoptionsowner[.group]file YoucanspecifyeithertheloginnameorthenumericUIDforthenewownerofthefile: #chowndannewfile #ls-lnewfile -rw-rw-r—1danrich0Sep2019:16newfile # Simple.Thechowncommandalsoallowsyoutochangeboththeuserandgroupofafile: #chowndan.sharednewfile #ls-lnewfile -rw-rw-r—1danshared0Sep2019:16newfile # Ifyoureallywanttogettricky,youcanjustchangethedefaultgroupforafile: #chown.richnewfile #ls-lnewfile -rw-rw-r—1danrich0Sep2019:16newfile # Finally, if your Linux system uses individual group names that match user login names, youcanchangebothwithjustoneentry: #chowntest.newfile #ls-lnewfile -rw-rw-r—1testtest0Sep2019:16newfile # Thechowncommandusesafewdifferentoptionparameters.The-Rparameterallowsyou tomakechangesrecursivelythroughsubdirectoriesandfiles,usingawildcardcharacter. The -hparameteralsochangestheownershipofanyfilesthataresymbolicallylinkedto thefile. Note Onlytherootusercanchangetheownerofafile.Anyusercanchangethedefault groupofafile,buttheusermustbeamemberofthegroupsthefileischangedfrom andto. The chgrpcommandprovidesaneasywaytochangejustthedefaultgroupforafileor directory: $chgrpsharednewfile $ls-lnewfile -rw-rw-r—1richshared0Sep2019:16newfile $ Theuseraccountmustownthefile,andbeamemberofthenewgroupaswelltobeable tochangethegroup.Nowanymemberinthesharedgroupcanwritetothefile.Thisis onewaytosharefilesonaLinuxsystem.However,sharingfilesamongagroupofpeople onthesystemcangettricky.Thenextsectiondiscusseshowtodothis. SharingFiles Asyou’veprobablyalreadyfiguredout,creatinggroupsisthewaytoshareaccesstofiles ontheLinuxsystem.However,foracompletefile-sharingenvironment,thingsaremore complicated. As you’ve already seen in the “Decoding File Permissions” section, when you create a new file, Linux assigns the file permissions of the new file using your default UID and GID.Toallowothersaccesstothefile,youneedtoeitherchangethesecuritypermissions for the everyone security group or assign the file a different default group that contains otherusers. This can be a pain in a large environment if you want to create and share documents amongseveralpeople.Fortunately,there’sasimplesolutionforhowtosolvethisproblem. TherearethreeadditionalbitsofinformationthatLinuxstoresforeachfileanddirectory: Thesetuserid(SUID):Whenafileisexecutedbyauser,theprogramrunsunder thepermissionsofthefileowner. Thesetgroupid(SGID):Forafile,theprogramrunsunderthepermissionsofthe filegroup.Foradirectory,newfilescreatedinthedirectoryusethedirectorygroup asthedefaultgroup. Thestickybit:Thefileremains(sticks)inmemoryaftertheprocessends. TheSGIDbitisimportantforsharingfiles.ByenablingtheSGIDbit,youcanforceall newfilescreatedinashareddirectorytobeownedbythedirectory’sgroupandnowthe individualuser’sgroup. The SGID is set using the chmod command. It’s added to the beginning of the standard three-digit octal value (making a four-digit octal value), or you can use the symbol sin symbolicmode. If you’re using octal mode, you’ll need to know the arrangement of the bits, shown in Table7.6. Table7.6ThechmodSUID,SGID,andStickyBitOctalValues Binary Octal Description 000 0 Allbitsarecleared. 001 1 Thestickybitisset. 010 2 TheSGIDbitisset. 011 3 TheSGIDandstickybitsareset. 100 4 TheSUIDbitisset. 101 5 TheSUIDandstickybitsareset. 110 6 TheSUIDandSGIDbitsareset. 111 7 Allbitsareset. So,tocreateashareddirectorythatalwayssetsthedirectorygroupforallnewfiles,all youneedtodoissettheSGIDbitforthedirectory: $mkdirtestdir $ls-l drwxrwxr-x2richrich4096Sep2023:12testdir/ $chgrpsharedtestdir $chmodg+stestdir $ls-l drwxrwsr-x2richshared4096Sep2023:12testdir/ $umask002 $cdtestdir $touchtestfile $ls-l total0 -rw-rw-r—1richshared0Sep2023:13testfile $ The first step is to create a directory that you want to share using the mkdircommand. Next,usethechgrpcommandtochangethedefaultgroupforthedirectorytoagroupthat containsthememberswhoneedtosharefiles(youmustbeamemberofthatgroupfor thistowork).Finally,settheSGIDbitforthedirectorytoensurethatanyfilescreatedin thedirectoryusethesharedgroupnameasthedefaultgroup. For this environment to work properly, all the group members must have their umask valuessettomakefileswritablebygroupmembers.Intheprecedingexample,the umask ischangedto002sothefilesarewritablebythegroup. Afterallthat’sdone,anymemberofthegroupcangototheshareddirectoryandcreatea new file. As expected, the new file uses the default group of the directory, not the user account’sdefaultgroup.Nowanyuserinthesharedgroupcanaccessthisfile. Summary This chapter discussed the command line commands you need to know to manage the Linuxsecurityonyoursystem.LinuxusesasystemofuserIDsandgroupIDstoprotect access to files, directories, and devices. Linux stores information about user accounts in the /etc/passwd file and information about groups in the /etc/group file. Each user is assignedauniquenumericuserID,alongwithatextloginnametoidentifytheuserinthe system. Groups are also assigned unique numerical group IDs and text group names. A groupcancontainoneormoreuserstoallowedsharedaccesstosystemresources. Several commands are available for managing user accounts and groups. The useradd commandallowsyoutocreatenewuseraccounts,andthegroupaddcommandallowsyou to create new group accounts. To modify an existing user account, use the usermod command.Similarly,usethegroupmodcommandtomodifygroupaccountinformation. Linux uses a complicated system of bits to determine access permissions for files and directories.Eachfilecontainsthreesecuritylevelsofprotection:thefile’sowner,adefault groupthathasaccesstothefile,andalevelforeveryoneelseonthesystem.Eachsecurity level is defined by three access bits: read, write, and execute. The combination of three bitsisoftenreferredtobythesymbolsrwx,forread,write,andexecute.Ifapermissionis denied,itssymbolisreplacedwithadash(suchasr—forread-onlypermission). The symbolic permissions are often referred to as octal values, with the three bits combinedintooneoctalvalueandthreeoctalvaluesrepresentingthethreesecuritylevels. Usetheumaskcommandtosetthedefaultsecuritysettingsforfilesanddirectoriescreated on the system. The system administrator normally sets a default umask value in the /etc/profile file, but you can use the umask command to change your umask value at anytime. Use the chmod command to change security settings for files and directories. Only the file’s owner can change permissions for a file or directory. However, the root user can changethesecuritysettingsforanyfileordirectoryonthesystem.Youcanusethechown andchgrpcommandstochangethedefaultownerandgroupofthefile. The chapter closed with a discussion on how to use the set GID bit to create a shared directory.TheSGIDbitforcesanynewfilesordirectoriescreatedinadirectorytousethe default group name of the parent directory, not that of the user who created them. This providesaneasywaytosharefilesbetweenusersonthesystem. Nowthatyou’reuptospeedwithfilepermissions,it’stimetotakeacloserlookathowto workwiththeactualfilesysteminLinux.Thenextchaptershowsyouhowtocreatenew partitionsinLinuxfromthecommandlineandthenhowtoformatthenewpartitionsso thattheycanbeusedintheLinuxvirtualdirectory. Chapter8 ManagingFilesystems InThisChapter 1. Understandingfilesystembasics 2. Exploringjournalingandcopy-on-writefilesystems 3. Managingfilesystems 4. Investigatingthelogicalvolumelayout 5. UsingtheLinuxLogicalVolumeManager Whenyou’reworkingwithyourLinuxsystem,oneofthedecisionsyou’llneedto makeiswhatfilesystemtouseforthestoragedevices.MostLinuxdistributions kindlyprovideadefaultfilesystemforyouatinstallationtime,andmostbeginning Linuxusersjustuseitwithoutgivingthetopicanotherthought. Althoughusingthedefaultfilesystemchoiceisn’tnecessarilyabadthing,sometimes ithelpstoknowtheotheroptionsavailabletoyou.Thischapterdiscussesthe differentfilesystemoptionsyouhaveavailableintheLinuxworldandshowsyou howtocreateandmanagethemfromtheLinuxcommandline. ExploringLinuxFilesystems Chapter 3 discussed how Linux uses a filesystem to store files and folders on a storage device.ThefilesystemprovidesawayforLinuxtobridgethegapbetweentheonesand zeroes stored in the hard drive and the files and folders you work with in your applications. Linuxsupportsseveraltypesoffilesystemstomanagefilesandfolders.Eachfilesystem implements the virtual directory structure on storage devices using slightly different features. This section walks you through the strengths and weaknesses of the more commonfilesystemsusedintheLinuxenvironment. UnderstandingthebasicLinuxfilesystems TheoriginalLinuxsystemusedasimplefilesystemthatmimickedthefunctionalityofthe Unixfilesystem.Thissectiondiscussestheevolutionofthatfilesystem. LookingattheextFilesystem TheoriginalfilesystemintroducedwiththeLinuxoperatingsystemiscalledtheextended filesystem(orjustextforshort).ItprovidesabasicUnix-likefilesystemforLinux,using virtual directories to handle physical devices, and storing data in fixed-length blocks on thephysicaldevices. Theextfilesystemusesasystemcalledinodestotrackinformationaboutthefilesstored inthevirtualdirectory.Theinodesystemcreatesaseparatetableoneachphysicaldevice, calledtheinodetable,tostorefileinformation.Eachstoredfileinthevirtualdirectoryhas anentryintheinodetable.Theextendedpartofthenamecomesfromtheadditionaldata thatittracksoneachfile,whichconsistsoftheseitems: Thefilename Thefilesize Theownerofthefile Thegroupthefilebelongsto Accesspermissionsforthefile Pointerstoeachdiskblockthatcontainsdatafromthefile Linux references each inode in the inode table using a unique number (called the inode number),assignedbythefilesystemasdatafilesarecreated.Thefilesystemusestheinode numbertoidentifythefileratherthanhavingtousethefullfilenameandpath. Lookingattheext2Filesystem The original ext filesystem had quite a few limitations, such as restraining files to only 2GB in size. Not too long after Linux was first introduced, the ext filesystem was upgradedtocreatethesecondextendedfilesystem,calledext2. As you can guess, the ext2 filesystem is an expansion of the basic abilities of the ext filesystem,butmaintainsthesamestructure.Theext2filesystemexpandstheinodetable formattotrackadditionalinformationabouteachfileonthesystem. Theext2inodetableaddsthecreated,modified,andlastaccessedtimevaluesforfilesto help system administrators track file access on the system. The ext2 filesystem also increasesthemaximumfilesizeallowedto2TB(theninlaterversionsofext2,thatwas increasedto32TB)tohelpaccommodatelargefilescommonlyfoundindatabaseservers. In addition to expanding the inode table, the ext2 filesystem also changed the way in whichfilesarestoredinthedatablocks.Acommonproblemwiththeextfilesystemwas thatasafileiswrittentothephysicaldevice,theblocksusedtostorethedatatendtobe scatteredthroughoutthedevice(calledfragmentation).Fragmentationofdatablockscan reducethefilesystemperformance,becauseittakeslongertosearchthestoragedeviceto accessalltheblocksforaspecificfile. Theext2filesystemhelpsreducefragmentationbyallocatingdiskblocksingroupswhen you save a file. By grouping the data blocks for a file, the filesystem doesn’t have to searchalloverthephysicaldeviceforthedatablockstoreadthefile. Theext2filesystemwasthedefaultfilesystemusedinLinuxdistributionsformanyyears, but it, too, had its limitations. The inode table, although a nice feature that allows the filesystemtotrackadditionalinformationaboutfiles,cancauseproblemsthatcanbefatal tothesystem.Eachtimethefilesystemstoresorupdatesafile,itmustmodifytheinode tablewiththenewinformation.Theproblemisthatthisisn’talwaysafluidaction. Ifsomethingshouldhappentothecomputersystembetweenthefilebeingstoredandthe inode table being updated, the two would become out of sync. The ext2 filesystem is notoriousforeasilybecomingcorruptedduetosystemcrashesandpoweroutages.Evenif the file data is stored just fine on the physical device, if the inode table entry isn’t completed,theext2filesystemdoesn’tevenknowthatthefileexisted! Itwasn’tlongbeforedeveloperswereexploringadifferentavenueofLinuxfilesystems. Understandingjournalingfilesystems Journaling filesystems provide a new level of safety to the Linux system. Instead of writing data directly to the storage device and then updating the inode table, journaling filesystemswritefilechangesintoatemporaryfile(calledthejournal)first.Afterdatais successfullywrittentothestoragedeviceandtheinodetable,thejournalentryisdeleted. Ifthesystemshouldcrashorsufferapoweroutagebeforethedatacanbewrittentothe storagedevice,thejournalingfilesystemjustreadsthroughthejournalfileandprocesses anyuncommitteddataleftover. Linuxcommonlyusesthreedifferentmethodsofjournaling,eachwithdifferentlevelsof protection.TheseareshowninTable8.1. Table8.1JournalingFilesystemMethods Method Data mode Description Bothinodeandfiledataarejournaled.Lowriskoflosingdata,butpoor performance. Ordered mode Onlyinodedataiswrittentothejournal,butnotremoveduntilfiledatais successfullywritten.Goodcompromisebetweenperformanceandsafety. Writeback Onlyinodedataiswrittentothejournal,nocontroloverwhenthefiledatais mode written.Higherriskoflosingdata,butstillbetterthannotusingjournaling. Thedatamodejournalingmethodisbyfarthesafestforprotectingdata,butitisalsothe slowest.Allthedatawrittentoastoragedevicemustbewrittentwice,oncetothejournal and again to the actual storage device. This can cause poor performance, especially for systemsthatdolotsofdatawriting. Over the years, a few different journaling filesystems have appeared in Linux. The followingsectionsdescribethepopularLinuxjournalingfilesystemsavailable. Lookingattheext3Filesystem Theext3filesystemwasaddedtotheLinuxkernelin2001,andupuntilrecentlywasthe defaultfilesystemusedbyjustaboutallLinuxdistributions.Itusesthesameinodetable structureastheext2filesystem,butaddsajournalfiletoeachstoragedevicetojournalthe datawrittentothestoragedevice. Bydefault,theext3filesystemusestheorderedmodemethodofjournaling,onlywriting the inode information to the journal file, but not removing it until the data blocks have been successfully written to the storage device. You can change the journaling method usedintheext3filesystemtoeitherdataorwritebackmodeswithasimplecommandline optionwhencreatingthefilesystem. Althoughtheext3filesystemaddedbasicjournalingtotheLinuxfilesystem,itstilllacked a few things. For example, the ext3 filesystem doesn’t provide any recovery from accidentaldeletionoffiles,nobuilt-indatacompressionisavailable(althoughapatchcan beinstalledseparatelythatprovidesthisfeature),andtheext3filesystemdoesn’tsupport encryptingfiles.Forthosereasons,developersintheLinuxprojectchosetocontinuework onimprovingtheext3filesystem. Lookingattheext4Filesystem The result of expanding the ext3 filesystem was (as you probably guessed) the ext4 filesystem.Theext4filesystemwasofficiallysupportedintheLinuxkernelin2008andis nowthedefaultfilesystemusedinpopularLinuxdistributions,suchasUbuntu. Inadditiontosupportingcompressionandencryption,theext4filesystemalsosupportsa featurecalledextents.Extentsallocatespaceonastoragedeviceinblocksandonlystore thestartingblocklocationintheinodetable.Thishelpssavespaceintheinodetableby nothavingtolistallthedatablocksusedtostoredatafromthefile. Theext4filesystemalsoincorporatesblockpreallocation.Ifyouwanttoreservespaceon a storage device for a file that you know will grow in size, with the ext4 filesystem it’s possibletoallocatealltheexpectedblocksforthefile,notjusttheblocksthatphysically exist.Theext4filesystemfillsinthereserveddatablockswithzeroesandknowsnotto allocatethemforanyotherfile. LookingattheReiserFilesystem In 2001, Hans Reiser created the first journaling filesystem for Linux, called ReiserFS. TheReiserFSfilesystemsupportsonlywritebackjournalingmode,writingonlytheinode tabledatatothejournalfile.Becauseitwritesonlytheinodetabledatatothejournal,the ReiserFSfilesystemisoneofthefasterLinuxjournalingfilesystems. TwointerestingfeaturesincorporatedintotheReiserFSfilesystemarethatyoucanresize anexistingfilesystemwhileit’sstillactiveandthatitusesatechniquecalledtailpacking, which stuffs data from one file into empty space in a data block from another file. The active filesystem resizing feature is great if you have to expand an already created filesystemtoaccommodatemoredata. TheReiserFSdevelopmentteambeganworkingonanewversioncalledReiser4in2004. The Reiser4 filesystem has several improvements over ResierFS, including extremely efficient handling of small files. However, most current mainstream Linux distributions don’tusetheReiser4filesystem.Yet,youmaystillrunintoaLinuxsystemthatemploys it. LookingattheJournaledFilesystem Possiblyoneoftheoldestjournalingfilesystemsaround,theJournaledFileSystem(JFS) was developed by IBM in 1990 for its AIX flavor of Unix. However, it wasn’t until its secondversionthatitwasportedtotheLinuxenvironment. Note TheofficialIBMnameofthesecondversionoftheJFSfilesystemisJFS2,butmost LinuxsystemsrefertoitasjustJFS. TheJFSfilesystemusestheorderedjournalingmethod,storingonlytheinodetabledata inthejournal,butnotremovingituntiltheactualfiledataiswrittentothestoragedevice. This method is a compromise between the speed of the Reiser4 and the integrity of the datamodejournalingmethod. TheJFSfilesystemusesextent-basedfileallocation,allocatingagroupofblocksforeach file written to the storage device. This method provides for less fragmentation on the storagedevice. OutsideoftheIBMLinuxofferings,theJFSfilesystemisn’tpopularlyused,butyoumay runintoitinyourLinuxjourney. LookingattheXFSFilesystem The XFS journaling filesystem is yet another filesystem originally created for a commercial Unix system that made its way into the Linux world. Silicon Graphics Incorporated(SGI)originallycreatedXFSin1994foritscommercialIRIXUnixsystem. It was released to the Linux environment for common use in 2002. The XFS filesystem has recently become more popular and is used as the default filesystem in mainstream Linuxdistributions,suchasRHEL. The XFS filesystem uses the writeback mode of journaling, which provides high performancebutdoesintroduceanamountofriskbecausetheactualdataisn’tstoredin thejournalfile.TheXFSfilesystemalsoallowsonlineresizingofthefilesystem,similar totheReiser4filesystem,exceptXFSfilesystemscanonlybeexpandedandnotshrunk. Understandingthecopy-on-writefilesystems Withjournaling,youmustchoosebetweensafetyandperformance.Althoughdatamode journalingprovidesthehighestsafety,performancesuffersbecausebothinodeanddatais journaled. With writeback mode journaling, performance is acceptable, but safety is compromised. For filesystems, an alternative to journaling is a technique called copy-on-write(COW). COW offers both safety and performance via snapshots. For modifying data, a cloneor writable-snapshotisused.Insteadofwritingmodifieddataovercurrentdata,themodified data is put in a new filesystem location. Even when data modification is completed, the olddataisneveroverwritten. COWfilesystemsaregaininginpopularity.Twoofthemostpopular,BtrfsandZFS,are brieflyreviewedinthefollowingsections. LookingattheZFSFilesystem The COW filesystem ZFS was developed in 2005 by Sun Microsystems for the OpenSolaris operating system. It began being ported to Linux in 2008 and was finally availableforLinuxproductionusein2012. ZFSisastablefilesystemandcompeteswellagainstResier4,Btrfs,andext4.Itsbiggest detractoristhatZFSdoesnothaveaGPLlicense.TheOpenZFSprojectwaslaunchedin 2013, which may help to change this situation. However, it’s possible that until a GPL licenseisobtained,ZFSwillneverbeadefaultLinuxfilesystem. LookingattheBtrfsFilesystem The COW newcomer is the Btrfs filesystem, also called the B-tree filesystem. Oracle started development on Btrfs in 2007. It was based on many of Reiser4’s features, but offeredimprovementsinreliability.Additionaldeveloperseventuallyjoinedinandhelped Btrfsquicklyrisetowardthetopofthepopularfilesystemslist.Thispopularityisdueto stability, ease of use, as well as the ability to dynamically resize a mounted filesystem. TheopenSUSELinuxdistributionrecentlyestablishedBtrfsasitsdefaultfilesystem.Itis also offered in other Linux distributions, such as RHEL, although not as the default filesystem. WorkingwithFilesystems Linuxprovidesafewdifferentutilitiesthatmakeiteasiertoworkwithfilesystemsfrom thecommandline.Youcanaddnewfilesystemsorchangeexistingfilesystemsfromthe comfort of your own keyboard. This section walks you through the commands for interactingwithfilesystemsfromacommandlineenvironment. Creatingpartitions Tostartout,youneedtocreateapartitiononthestoragedevicetocontainthefilesystem. The partition can be an entire disk or a subset of a disk that contains a portion of the virtualdirectory. Thefdiskutilityisusedtohelpyouorganizepartitionsonanystoragedeviceinstalledon the system. The fdisk command is an interactive program that allows you to enter commandstowalkthroughthestepsofpartitioningaharddrive. Tostartthe fdiskcommand,youneedtospecifythedevicenameofthestoragedevice you want to partition and you need to have superuser privileges. When you don’t have superuserprivilegesandattempttouse fdisk,you’llreceivesomesortoferrormessage, likethisone: $fdisk/dev/sdb Unabletoopen/dev/sdb $ Note Sometimes,thehardestpartofcreatinganewdiskpartitionistryingtofindthe physicaldiskonyourLinuxsystem.Linuxusesastandardformatforassigning devicenamestoharddrives,butyouneedtobefamiliarwiththeformat.Forolder IDEdrives,Linuxuses/dev/hdx,wherexisaletterbasedontheorderthedriveis detected(aforthefirstdrive,bforthesecond,andsoon).ForboththenewerSATA drivesandSCSIdrives,Linuxuses/dev/sdx,wherexisaletterbasedontheorder thedriveisdetected(again,aforthefirstdrive,bforthesecond,andsoon).It’s alwaysagoodideatodouble-checktomakesureyouarereferencingthecorrect drivebeforeformattingthepartition! If you do have superuser privileges and the correct device name, the fdisk command allowsyouentranceintotheutilityasdemonstratedhereonaCentOSdistribution: $sudofdisk/dev/sdb [sudo]passwordforChristine: DevicecontainsneitheravalidDOSpartitiontable, norSun,SGIorOSFdisklabel BuildinganewDOSdisklabelwithdiskidentifier0xd3f759b5. Changeswillremaininmemoryonly untilyoudecidetowritethem. Afterthat,ofcourse,thepreviouscontentwon’tberecoverable. Warning:invalidflag0x0000ofpartitiontable4will becorrectedbyw(rite) […] Command(mforhelp): Tip Ifthisisthefirsttimeyou’repartitioningthestoragedevice,fdiskgivesyoua warningthatapartitiontableisnotonthedevice. The fdisk interactive command prompt uses single letter commands to instruct fdisk whattodo.Table8.2showsthecommandsavailableatthefdiskcommandprompt. Table8.2ThefdiskCommands Command a b c d l m n o p q s t u v w x Description Togglesaflagindicatingifthepartitionisbootable EditsthedisklabelusedbyBSDUnixsystems TogglestheDOScompatibilityflag Deletesthepartition Liststheavailablepartitiontypes Displaysthecommandoptions Addsanewpartition CreatesaDOSpartitiontable Displaysthecurrentpartitiontable Quitswithoutsavingchanges CreatesanewdisklabelforSunUnixsystems ChangesthepartitionsystemID Changesthestorageunitsused Verifiesthepartitiontable Writesthepartitiontabletothedisk Advancedfunctions Althoughthislistmaylookintimidating,usuallyyouneedjustafewbasiccommandsin day-to-daywork. Forstarters,youcandisplaythedetailsofastoragedeviceusingthepcommand: Command(mforhelp):p Disk/dev/sdb:5368MB,5368709120bytes 255heads,63sectors/track,652cylinders Units=cylindersof16065*512=8225280bytes Sectorsize(logical/physical):512bytes/512bytes I/Osize(minimum/optimal):512bytes/512bytes Diskidentifier:0x11747e88 DeviceBootStartEndBlocksIdSystem Command(mforhelp): Theoutputshowsthatthestoragedevicehas5368MBofspaceonit(5GB).Thelisting under the storage device details shows whether there are any existing partitions on the device. The listing in this example doesn’t show any partitions, so the device is not partitionedyet. Next,you’llwanttocreateanewpartitiononthestoragedevice.Usethe ncommandfor that: Command(mforhelp):n Commandaction eextended pprimarypartition(1-4) p Partitionnumber(1-4):1 Firstcylinder(1-652,default1):1 Lastcylinder,+cylindersor+size{K,M,G}(1-652,default652):+2G Command(mforhelp): Partitions can be created as either a primarypartition or an extendedpartition. Primary partitions can be formatted with a filesystem directly, whereas extended partitions can onlycontainotherprimarypartitions.Thereasonforextendedpartitionsisthattherecan onlybefourpartitionsonasinglestoragedevice.Youcanextendthatbycreatingmultiple extended partitions and then creating primary partitions inside the extended partitions. This example creates a primary storage device, assigns it partition number 1, and then allocates 2GB of the storage device space to it. You can see the results using the p commandagain: Command(mforhelp):p Disk/dev/sdb:5368MB,5368709120bytes 255heads,63sectors/track,652cylinders Units=cylindersof16065*512=8225280bytes Sectorsize(logical/physical):512bytes/512bytes I/Osize(minimum/optimal):512bytes/512bytes Diskidentifier:0x029aa6af DeviceBootStartEndBlocksIdSystem /dev/sdb112622104483+83Linux Command(mforhelp): Nowintheoutputthere’sapartitionshownonthestoragedevice(called/dev/sdb1).The IdentrydefineshowLinuxtreatsthepartition.fdiskallowsyoutocreatelotsofpartition types. Using the l command lists the different types available. The default is type 83, which defines a Linux filesystem. If you want to create a partition for a different filesystem(suchasaWindowsNTFSpartition),justselectadifferentpartitiontype. Youcanrepeattheprocesstoallocatetheremainingspaceonthestoragedevicetoanother Linuxpartition.Afteryou’vecreatedthepartitionsyouwant,usethe wcommandtosave thechangestothestoragedevice: Command(mforhelp):w Thepartitiontablehasbeenaltered! Callingioctl()tore-readpartitiontable. Syncingdisks. $ Thestoragedevicepartitioninformationwaswrittentothepartitiontable,andLinuxwas informedofthenewpartitionviatheioctl()call.Nowthatyouhavesetupapartitionon thestoragedevice,you’rereadytoformatitwithaLinuxfilesystem. Tip Somedistributionsandolderdistributionversionsdonotautomaticallyinformyour Linuxsystemofanewpartitionafteritismade.Inthiscase,youneedtouseeither thepartprobeorhdparmcommand(seetheirmanpages),orrebootyoursystemsoit readstheupdatedpartitiontable Creatingafilesystem Beforeyoucanstoredataonthepartition,youmustformatitwithafilesystemsoLinux canuseit.Eachfilesystemtypeusesitsowncommandlineprogramtoformatpartitions. Table8.3liststheutilitiesusedforthedifferentfilesystemsdiscussedinthischapter. Table8.3CommandLineProgramstoCreateFilesystems Utility mkefs mke2fs mkfs.ext3 mkfs.ext4 mkreiserfs jfs_mkfs mkfs.xfs mkfs.zfs mkfs.btrfs Purpose Createsanextfilesystem Createsanext2filesystem Createsanext3filesystem Createsanext4filesystem CreatesaReiserFSfilesystem CreatesaJFSfilesystem CreatesanXFSfilesystem CreatesaZFSfilesystem CreatesaBtrfsfilesystem Not all filesystem utilities are installed by default. To determine whether you have a particularfilesystemutility,usethetypecommand: $typemkfs.ext4 mkfs.ext4is/sbin/mkfs.ext4 $ $typemkfs.btrfs -bash:type:mkfs.btrfs:notfound $ TheprecedingexampleonanUbuntusystemshowsthatthemkfs.ext4utilityisavailable. However,theBtrfsutilityisnot.SeeChapter9onhowtoinstalladditionalsoftwareand utilitiesonyourLinuxdistribution. Each filesystem utility command has lots of command line options that allow you to customizejusthowthefilesystemiscreatedinthepartition.Toseeallthecommandline options available, use the man command to display the manual pages for the filesystem command (see Chapter 3). All the filesystem commands allow you to create a default filesystemwithjustthesimplecommandwithnooptions: $sudomkfs.ext4/dev/sdb1 [sudo]passwordforChristine: mke2fs1.41.12(17-May-2010) Filesystemlabel= OStype:Linux Blocksize=4096(log=2) Fragmentsize=4096(log=2) Stride=0blocks,Stripewidth=0blocks 131648inodes,526120blocks 26306blocks(5.00%)reservedforthesuperuser Firstdatablock=0 Maximumfilesystemblocks=541065216 17blockgroups 32768blockspergroup,32768fragmentspergroup 7744inodespergroup Superblockbackupsstoredonblocks: 32768,98304,163840,229376,294912 Writinginodetables:done Creatingjournal(16384blocks):done Writingsuperblocksandfilesystemaccountinginformation:done Thisfilesystemwillbeautomaticallycheckedevery23mountsor 180days,whichevercomesfirst.Usetune2fs-cor-itooverride. $ The new filesystem uses the ext4 filesystem type, which is a journaling filesystem in Linux.Noticethatpartofthecreationprocesswastocreatethenewjournal. After you create the filesystem for a partition, the next step is to mount it on a virtual directorymountpointsoyoucanstoredatainthenewfilesystem.Youcanmountthenew filesystemanywhereinyourvirtualdirectorywhereyouneedtheextraspace. $ls/mnt $ $sudomkdir/mnt/my_partition $ $ls-al/mnt/my_partition/ $ $ls-dF/mnt/my_partition /mnt/my_partition/ $ $sudomount-text4/dev/sdb1/mnt/my_partition $ $ls-al/mnt/my_partition/ total24 drwxr-xr-x.3rootroot4096Jun1109:53. drwxr-xr-x.3rootroot4096Jun1109:58.. drwx––.2rootroot16384Jun1109:53lost+found $ The mkdircommand(Chapter3)createsthemountpointinthevirtualdirectory,andthe mountcommandaddsthenewharddrivepartitiontothemountpoint.The -toptionon themountcommandindicateswhatfilesystemtype,ext4,youaremounting.Nowyoucan savenewfilesandfoldersonthenewpartition! Note Thismethodofmountingafilesystemonlytemporarilymountsthefilesystem.When yourebootyourLinuxsystem,thefilesystemdoesn’tautomaticallymount.Toforce Linuxtoautomaticallymountthenewfilesystematboottime,addthenew filesystemtothe/etc/fstabfile. Nowthatthefilesystemismountedwithinthevirtualdirectorysystem,itcanstarttobe used on a regular basis. Unfortunately, with regular use comes the potential for serious problems,suchasfilesystemcorruption.Thenextsectionlooksathowtodealwiththese issues. Checkingandrepairingafilesystem Evenwithmodernfilesystems,thingscangowrongifpowerisunexpectedlylost,orifa wayward application locks up the system while file access is in progress. Fortunately, some command line tools are available to help you make an attempt to restore the filesystembacktoorder. Eachfilesystemhasitsownrecoverycommandforinteractingwiththefilesystem.That hasthepotentialofgettingugly,becausemoreandmorefilesystemsareavailableinthe Linux environment, making for lots of individual commands you have to know. Fortunately, a common front-end program available can determine the filesystem on the storage device and use the appropriate filesystem recovery command based on the filesystembeingrecovered. The fsck command is used to check and repair most Linux filesystem types, including onesdiscussedearlierinthischapter—ext,ext2,ext3,ext4,Reiser4,JFS,andXFS.The formatofthecommandis: fsckoptionsfilesystem Youcanlistmultiple filesystementriesonthecommandlinetocheck.Filesystemscan bereferencedusingeitherthedevicename,themountpointinthevirtualdirectory,ora specialLinuxUUIDvalueassignedtothefilesystem. Tip Althoughjournalingfilesystemsusersdoneedthefsckcommand,itisarguableasto whetherCOWfilesystemsusersdo.Infact,theZFSfilesystemdoesnotevenhavean interfacetothefsckutility Thefsckcommandusesthe/etc/fstabfiletoautomaticallydeterminethefilesystemon a storage device that’s normally mounted on the system. If the storage device isn’t normallymounted(suchasifyoujustcreatedafilesystemonanewstoragedevice),you needtousethe -tcommandlineoptiontospecifythefilesystemtype.Table8.4liststhe othercommandlineoptionsavailable. Table8.4ThefsckCommandLineOptions Option -a -A -C -N -r -R -s -t -T -V -y Description Automaticallyrepairsthefilesystemiferrorsaredetected Checksallthefilesystemslistedinthe/etc/fstabfile Displaysaprogressbarforfilesystemsthatsupportthatfeature(onlyext2and ext3) Doesn’trunthecheck,onlydisplayswhatcheckswouldbeperformed Promptstofixiferrorsfound Skipstherootfilesystemifusingthe-Aoption Ifcheckingmultiplefilesystems,performsthechecksoneatatime Specifiesthefilesystemtypetocheck Doesn’tshowtheheaderinformationwhenstarting Producesverboseoutputduringthechecks Automaticallyrepairsthefilesystemiferrorsdetected Youmaynoticethatsomeofthecommandlineoptionsareredundant.That’spartofthe problemoftryingtoimplementacommonfront-endformultiplecommands.Someofthe individual filesystem repair commands have additional options that can be used. If you need to do more advanced error checking, you’ll need to check the man pages for the individual filesystem repair tool to see if there are extended options specific to that filesystem. Tip Youcanrunthefsckcommandonunmountedfilesystemsonly.Formost filesystems,youcanjustunmountthefilesystemtocheckitandthenremountitwhen you’refinished.However,becausetherootfilesystemcontainsallthecoreLinux commandsandlogfiles,youcan’tunmountitonarunningsystem. ThisisatimewherehavingaLinuxLiveCDcomesinhandy!Justbootyoursystem withtheLiveCD,andthenrunthefsckcommandontherootfilesystem! Thischapterhasshowedhowtohandlefilesystemscontainedinphysicalstoragedevices. Linux also provides a couple of different ways to create logical storage devices for filesystems.Thenextsectionexamineshowyoucanusealogicalstoragedeviceforyour filesystems. ManagingLogicalVolumes If you create your filesystems using standard partitions on hard drives, trying to add additionalspacetoanexistingfilesystemcanbesomewhatofapainfulexperience.You canonlyexpandapartitiontotheextentoftheavailablespaceonthesamephysicalhard drive.Ifnomorespaceisavailableonthatharddrive,you’restuckhavingtogetalarger harddriveandmanuallymovingtheexistingfilesystemtothenewdrive. What would come in handy is a way to dynamically add more space to an existing filesystem by just adding a partition from another hard drive to the existing filesystem. TheLinuxLogicalVolumeManager(LVM)softwarepackageallowsyoutodojustthat.It providesaneasywayforyoutomanipulatediskspaceonaLinuxsystemwithouthaving torebuildentirefilesystems. Exploringlogicalvolumemanagementlayout The core of logical volume management is how it handles the physical hard drive partitions installed on the system. In the logical volume management world, hard drives arecalledphysicalvolumes(PV).EachPVmapstoaspecificphysicalpartitioncreatedon aharddrive. Multiple PV elements are pooled together to create a volume group (VG). The logical volumemanagementsystemtreatstheVGlikeaphysicalharddrive,butinrealitytheVG may consist of multiple physical partitions spread across multiple hard drives. The VG providesaplatformtocreatethelogicalpartitions,whichactuallycontainthefilesystem. The final layer in the structure is the logicalvolume (LV). The LV creates the partition environment for Linux to create a filesystem, acting similar to a physical hard disk partitionasfarasLinuxisconcerned.TheLinuxsystemtreatstheLVjustlikeaphysical partition.YoucanformattheLVusinganyoneofthestandardLinuxfilesystemsandthen addittotheLinuxvirtualdirectoryatamountpoint. Figure 8.1 shows the basic layout of a typical Linux logical volume management environment. Figure8.1Thelogicalvolumemanagementenvironment Thevolumegroup,showninFigure8.1,spansacrossthreeseparatephysicalharddrives, whichcontainfiveseparatephysicalpartitions.Insidethevolumegrouparetwoseparate logical volumes. The Linux system treats each logical volume just like a physical partition.Eachlogicalvolumecanbeformattedasanext4filesystemandthenmountedto aspecificlocationinthevirtualdirectory. Notice in Figure 8.1 that the third physical hard drive has an unused partition. Using logical volume management, you can easily assign this unused partition to the existing volumegroupatalatertime,andtheneitheruseittocreateanewlogicalvolumeoraddit toexpandoneoftheexistinglogicalvolumeswhenyouneedmorespace. Likewise,ifyouaddanewharddrivetothesystem,thelocalvolumemanagementsystem allowsyoutoaddittotheexistingvolumegroup,andthencreatemorespaceforoneof theexistinglogicalvolumes,orstartanewlogicalvolumetobemounted.That’samuch betterwayofhandlingexpandingfilesystems! UsingtheLVMinLinux The Linux LVM was developed by Heinz Mauelshagen and released to the Linux community in 1998. It allows you to manage a complete logical volume management environmentinLinuxusingsimplecommandlinecommands. TwoversionsofLinuxLVMareavailable: LVM1:TheoriginalLVMpackagereleasedin1998andavailableinonlythe2.4 Linuxkernels.Itprovidesonlybasiclogicalvolumemanagementfeatures. LVM2:AnupdatedversionoftheLVM,availableinthe2.6Linuxkernels.It providesadditionalfeaturesoverthestandardLVM1features. MostmodernLinuxdistributionsusingthe2.6kernelversionoraboveprovidesupportfor LVM2.Besidesthestandardlogicalvolumemanagementfeatures,LVM2providesafew othernicethingsforyoutouseinyourLinuxsystem. TakingaSnapshot TheoriginalLinuxLVMallowsyoutocopyanexistinglogicalvolumetoanotherdevice whilethelogicalvolumeisactive.Thisfeatureiscalledasnapshot.Snapshotsaregreat forbackingupimportantdatathatcan’tbelockedduetohighavailabilityrequirements. Traditional backup methods usually lock files as they’re being copied to the backup media. The snapshot allows you to continue running mission critical web or database serverswhileperformingthecopy.Unfortunately,LVM1allowsyoutocreateonlyareadonlysnapshot.Afteryoucreatethesnapshot,youcan’twritetoit. LVM2 allows you to create a read-write snapshot of an active logical volume. With the read-writecopy,youcanremovetheoriginallogicalvolumeandmountthesnapshotasa replacement.Thisfeatureisgreatforfastfail-oversorforexperimentingwithapplications thatmodifydatathatmayneedtoberestoredifsomethingfails. Striping AnotherinterestingfeaturethatLVM2providesisstriping.Withstriping,alogicalvolume iscreatedacrossmultiplephysicalharddrives.WhentheLinuxLVMwritesafiletothe logicalvolume,thedatablocksinthefilearespreadacrossthemultipleharddrives.Each successiveblockofdataiswrittentothenextharddrive. Striping helps improve disk performance, because Linux can write the multiple data blocksforafiletothemultipleharddrivessimultaneously,ratherthanhavingtowaitfora single hard drive to move the read/write head to different locations. This improvement also applies to reading sequentially accessed files, because the LVM can read data from themultipleharddrivessimultaneously. Note LVMstripingisnotthesameasRAIDstriping.LVMstripingdoesn’tprovidea parityentry,whichcreatesthefault-tolerantenvironment.Infact,LVMstripingmay increasethechanceofafilebeinglostduetoaharddrivefailure.Asingledisk failurecanresultinmultiplelogicalvolumesbeinginaccessible. Mirroring Just because you install a filesystem using LVM doesn’t mean that things can’t still go wrong in the filesystem. Just as in a physical partition, LVM logical volumes are susceptibletopoweroutagesanddiskcrashes.Afterafilesystembecomescorrupt,there’s alwaysapossibilitythatyouwon’tbeabletorecoverit. TheLVMsnapshotprocessprovidessomecomfortknowingthatyoucancreateabackup copyofalogicalvolumeatanytime,butforsomeenvironmentsthatmaynotbeenough. Systemsthathavelotsofdatachanges,suchasdatabaseservers,maystorehundredsor thousandsofrecordssincethelastsnapshot. A solution to this problem is the LVM mirror. A mirror is a complete copy of a logical volume that’s updated in real time. When you create the mirror logical volume, LVM synchronizestheoriginallogicalvolumetothemirrorcopy.Dependingonthesizeofthe originallogicalvolume,thismaytakesometimetocomplete. Aftertheoriginalsynchronizationiscomplete,LVMperformstwowritesforeachwrite processinthefilesystem—onetothemainlogicalvolumeandonetothemirroredcopy. As you can guess, this process does slow down write performance on the system. However,iftheoriginallogicalvolumeshouldbecomecorruptforsomereason,youhave acompleteup-to-datecopyatyourfingertips! UsingtheLinuxLVM Now that you’ve seen what the Linux LVM can do, this section discusses how to implement it to help organize the disk space on your system. The Linux LVM package only provides command line programs for creating and managing all the components in thelogicalvolumemanagementsystem.SomeLinuxdistributionsincludegraphicalfrontendstothecommandlinecommands,butforcompletecontrolofyourLVMenvironment, it’sbesttogetcomfortableworkingdirectlywiththecommands. DefiningPhysicalVolumes The first step in the process is to convert the physical partitions on the hard drive into physicalvolumeextentsusedbytheLinuxLVM.Ourfriendthefdiskcommandhelpsus here.AftercreatingthebasicLinuxpartition,youneedtochangethepartitiontypeusing thetcommand: […] Command(mforhelp):t Selectedpartition1 Hexcode(typeLtolistcodes):8e Changedsystemtypeofpartition1to8e(LinuxLVM) Command(mforhelp):p Disk/dev/sdb:5368MB,5368709120bytes 255heads,63sectors/track,652cylinders Units=cylindersof16065*512=8225280bytes Sectorsize(logical/physical):512bytes/512bytes I/Osize(minimum/optimal):512bytes/512bytes Diskidentifier:0xa8661341 DeviceBootStartEndBlocksIdSystem /dev/sdb112622104483+8eLinuxLVM Command(mforhelp):w Thepartitiontablehasbeenaltered! Callingioctl()tore-readpartitiontable. Syncingdisks. $ The8epartitiontypedenotesthatthepartitionwillbeusedaspartofaLinuxLVMsystem andnotasadirectfilesystem,asyousawwiththe83partitiontypeearlier. Note Ifthepvcreatecommandinthenextstepdoesnotworkforyou,it’smostlikelydue totheLVM2packagenotbeinginstalledbydefault.Toinstallthepackage,usethe packagenamelvm2andseeChapter9forhowtoinstallsoftwarepackages. Thenextstepistousethepartitiontocreatetheactualphysicalvolume.That’sdoneusing the pvcreatecommand.The pvcreatecommanddefinesthephysicalpartitiontousefor thePV.ItsimplytagsthepartitionasaphysicalvolumeintheLinuxLVMsystem: $sudopvcreate/dev/sdb1 dev_is_mpath:failedtogetdevicefor8:17 Physicalvolume“/dev/sdb1”successfullycreated $ Note Don’tletthedauntingmessagedev_is_mpath:failedtogetdevicefor8:17 orsimilarmessagesfrightenyou.Aslongasyoureceivethesuccessfullycreated message,alliswell.Thepvcreatecommandcheckstoseewhetherthepartitionisa multi-path(mpath)device.Ifitisnot,itissuesthedauntingmessage. Youcanusethepvdisplaycommandtodisplayalistofphysicalvolumesyou’vecreated ifyou’dliketoseeyourprogressalongtheway: $sudopvdisplay/dev/sdb1 “/dev/sdb1”isanewphysicalvolumeof“2.01GiB” –NEWPhysicalvolume– PVName/dev/sdb1 VGName PVSize2.01GiB AllocatableNO PESize0 TotalPE0 FreePE0 AllocatedPE0 PVUUID0FIuq2-LBod-IOWt-8VeN-tglm-Q2ik-rGU2w7 $ The pvdisplaycommandshowsthat /dev/sdb1isnowtaggedasaPV.Notice,however, thatintheoutput,theVGNameisblank.ThePVdoesnotyetbelongtoavolumegroup. CreatingVolumeGroups The next step in the process is to create one or more volume groups from the physical volumes.Therearenosetrulesforhowmanyvolumegroupsyouneedtocreateforyour system—youcanaddalltheavailablephysicalvolumestoasinglevolumegroup,oryou cancreatemultiplevolumegroupsbycombiningdifferentphysicalvolumes. To create the volume group from the command line, you need to use the vgcreate command.Thevgcreatecommandrequiresafewcommandlineparameterstodefinethe volumegroupname,aswellasthenameofthephysicalvolumesyou’reusingtocreate thevolumegroup: $sudovgcreateVol1/dev/sdb1 Volumegroup“Vol1”successfullycreated $ That’s not all too exciting for output! If you’d like to see some details about the newly createdvolumegroup,usethevgdisplaycommand: $sudovgdisplayVol1 –Volumegroup– VGNameVol1 SystemID Formatlvm2 MetadataAreas1 MetadataSequenceNo1 VGAccessread/write VGStatusresizable MAXLV0 CurLV0 OpenLV0 MaxPV0 CurPV1 ActPV1 VGSize2.00GiB PESize4.00MiB TotalPE513 AllocPE/Size0/0 FreePE/Size513/2.00GiB VGUUIDoe4I7e-5RA9-G9ti-ANoI-QKLz-qkX4-58Wj6e $ Thisexamplecreatesavolumegroupnamed Vol1,usingthephysicalvolumecreatedon the/dev/sdb1partition. Nowthatyouhaveoneormorevolumegroupscreated,you’rereadytocreatethelogical volume. CreatingLogicalVolumes ThelogicalvolumeiswhattheLinuxsystemusestoemulateaphysicalpartition,andit holds the filesystem. The Linux system handles the logical volumes just like a physical partition, allowing you to define filesystems in the logical volume and then mount the filesystemintothevirtualdirectory. Tocreatethelogicalvolume,usethe lvcreatecommand.Althoughyoucanusuallyget away without using command line options in the other Linux LVM commands, the lvcreate command requires at least some options to be entered. Table 8.5 shows the availablecommandlineoptions. Table8.5ThelvcreateOptions Option Long Option Name Description -c —chunksize Specifiesthechunksizeofthesnapshotlogicalvolume -C — contiguous Setsorresetsthecontiguousallocationpolicy -i -I -l -L -m Specifiesthenumberofstripes —stripsize Specifiesthesizeofeachstripe Specifiesthenumberoflogicalextentstoallocatetoanewlogical —extents volumeorthepercentofthelogicalextentstouse —size Specifiesthedisksizetoallocatetoanewlogicalvolume —minor Specifiestheminornumberofthedevice —mirrors Createsamirroredlogicalvolume —stripes -M — persistent Makestheminornumberpersistent -n —name Specifiesthenameofthenewlogicalvolume -p — permission Setsread/writepermissionforthelogicalvolume -r —readahead Setsthereadaheadsectorcount -R — regionsize Specifiesthesizetodividethemirrorregionsinto -s —snapshot -Z —zero Createsasnapshotlogicalvolume Setsthefirst1KBofdataonthenewlogicalvolumetozeros Although the command line options may look intimidating, for most situations, you can getbywithaminimalamountofoptions: $sudolvcreate-l100%FREE-nlvtestVol1 Logicalvolume“lvtest”created $ Ifyouwanttoseethedetailsofwhatyoucreated,usethelvdisplaycommand: $sudolvdisplayVol1 –Logicalvolume– LVPath/dev/Vol1/lvtest LVNamelvtest VGNameVol1 LVUUID4W2369-pLXy-jWmb-lIFN-SMNX-xZnN-3KN208 LVWriteAccessread/write LVCreationhost,time…-0400 LVStatusavailable #open0 LVSize2.00GiB CurrentLE513 Segments1 Allocationinherit Readaheadsectorsauto -currentlysetto256 Blockdevice253:2 $ Nowyoucanseejustwhatyoucreated!Noticethatthevolumegroupname(Vol1)isused toidentifythevolumegrouptousewhencreatingthenewlogicalvolume. The-lparameterdefineshowmuchoftheavailablespaceonthevolumegroupspecified touseforthelogicalvolume.Noticethatyoucanspecifythevalueasapercentofthefree space in the volume group. This example used all (100%) of the free space for the new logicalvolume. Youcanusethe -lparametertospecifythesizeasapercentageoftheavailablespaceor the -Lparametertospecifytheactualsizeinbytes,kilobytes(KB),megabytes(MB),or gigabytes(GB).The -n parameter allows you to provide a name for the logical volume (calledlvtestinthisexample). CreatingtheFilesystem After you run the lvcreate command, the logical volume exists but doesn’t have a filesystem. To do that, you need to use the appropriate command line program for the filesystemyouwanttocreate: $sudomkfs.ext4/dev/Vol1/lvtest mke2fs1.41.12(17-May-2010) Filesystemlabel= OStype:Linux Blocksize=4096(log=2) Fragmentsize=4096(log=2) Stride=0blocks,Stripewidth=0blocks 131376inodes,525312blocks 26265blocks(5.00%)reservedforthesuperuser Firstdatablock=0 Maximumfilesystemblocks=541065216 17blockgroups 32768blockspergroup,32768fragmentspergroup 7728inodespergroup Superblockbackupsstoredonblocks: 32768,98304,163840,229376,294912 Writinginodetables:done Creatingjournal(16384blocks):done Writingsuperblocksandfilesystemaccountinginformation:done Thisfilesystemwillbeautomaticallycheckedevery28mountsor 180days,whichevercomesfirst.Usetune2fs-cor-itooverride. $ After you’ve created the new filesystem, you can mount the volume in the virtual directoryusingthestandardLinuxmountcommand,justasifitwereaphysicalpartition. Theonlydifferenceisthatyouuseaspecialpaththatidentifiesthelogicalvolume: $sudomount/dev/Vol1/lvtest/mnt/my_partition $ $mount /dev/mapper/vg_server01-lv_rooton/typeext4(rw) […] /dev/mapper/Vol1-lvteston/mnt/my_partitiontypeext4(rw) $ $cd/mnt/my_partition $ $ls-al total24 drwxr-xr-x.3rootroot4096Jun1210:22. drwxr-xr-x.3rootroot4096Jun1109:58.. drwx––.2rootroot16384Jun1210:22lost+found $ Notice that the path used in both the mkfs.ext4 and mount commands is a little odd. Insteadofaphysicalpartitionpath,thepathusesthevolumegroupname,alongwiththe logicalvolumename.Afterthefilesystemismounted,youcanaccessthenewareainthe virtualdirectory. ModifyingtheLVM BecausethebenefitofusingtheLinuxLVMistodynamicallymodifyfilesystems,you’d expectthatsometoolswouldallowyoutodothat.SometoolsareavailableinLinuxthat allowyoutomodifytheexistinglogicalvolumemanagementconfiguration. If you don’t have access to a fancy graphical interface for managing your Linux LVM environment,allisnotlost.You’vealreadyseensomeoftheLinuxLVMcommandline programsinactioninthischapter.Youcanuseahostofothercommandlineprogramsto manage the LVM setup after you’ve installed it. Table8.6 lists the common commands thatareavailableintheLinuxLVMpackage. Table8.6TheLinuxLVMCommands Command vgchange vgremove vgextend vgreduce lvextend lvreduce Function Activatesanddeactivatesavolumegroup Removesavolumegroup Addsphysicalvolumestoavolumegroup Removesphysicalvolumesfromavolumegroup Increasesthesizeofalogicalvolume Decreasesthesizeofalogicalvolume Using these command line programs, you have full control over your Linux LVM environment. Tip Becarefulwhenmanuallyincreasingordecreasingthesizeofalogicalvolume.The filesystemstoredinthelogicalvolumemustbemanuallyfixedtohandlethechange insize.Mostfilesystemsincludecommandlineprogramsforreformattingthe filesystem,suchastheresize2fsprogramfortheext2,ext3,andext4filesystems. Summary Working with storage devices in Linux requires that you know a little bit about filesystems. Knowing how to create and work with filesystems from the command line cancomeinhandyasyouworkonLinuxsystems.Thischapterdiscussedhowtohandle filesystemsfromtheLinuxcommandline. TheLinuxsystemisdifferentfromWindowsinthatitsupportslotsofdifferentmethods for storing files and folders. Each filesystem method has different features that make it ideal for different situations. Also, each filesystem method uses different commands for interactingwiththestoragedevice. Beforeyoucaninstallafilesystemonastoragedevice,youmustfirstpreparethedevice. The fdisk command is used to partition storage devices to get them ready for the filesystem. When you partition the storage device, you must define what type of filesystemwillbeusedonit. Afteryoupartitionastoragedevice,youcanuseoneofseveraldifferentfilesystemsfor thepartition.PopularLinuxfilesystemsincludeext4andXFS.Bothofthesefilesystems providejournalingfilesystemfeatures,makingthemlesspronetoerrorsandproblemsif theLinuxsystemshouldcrash. Onelimitingfactortocreatingfilesystemsdirectlyonastoragedevicepartitionisthatyou can’teasilychangethesizeofthefilesystemifyourunoutofdiskspace.However,Linux supports logical volume management, a method of creating virtual partitions across multiplestoragedevices.Thismethodallowsyoutoeasilyexpandanexistingfilesystem withouthavingtocompletelyrebuildit.TheLinuxLVMpackageprovidescommandline commands to create logical volumes across multiple storage devices on which to build filesystems. Nowthatyou’veseenthecoreLinuxcommandlinecommands,it’sclosetothetimeto start creating some shell script programs. However, before you start coding, we need to discussanotherelement:installingsoftware.Ifyouplantowriteshellscripts,youneedan environment in which to create your masterpieces. The next chapter discusses how to install and manage software packages from the command line in different Linux environments. Chapter9 InstallingSoftware InThisChapter 1. Installingsoftware 2. UsingDebianpackages 3. WorkingwithRedHatpackages IntheolddaysofLinux,installingsoftwarecouldbeapainfulexperience. Fortunately,theLinuxdevelopershavemadelifealittleeasierforusbybundling softwareintopre-builtpackagesthataremucheasiertoinstall.However,youstill havealittleworktodotogetthesoftwarepackagesinstalled,especiallyifyouwant todothatfromthecommandline.ThischapterlooksatthevariousPackage ManagementSystemsavailableinLinuxandthecommandlinetoolsusedfor softwareinstallation,management,andremoval. PackageManagementPrimer Before diving into the world of Linux software package management, this chapter goes throughafewofthebasicsfirst.EachofthemajorLinuxdistributionsutilizessomeform ofaPackageManagementSystem(PMS)tocontrolinstallingsoftwareapplicationsand libraries.APMSutilizesadatabasethatkeepstrackoftheseitems: WhatsoftwarepackagesareinstalledontheLinuxsystem Whatfileshavebeeninstalledforeachpackage Versionsofeachofthesoftwarepackagesinstalled Softwarepackagesarestoredonservers,calledrepositories,andareaccessedacrossthe Internet via PMS utilities running on your local Linux system. You can use the PMS utilitiestosearchfornewsoftwarepackagesorevenupdatestosoftwarepackagesalready installedonthesystem. Asoftwarepackageoftenhasdependenciesorotherpackagesthatmustbeinstalledfirst forthesoftwaretorunproperly.ThePMSutilitiesdetectthesedependenciesandofferto installanyadditionallyneededsoftwarepackagesbeforeinstallingthedesiredpackage. The downside to PMS is that there isn’t a single standard utility. Whereas all the bash shellcommandsdiscussedsofarinthisbookworknomatterwhichLinuxdistributionyou use,thisisnottruewithsoftwarepackagemanagement. ThePMSutilitiesandtheirassociatedcommandsarevastlydifferentbetweenthevarious Linux distributions. The two primary PMS base utilities commonly used in the Linux worldaredpkgandrpm. Debian-baseddistributionssuchasUbuntuandLinuxMintuse,atthebaseoftheirPMS utilities,the dpkgcommand.ThiscommandinteractsdirectlywiththePMSontheLinux systemandisusedforinstalling,managing,andremovingsoftwarepackages. TheRedHat–baseddistributions,suchasFedora,openSUSE,andMandriva,usethe rpm commandatthebaseoftheirPMS.Similartothe dpkgcommand,the rpmcommandcan listinstalledpackages,installnewpackages,andremoveexistingsoftware. NotethatthesetwocommandsarethecoreoftheirrespectivePMS,nottheentirePMS itself. Many Linux distributions that use the dpkg or rpm methods have built additional specialtyPMSutilitiesuponthesebasecommandstohelpmakeyourlifemucheasier.The following sections walk through various PMS utility commands you’ll run into in the popularLinuxdistributions. TheDebian-BasedSystems The dpkgcommandisatthecoreoftheDebian-basedfamilyofPMStools.Theseother toolsareincludedinthisPMS: apt-get apt-cache aptitude Byfarthemostcommoncommandlinetoolisaptitude,andforgoodreason.Theaptitude toolisessentiallyafront-endforboththeapttoolsanddpkg.WhereasdpkgisaPMStool, aptitudeisacompletePackageManagementSystem. Using the aptitude command at the command line helps you avoid common software installation problems, such as missing software dependencies, unstable system environments,andjustawholelotofunnecessaryhassle.Thissectionlooksathowtouse theaptitudecommandtoolfromtheLinuxcommandline. Managingpackageswithaptitude AcommontaskfacedbyLinuxsystemadministratorsistodeterminewhatpackagesare alreadyinstalledonthesystem.Fortunately,aptitudehasahandyinteractiveinterfacethat makesthistaskaneasyone. If you have aptitude installed in your Linux distribution, at the shell prompt just type aptitudeandpressEnter.Youarethrownintoaptitude’sfull-screenmode,asyoucansee inFigure9.1. Figure9.1Theaptitudemainwindow Use the arrow keys to maneuver around the menu. Select the menu option Installed Packages to see what packages are installed. You will see several groups of software packages,suchaseditors,andsoon.Anumberinparenthesesfollowseachgroup,which indicatesthenumberofpackagesthegroupcontains. Use the arrow keys to highlight a group, and press Enter to see each subgroup of packages. You then see the individual package names and their version numbers. Press Enter on individual packages to get very detailed information, such as the package’s description,homepage,size,maintainer,andsoon. Whenyou’refinishedviewingtheinstalledpackages,press qtoquitthedisplay.Youcan thengobacktothearrowkeys.anduseEntertotoggleopenorclosedthepackagesand theirsubgroups.Whenyouareallfinished,justpress qmultipletimesuntilyoureceive thepop-upscreen“ReallyquitAptitude?” If you already know the packages on your system and want to quickly display detailed information about a particular package, you don’t need to go into aptitude’s interactive interface.Youcanuseaptitudeasasinglecommandatthecommandline: aptitudeshowpackage_name Here’sanexampleofdisplayingthedetailsofthepackagemysql-client: $aptitudeshowmysql-client Package:mysql-client State:notinstalled Version:5.5.38-0ubuntu0.14.04.1 Priority:optional Section:database Maintainer:UbuntuDevelopers<[email protected]> Architecture:all UncompressedSize:129k Depends:mysql-client-5.5 Providedby:mysql-client-5.5 Description:MySQLdatabaseclient(metapackagedependingonthelatest version) Thisisanemptypackagethatdependsonthecurrent”best”versionof mysql-client(currentlymysql-client-5.5),asdeterminedbytheMySQL maintainers.InstallthispackageifindoubtaboutwhichMySQLversion you want,asthisistheoneconsideredtobeinthebestshapebythe Maintainers. Homepage:http://dev.mysql.com/ $ Note Theaptitudeshowcommandindicatesthatthepackageisnotinstalledonthe system.Italsoshowsdetailedpackageinformationfromthesoftwarerepository. One detail you cannot get with aptitude is a listing of all the files associated with a particularsoftwarepackage.Togetthislist,youmustgotothedpkgtoolitself: dpkg-Lpackage_name Here’s an example of using dpkg to list all the files installed as part of the vim-common package: $ $dpkg-Lvim-common /. /usr /usr/bin /usr/bin/xxd /usr/bin/helpztags /usr/lib /usr/lib/mime /usr/lib/mime/packages /usr/lib/mime/packages/vim-common /usr/share /usr/share/man /usr/share/man/ru /usr/share/man/ru/man1 /usr/share/man/ru/man1/vim.1.gz /usr/share/man/ru/man1/vimdiff.1.gz /usr/share/man/ru/man1/xxd.1.gz /usr/share/man/it /usr/share/man/it/man1 […] $ Youcanalsodothereverse—findwhatpackageaparticularfilebelongsto: dpkg—searchabsolute_file_name Notethatyouneedtouseanabsolutefilereferenceforthistowork: $ $dpkg—search/usr/bin/xxd vim-common:/usr/bin/xxd $ The output shows the /usr/bin/xxd file was installed as part of the vim-common package. Installingsoftwarepackageswithaptitude Nowthatyouknowmoreaboutlistingsoftwarepackageinformationonyoursystem,this section walks through a software package install. First, you’ll want to determine the package name to install. How do you find a particular software package? Use the aptitudecommandwiththesearchoption: aptitudesearchpackage_name The beauty of the search option is that you do not need to insert wildcards around package_name.Wildcardsareimplied.Here’sanexampleofusing aptitudetolookfor thewinesoftwarepackage: $ $aptitudesearchwine pgnome-wine-icon-theme-redvariationoftheGNOME-… vlibkwineffects1-apiplibkwineffects1a-libraryusedbyeffects… pq4wine-Qt4GUIforwine(W.I.N.E) pshiki-wine-theme-redvariationoftheShiki-… pwine-MicrosoftWindowsCompatibility… pwine-dev-MicrosoftWindowsCompatibility… pwine-gecko-MicrosoftWindowsCompatibility… pwine1.0-MicrosoftWindowsCompatibility… pwine1.0-dev-MicrosoftWindowsCompatibility… pwine1.0-gecko-MicrosoftWindowsCompatibility… pwine1.2-MicrosoftWindowsCompatibility… pwine1.2-dbg-MicrosoftWindowsCompatibility… pwine1.2-dev-MicrosoftWindowsCompatibility… pwine1.2-gecko-MicrosoftWindowsCompatibility… pwinefish-LaTeXEditorbasedonBluefish $ Noticethatbeforeeachpackagenameiseithera pori.Ifyouseean iu,thepackageis currentlyinstalledonyoursystem.Ifyouseea porv,itisavailablebutnotinstalled.As youcanseefromtheprecedinglisting,thissystemdoesnothavewinecurrentlyinstalled, butthepackageisavailablefromthesoftwarerepository. Installingasoftwarepackageonasystemfromarepositoryusing aptitudeisaseasyas this: aptitudeinstallpackage_name After you find the software package name from the search option, just plug it into the aptitudecommandusingtheinstalloption: $ $sudoaptitudeinstallwine ThefollowingNEWpackageswillbeinstalled: cabextract{a}esound-clients{a}esound-common{a}gnome-exe-thumbnailer {a} icoutils{a}imagemagick{a}libaudio2{a}libaudiofile0{a}libcdt4{a} libesd0{a}libgraph4{a}libgvc5{a}libilmbase6{a}libmagickcore3-extra {a} libmpg123-0{a}libnetpbm10{a}libopenal1{a}libopenexr6{a} libpathplan4{a}libxdot4{a}netpbm{a}ttf-mscorefonts-installer{a} ttf-symbol-replacement{a}winbind{a}winewine1.2{a}wine1.2-gecko{a} 0packagesupgraded,27newlyinstalled,0toremoveand0notupgraded. Needtoget0B/27.6MBofarchives.Afterunpacking121MBwillbeused. Doyouwanttocontinue?[Y/n/?]Y Preconfiguringpackages… […] Alldone,noerrors. Allfontsdownloadedandinstalled. Updatingfontconfigcachefor/usr/share/fonts/truetype/msttcorefonts Settingupwinbind(2:3.5.4~dfsg-1ubuntu7)… *StartingtheWinbinddaemonwinbind [OK] Settingupwine(1.2-0ubuntu5)… Settingupgnome-exe-thumbnailer(0.6-0ubuntu1)… Processingtriggersforlibc-bin… ldconfigdeferredprocessingnowtakingplace $ Note Beforetheaptitudecommandintheprecedinglisting,thesudocommandisused. Thesudocommandallowsyoutorunacommandastherootuser.Youcanusethe sudocommandtorunadministrativetasks,suchasinstallingsoftware. Tocheckiftheinstallationprocessedproperly,justusethesearchoptionagain.Thistime you should see an i u listed in front of the wine software package, indicating it is installed. Youmayalsonoticethatthereareadditionalpackageswiththe iuinfrontofthem.This is because aptitude automatically resolved any necessary package dependencies for us and installs the needed additional library and software packages. This is a wonderful featureincludedinmanyPackageManagementSystems. Updatingsoftwarewithaptitude Whileaptitudehelpsprotectyoufromproblemsinstallingsoftware,tryingtocoordinate a multiple-package update with dependencies can get tricky. To safely update all the software packages on a system with any new versions in the repository, use the safeupgradeoption: aptitudesafe-upgrade Notice that this command doesn’t take a software package name as an argument. That’s becausethe safe-upgrade option upgrades all the installed packages to the most recent versionavailableintherepository,whichissaferforsystemstabilization. Here’sasampleoutputfromrunningtheaptitudesafe-updatecommand: $ $sudoaptitudesafe-upgrade Thefollowingpackageswillbeupgraded: evolutionevolution-commonevolution-pluginsgsfontslibevolution xserver-xorg-video-geode 6packagesupgraded,0newlyinstalled,0toremoveand0notupgraded. Needtoget9,312kBofarchives.Afterunpacking0Bwillbeused. Doyouwanttocontinue?[Y/n/?]Y Get:1http://us.archive.ubuntu.com/ubuntu/maverick/main libevolutioni3862.30.3-1ubuntu4[2,096kB] […] Preparingtoreplacexserver-xorg-video-geode2.11.9-2 (using…/xserver-xorg-video-geode_2.11.9-3_i386.deb)… Unpackingreplacementxserver-xorg-video-geode… Processingtriggersforman-db… Processingtriggersfordesktop-file-utils… Processingtriggersforpython-gmenu… […] Currentstatus:0updates[-6]. $ Youcanalsouseless-conservativeoptionsforsoftwareupgrades: aptitudefull-upgrade aptitudedist-upgrade These options perform the same task, upgrading all the software packages to the latest versions. Where they differ from safe-upgrade is that they do not check dependencies betweenpackages.Thewholepackagedependencyissuecangetrealugly.Ifyou’renot exactly sure of the dependencies for the various packages, stick with the safe-upgrade option. Note Obviously,runningaptitude’ssafe-upgradeoptionissomethingyoushoulddoona regularbasistokeepyoursystemuptodate.However,itisespeciallyimportantto runitafterafreshdistributioninstallation.Usually,lotsofsecuritypatchesand updateshavebeenreleasedsincethelastfullreleaseofadistribution. Uninstallingsoftwarewithaptitude Gettingridofsoftwarepackageswithaptitudeisaseasyasinstallingandupgradingthem. The only real choice you have to make is whether to keep the software’s data and configurationfilesaroundafterward. To remove a software package, but not the data and configuration files, use the remove optionofaptitude.Toremoveasoftwarepackageandtherelateddataandconfiguration files,usethepurgeoption: $sudoaptitudepurgewine [sudo]passwordforuser: ThefollowingpackageswillbeREMOVED: cabextract{u}esound-clients{u}esound-common{u}gnome-exe-thumbnailer {u} icoutils{u}imagemagick{u}libaudio2{u}libaudiofile0{u}libcdt4{u} libesd0{u}libgraph4{u}libgvc5{u}libilmbase6{u}libmagickcore3-extra {u} libmpg123-0{u}libnetpbm10{u}libopenal1{u}libopenexr6{u} libpathplan4{u}libxdot4{u}netpbm{u}ttf-mscorefonts-installer{u} ttf-symbol-replacement{u}winbind{u}wine{p}wine1.2{u}wine1.2-gecko {u} 0packagesupgraded,0newlyinstalled,27toremoveand6notupgraded. Needtoget0Bofarchives.Afterunpacking121MBwillbefreed. Doyouwanttocontinue?[Y/n/?]Y (Readingdatabase…120968filesanddirectoriescurrentlyinstalled.) Removingttf-mscorefonts-installer… […] Processingtriggersforfontconfig… Processingtriggersforureadahead… Processingtriggersforpython-support… $ Toseeifthepackagehasbeenremoved,youcanusethe aptitudesearchoptionagain. Ifyouseea cinfrontofthepackagename,itmeansthesoftwarehasbeenremoved,but the configuration files have not been purged from the system. A p in front indicates the configurationfileshavealsobeenremoved. Theaptituderepositories Thedefaultsoftwarerepositorylocationsforaptitudearesetupforyouwhenyouinstall your Linux distribution. The repository locations are stored in the file /etc/apt/sources.list. Inmanycases,youneverneedtoadd/removeasoftwarerepositorysoyoudon’tneedto touchthisfile.However,aptitudepullssoftwarefromonlytheserepositories.Also,when searchingforsoftwaretoinstallorupdate,aptitudechecksonlytheserepositories.Ifyou needtoincludesomeadditionalsoftwarerepositoriesforyourPMS,thisistheplacetodo it. Tip TheLinuxdistributiondevelopersworkhardtomakesurepackageversionsaddedto therepositoriesdon’tconflictwithoneanother.Usuallyit’ssafesttoupgradeor installasoftwarepackagefromtherepository.Evenifanewerversionisavailable elsewhere,youmaywanttoholdoffinstallingituntilthatversionisavailableinyour Linuxdistribution’srepository Thefollowingisanexampleofasources.listfilefromanUbuntusystem: $cat/etc/apt/sources.list #debcdrom:[Ubuntu14.04LTS_TrustyTahr_-Releasei386(20140417)]/ trustymainrestricted #Seehttp://help.ubuntu.com/community/UpgradeNotesforhowtoupgradeto #newerversionsofthedistribution. debhttp://us.archive.ubuntu.com/ubuntu/trustymainrestricted deb-srchttp://us.archive.ubuntu.com/ubuntu/trustymainrestricted ##Majorbugfixupdatesproducedafterthefinalreleaseofthe ##distribution. debhttp://us.archive.ubuntu.com/ubuntu/trusty-updatesmainrestricted deb-srchttp://us.archive.ubuntu.com/ubuntu/trusty-updatesmainrestricted ##N.B.softwarefromthisrepositoryisENTIRELYUNSUPPORTEDbytheUbuntu ##team.Also,pleasenotethatsoftwareinuniverseWILLNOTreceiveany ##revieworupdatesfromtheUbuntusecurityteam. debhttp://us.archive.ubuntu.com/ubuntu/trustyuniverse deb-srchttp://us.archive.ubuntu.com/ubuntu/trustyuniverse debhttp://us.archive.ubuntu.com/ubuntu/trusty-updatesuniverse deb-srchttp://us.archive.ubuntu.com/ubuntu/trusty-updatesuniverse […] ##UncommentthefollowingtwolinestoaddsoftwarefromCanonical’s ##‘partner’repository. ##ThissoftwareisnotpartofUbuntu,butisofferedbyCanonicalandthe ##respectivevendorsasaservicetoUbuntuusers. #debhttp://archive.canonical.com/ubuntutrustypartner #deb-srchttp://archive.canonical.com/ubuntutrustypartner ##ThissoftwareisnotpartofUbuntu,butisofferedbythird-party ##developerswhowanttoshiptheirlatestsoftware. debhttp://extras.ubuntu.com/ubuntutrustymain deb-srchttp://extras.ubuntu.com/ubuntutrustymain $ First,noticethatthefileisfullofhelpfulcommentsandwarnings.Therepositorysources specifiedusethefollowingstructure: deb(ordeb-src)addressdistribution_namepackage_type_list The debor deb-srcvalueindicatesthesoftwarepackagetype.The debvalueindicatesit is a source of compiled programs, whereas the deb-src value indicates it is a source of sourcecode. The address entry is the software repository’s web address. The distribution_name entry is the name of this particular software repository’s distribution’s version. In the example, the distribution name is trusty. This does not necessarily mean that the distributionyouarerunningisUbuntu’sTrustyTahr;itjustmeanstheLinuxdistribution is using the Ubuntu Trusty Tahr software repositories! For example, in Linux Mint’s sources.listfile,youseeamixofLinuxMintandUbuntusoftwarerepositories. Finally,thepackage_type_listentrymaybemorethanonewordandindicateswhattype of packages the repository has in it. For example, you may see values such as main, restricted,universe,orpartner. Whenyouneedtoaddasoftwarerepositorytoyoursourcesfile,youcantrytowingit yourself,butthatmorethanlikelywillcauseproblems.Often,softwarerepositorysitesor various package developer sites have an exact line of text that you can copy from their websiteandpasteintoyour sources.listfile.It’sbesttochoosethesaferrouteandjust copy/paste. Thefront-endinterface, aptitude,providesintelligentcommandlineoptionsforworking with the Debian-based dpkg utility. Now it’s time to look at the Red Hat–based distributions’rpmutilityanditsvariousfront-endinterfaces. TheRedHat–BasedSystems Like the Debian-based distributions, the Red Hat–based systems have several different front-endtoolsthatareavailable.Thesearethecommonones: yum:UsedinRedHatandFedora urpm:UsedinMandriva zypper:UsedinopenSUSE These front-ends are all based on the rpm command line tool. The following section discusses how to manage software packages using these various rpm-based tools. The focusisonyum,butinformationisalsoincludedforzypperandurpm. Listinginstalledpackages To find out what is currently installed on your system, at the shell prompt, type the followingcommand: yumlistinstalled Theinformationwillprobablywhizbyyouonthedisplayscreen,soit’sbesttoredirect theinstalledsoftwarelistingintoafile.Youcanthenusethemoreorlesscommand(ora GUIeditor)tolookatthelistinacontrolledmanner. yumlistinstalled>installed_software To list out the installed packages on your openSUSE or Mandriva distribution, see the commandsinTable9.1.Unfortunately,the urpmtoolusedinMandrivacannotproducea currentlyinstalledsoftwarelisting.Thus,youneedtoreverttotheunderlyingrpmtool. Table9.1HowtoListInstalledSoftwarewithzypperandurpm Distribution Front-EndTool Command urpm rpm-qa>installed_software Mandriva zypper zippersearch-I>installed_software openSUSE To find out detailed information for a particular software package, yum really shines. It givesyouaveryverbosedescriptionofthepackage,andwithanothersimplecommand, youcanseewhetherthepackageisinstalled: #yumlistxterm Loadedplugins:langpacks,presto,refresh-packagekit Addingen_UStolanguagelist AvailablePackages xterm.i686253-1.el6 # #yumlistinstalledxterm Loadedplugins:refresh-packagekit Error:NomatchingPackagestolist # Thecommandstolistdetailedsoftwarepackageinformationusingurpmandzypperarein Table9.2. You can acquire an even more detailed set of package information from the repository,usingtheinfooptiononthezyppercommand. Table9.2HowtoSeeVariousPackageDetailswithzypperandurpm DetailType Package Information Installed? Package Information Installed? Front-End Tool Command urpm urpmq-ipackage_name urpm rpm-qpackage_name zypper zyppersearch-spackage_name zypper Samecommand,butlookforaniintheStatus column Finally,ifyouneedtofindoutwhatsoftwarepackageprovidesaparticularfileonyour filesystem,theversatileyumcandothat,too!Justenterthecommand: yumprovidesfile_name Here’s an example of trying to find what software provided the configuration file /etc/yum.conf: # #yumprovides/etc/yum.conf Loadedplugins:fastestmirror,refresh-packagekit,security Determiningfastestmirrors *base:mirror.web-ster.com *extras:centos.chi.host-engine.com *updates:mirror.umd.edu yum-3.2.29-40.el6.centos.noarch:RPMpackageinstaller/updater/manager Repo:base Matchedfrom: Filename:/etc/yum.conf yum-3.2.29-43.el6.centos.noarch:RPMpackageinstaller/updater/manager Repo:updates Matchedfrom: Filename:/etc/yum.conf yum-3.2.29-40.el6.centos.noarch:RPMpackageinstaller/updater/manager Repo:installed Matchedfrom: Other:Provides-match:/etc/yum.conf # # yum checked three separate repositories: base, updates, and installed. From both, the answeris:theyumsoftwarepackageprovidesthisfile! Installingsoftwarewithyum Installationofasoftwarepackageusing yumisincrediblyeasy.Thefollowingisthebasic command for installing a software package, all its needed libraries, and package dependenciesfromarepository: yuminstallpackage_name Here’sanexampleofinstallingthextermpackagethatwetalkedaboutinChapter2: $suPassword: #yuminstallxterm Loadedplugins:fastestmirror,refresh-packagekit,security Determiningfastestmirrors *base:mirrors.bluehost.com *extras:mirror.5ninesolutions.com *updates:mirror.san.fastserv.com SettingupInstallProcess ResolvingDependencies —>Runningtransactioncheck –>Packagexterm.i6860:253-1.el6willbeinstalled —>FinishedDependencyResolution DependenciesResolved […] Installed: xterm.i6860:253-1.el6 Complete! # Note Beforetheyumcommandintheprecedinglisting,thesu-commandisused.This commandallowsyoutoswitchtotherootuser.OnthisLinuxsystem,the#denotes youareloggedinasroot.Youshouldonlyswitchtorootusertemporarilyinorderto runadministrativetasks,suchasinstallingandupdatingsoftware.Thesudo commandisanotheroptionaswell. Youcanalsomanuallydownloadan rpminstallationfileandinstallitusing yum.Thisis calledalocalinstallation.Thisisthebasiccommand: yumlocalinstallpackage_name.rpm You can begin to see that one of yum‘s strengths is that it uses very logical and userfriendlycommands. Table9.3showshowtoperformapackageinstallwith urpmandzypper.Youshouldnote thatifyouarenotloggedinasroot,yougeta“commandnotfound”errormessageusing urpm. Table9.3HowtoInstallSoftwarewithzypperandurpm Front-EndTool Command urpm urpmipackage_name zypper zypperinstallpackage_name Updatingsoftwarewithyum In most Linux distributions, when you’re working away in the GUI, you get those nice littlenotificationiconstellingyouthatanupdateisneeded.Hereatthecommandline,it takesalittlemorework. Toseethelistofalltheavailableupdatesforyourinstalledpackages,typethefollowing command: yumlistupdates It’salwaysnicetogetnoresponsetothiscommandbecauseitmeansyouhavenothingto update!However,ifyoudodiscoveraparticularsoftwarepackageneedsupdating,type thefollowingcommand: yumupdatepackage_name If you’d like to update all the packages listed in the update list, just enter the following command: yumupdate Commands for updating software packages on Mandriva and openSUSE are listed in Table9.4.Whenurpmisused,therepositorydatabaseisautomaticallyrefreshedaswell assoftwarepackagesupdated. Table9.4HowtoUpdateSoftwarewithzypperandurpm Front-EndTool Command urpm urpmi—auto-update—update zypper zypperupdate Uninstallingsoftwarewithyum The yumtoolalsoprovidesaneasywaytouninstallsoftwareyounolongerwantonyour system. As with aptitude, you need to choose whether to keep the software package’s dataandconfigurationfiles. To just remove the software package and keep any configuration and data files, use the followingcommand: yumremovepackage_name Touninstallthesoftwareandallitsfiles,usetheeraseoption: yumerasepackage_name Itisequallyeasytoremovesoftwareusing urpm and zypperinTable9.5.Bothofthese toolsperformafunctionsimilartoyum‘seraseoption. Table9.5HowtoUninstallSoftwarewithzypperandurpm Front-EndTool Command urpm urpmepackage_name zypper zypperremovepackage_name Although life is considerably easier with PMS packages, it’s not always problem-free. Occasionally,thingsdogowrong.Fortunately,there’shelp. Dealingwithbrokendependencies Sometimes, as multiple software packages get loaded, a software dependency for one packagecangetoverwrittenbytheinstallationofanotherpackage.Thisiscalledabroken dependency. Ifthisshouldhappenonyoursystem,firsttrythefollowingcommand: yumcleanall Thentrytousethe updateoptioninthe yumcommand.Sometimes,justcleaningupany misplacedfilescanhelp. Ifthatdoesn’tsolvetheproblem,trythefollowingcommand: yumdeplistpackage_name Thiscommanddisplaysallthepackage’slibrarydependenciesandwhatsoftwarepackage providesthem.Afteryouknowthelibrariesrequiredforapackage,youcantheninstall them.Here’sanexampleofdeterminingthedependenciesforthextermpackage: #yumdeplistxterm Loadedplugins:fastestmirror,refresh-packagekit,security Loadingmirrorspeedsfromcachedhostfile *base:mirrors.bluehost.com *extras:mirror.5ninesolutions.com *updates:mirror.san.fastserv.com Findingdependencies: package:xterm.i686253-1.el6 dependency:libncurses.so.5 provider:ncurses-libs.i6865.7-3.20090208.el6 dependency:libfontconfig.so.1 provider:fontconfig.i6862.8.0-3.el6 dependency:libXft.so.2 provider:libXft.i6862.3.1-2.el6 dependency:libXt.so.6 provider:libXt.i6861.1.3-1.el6 dependency:libX11.so.6 provider:libX11.i6861.5.0-4.el6 dependency:rtld(GNU_HASH) provider:glibc.i6862.12-1.132.el6 provider:glibc.i6862.12-1.132.el6_5.1 provider:glibc.i6862.12-1.132.el6_5.2 dependency:libICE.so.6 provider:libICE.i6861.0.6-1.el6 dependency:libXaw.so.7 provider:libXaw.i6861.0.11-2.el6 dependency:libtinfo.so.5 provider:ncurses-libs.i6865.7-3.20090208.el6 dependency:libutempter.so.0 provider:libutempter.i6861.1.5-4.1.el6 dependency:/bin/sh provider:bash.i6864.1.2-15.el6_4 dependency:libc.so.6(GLIBC_2.4) provider:glibc.i6862.12-1.132.el6 provider:glibc.i6862.12-1.132.el6_5.1 provider:glibc.i6862.12-1.132.el6_5.2 dependency:libXmu.so.6 provider:libXmu.i6861.1.1-2.el6 # Ifthatdoesn’tsolveyourproblem,youhaveonelasttool: yumupdate—skip-broken The —skip-broken option allows you to just ignore the package with the broken dependency and update the other software packages. This may not help the broken package,butatleastyoucanupdatetheremainingpackagesonthesystem! In Table 9.6, the commands to try for broken dependencies with urpm and zypper are listed.Withzypper,thereisonlytheonecommandtoverifyandfixabrokendependency. With urpm, if the clean option does not work, you can skip updates on the offensive package. To do this, you must add the name of the offending package to the file /etc/urpmi/skip.list. Table9.6BrokenDependencieswithzypperandurpm FrontEndTool Command urpm urpmi—clean zypper zypperverify yumrepositories Justlikethe aptitudesystems,yumhasitssoftwarerepositoriessetupatinstallation.For most purposes, these pre-installed repositories work just fine for your needs. But if and whenthetimecomesthatyouneedtoinstallsoftwarefromadifferentrepository,hereare somethingsyouneedtoknow. Tip Awisesystemadministratorstickswithapprovedrepositories.Anapproved repositoryisonethatissanctionedbythedistribution’sofficialsite.Ifyoustart addingunapprovedrepositories,youlosetheguaranteeofstability.Andyouwillbe headingintobrokendependenciesterritory To see what repositories you are currently pulling software from, type the following command: yumrepolist Ifyoudon’tfindarepositoryyouneedsoftwarefrom,youneedtodoalittleconfiguration fileediting.Theyumrepositorydefinitionfilesarelocatedin/etc/yum.repos.d.Youneed toaddtheproperURLandgainaccesstoanynecessaryencryptionkeys. Goodrepositorysitessuchasrpmfusion.org lay out all the steps necessary to use them. Sometimes,theserepositorysitesofferanrpmfilethatyoucandownloadandinstallusing the yum localinstallcommand.Theinstallationofthe rpmfiledoesalltherepository setupworkforyou.Nowthat’sconvenient! urpmcallsitsrepositoriesmedia.Thecommandsforlookingat urpmmediaand zypper‘s repositoriesareinTable9.7.Noticewithbothofthesefront-endtoolsthatyoudonotedit aconfigurationfile.Instead,toaddmediaorarepository,youjusttypethecommand. Table9.7zypperandurpmRepositories Action Front-EndTool Command urpm urpmq—list-media Displayrepository urpm urpmi.addmediapath_name Addrepository zypper zypperrepos Displayrepository zypper zypperaddrepopath_name Addrepository Both Debian–based and Red Hat–based systems use Package Management Systems to ease the process of managing software. Now we are going to step out of the world of Package Management Systems and look at something a little more difficult: installing directlyfromsourcecode. InstallingfromSourceCode Chapter4discussedtarballpackages—howtocreatethemusingthe tarcommandline commandandhowtounpackthem.Beforethefancy rpmand dpkgtools,administrators hadtoknowhowtounpackandinstallsoftwarefromtarballs. If you work in the open source software environment much, there’s a good chance you willstillfindsoftwarepackedupasatarball.Thissectionwalksyouthroughtheprocess ofunpackingandinstallingatarballsoftwarepackage. Forthisexample,thesoftwarepackagesysstatisused.Thesysstatutilityisaverynice softwarepackagethatprovidesavarietyofsystemmonitoringtools. First,youneedtodownloadthesysstattarballtoyourLinuxsystem.Youcanoftenfind the sysstatpackageavailableondifferentLinuxsites,butit’susuallybesttogostraight to the source of the program. In this case, it’s the website http://sebastien.godard.pagesperso-orange.fr/. IfyouclicktheDownloadlink,yougotothepagethatcontainsthefilesfordownloading. Thecurrentversionatthetimeofthiswritingis11.1.1,andthedistributionfilenameis sysstat-11.1.1.tar.gz. ClickthelinktodownloadthefiletoyourLinuxsystem.Afteryouhavedownloadedthe file,youcanunpackit. Tounpackasoftwaretarball,usethestandardtarcommand: # #tar-zxvfsysstat-11.1.1.tar.gz sysstat-11.1.1/ sysstat-11.1.1/cifsiostat.c sysstat-11.1.1/FAQ sysstat-11.1.1/ioconf.h sysstat-11.1.1/rd_stats.h sysstat-11.1.1/COPYING sysstat-11.1.1/common.h sysstat-11.1.1/sysconfig.in sysstat-11.1.1/mpstat.h sysstat-11.1.1/rndr_stats.h […] sysstat-11.1.1/activity.c sysstat-11.1.1/sar.c sysstat-11.1.1/iostat.c sysstat-11.1.1/rd_sensors.c sysstat-11.1.1/prealloc.in sysstat-11.1.1/sa2.in # # Nowthatthetarballisunpackedandthefileshaveneatlyputthemselvesintoadirectory calledsysstat-11.1.1,youcandivedownintothatdirectoryandcontinue. First, use the cd command to get into the new directory and list the contents of the directory: $cdsysstat-11.1.1 $ls activity.ciconfigprealloc.insa.h buildINSTALLpr_stats.csar.c CHANGESioconf.cpr_stats.hsa_wrap.c cifsiostat.cioconf.hrd_sensors.csysconfig.in cifsiostat.hiostat.crd_sensors.hsysstat-11.1.1.lsm common.ciostat.hrd_stats.csysstat-11.1.1.spec common.hjson_stats.crd_stats.hsysstat.in configurejson_stats.hREADMEsysstat.ioconf configure.inMakefile.inrndr_stats.csysstat.service.in contribmanrndr_stats.hsysstat.sysconfig.in COPYINGmpstat.csa1.inversion.in count.cmpstat.hsa2.inxml count.hnfsiostat-sysstat.csa_common.cxml_stats.c CREDITSnfsiostat-sysstat.hsadc.cxml_stats.h cronnlssadf.c FAQpidstat.csadf.h format.cpidstat.hsadf_misc.c $ In the listing of the directory, you should typically see a READMEor AAAREADMEfile.Itis veryimportanttoreadthisfile.Theactualinstructionsyouneedtofinishthesoftware’s installationareinthisfile. Followingtheadvicecontainedinthe READMEfile,thenextstepisto configuresysstat for your system. This checks your Linux system to ensure it has the proper library dependencies,inadditiontothepropercompilertocompilethesourcecode: #./configure Checkprograms: . checkingforgcc…gcc checkingwhethertheCcompilerworks…yes checkingforCcompilerdefaultoutputfilename…a.out […] checkingforANSICheaderfiles…(cached)yes checkingfordirent.hthatdefinesDIR…yes checkingforlibrarycontainingopendir…nonerequired checkingctype.husability…yes checkingctype.hpresence…yes checkingforctype.h…yes checkingerrno.husability…yes checkingerrno.hpresence…yes checkingforerrno.h…yes […] Checklibraryfunctions: . checkingforstrchr…yes checkingforstrcspn…yes checkingforstrspn…yes checkingforstrstr…yes checkingforsensorssupport…yes checkingforsensors_get_detected_chipsin-lsensors…no checkingforsensorslib…no . Checksystemservices: . checkingforspecialCcompileroptionsneededforlargefiles…no checkingfor_FILE_OFFSET_BITSvalueneededforlargefiles…64 . Checkconfiguration: […] Nowcreatefiles: […] config.status:creatingMakefile Sysstatversion:11.1.1 Installationprefix:/usr/local rcdirectory:/etc/rc.d Initdirectory:/etc/rc.d/init.d Systemdunitdir: Configurationdirectory:/etc/sysconfig Manpagesdirectory:${datarootdir}/man Compiler:gcc Compilerflags:-g-O2 # If anything does go wrong, the configure step displays an error message explaining what’s missing. If you don’t have the GNU C compiler installed in your Linux distribution, you get a single error message, but for all other issues you should see multiplemessagesindicatingwhat’sinstalledandwhatisn’t. The next stage is to build the various binary files using the make command. The make commandcompilesthesourcecodeandthenthelinkertocreatethefinalexecutablefiles for the package. As with the configure command, the make command produces lots of outputasitgoesthroughthestepsofcompilingandlinkingallthesourcecodefiles: #make –gcc-osadc.o-c-g-O2-Wall-Wstrict-prototypes-pipe-O2 -DSA_DIR="/var/log/sa"-DSADC_PATH="/usr/local/lib/sa/sadc" -DUSE_NLS-DPACKAGE="sysstat" -DLOCALEDIR="/usr/local/share/locale"sadc.c gcc-oact_sadc.o-c-g-O2-Wall-Wstrict-prototypes-pipe-O2 -DSOURCE_SADC-DSA_DIR="/var/log/sa" -DSADC_PATH="/usr/local/lib/sa/sadc" -DUSE_NLS-DPACKAGE="sysstat" -DLOCALEDIR="/usr/local/share/locale"activity.c […] # When make is finished, you have the actual sysstat software program available in the directory! However, it’s somewhat inconvenient to have to run it from that directory. Instead,you’llwanttoinstallitinacommonlocationonyourLinuxsystem.Todothat, you need to log in as the root user account (or use the sudo command if your Linux distributionprefers)andthenusetheinstalloptionofthemakecommand: #makeinstall mkdir-p/usr/local/share/man/man1 mkdir-p/usr/local/share/man/man5 mkdir-p/usr/local/share/man/man8 rm-f/usr/local/share/man/man8/sa1.8* install-m644-gmanman/sa1.8/usr/local/share/man/man8 rm-f/usr/local/share/man/man8/sa2.8* install-m644-gmanman/sa2.8/usr/local/share/man/man8 rm-f/usr/local/share/man/man8/sadc.8* […] install-m644-gmanman/sadc.8/usr/local/share/man/man8 install-m644FAQ/usr/local/share/doc/sysstat-11.1.1 install-m644*.lsm/usr/local/share/doc/sysstat-11.1.1 # Nowthe sysstat package is installed on the system! Although it’s not quite as easy as installing a software package via a PMS, installing software using tarballs is not that difficult. Summary This chapter discussed how to work with a Package Management Systems (PMS) to install,update,orremovesoftwarefromthecommandline.AlthoughmostoftheLinux distributionsusefancyGUItoolsforsoftwarepackagemanagement,youcanalsoperform packagemanagementfromthecommandline. TheDebian-basedLinuxdistributionsusethe dpkgutilitytointerfacewiththePMSfrom the command line. A front-end to the dpkg utility is aptitude. It provides simple commandlineoptionsforworkingwithsoftwarepackagesinthedpkgformat. TheRedHat–basedLinuxdistributionsarebasedontherpmutilitybutusedifferentfrontendtoolsatthecommandline.RedHatandFedorause yumforinstallingandmanaging softwarepackages.TheopenSUSEdistributionuseszypperformanagingsoftware,while theMandrivadistributionusesurpm. The chapter closed with a discussion on how to install software packages that are only distributed in source code tarballs. The tar command allows you to unpack the source codefilesfromthetarball,andconfigureandmakeallowyoutobuildthefinalexecutable programfromthesourcecode. ThenextchapterlooksatthedifferenteditorsavailableinLinuxdistributions.Asyouget ready to start working on shell scripts, it will come in handy to know what editors are availabletouse! Chapter10 WorkingwithEditors InThisChapter 1. Workingwiththevimeditor 2. Exploringnano 3. Understandingemacs 4. Gettingcomfortablewithkwrite 5. LookingatKate 6. UsingtheGNOMEeditor Beforeyoucanstartyourshellscriptingcareer,youneedtoknowhowtouseatleast onetexteditorinLinux.Themoreyouknowabouthowtousefeaturessuchas searching,cutting,andpasting,thequickeryoucandevelopyourshellscripts. Youcanchoosefromseveraleditors.Manyindividualsfindaparticulareditorwhose featurestheyloveandexclusivelyusethattexteditor.Thischapterdiscussesjusta fewofthetexteditorsyou’llseeintheLinuxworld. VisitingthevimEditor ThevieditorwastheoriginaleditorusedonUnixsystems.Itusedtheconsolegraphics modetoemulateatext-editingwindow,allowingyoutoseethelinesofyourfile,move aroundwithinthefile,andinsert,edit,andreplacetext. Although it was quite possibly the most complicated editor in the world (at least in the opinionofthosewhohateit),itprovidesmanyfeaturesthathavemadeitastapleforUnix administratorsfordecades. WhentheGNUProjectportedthevieditortotheopensourceworld,theychosetomake someimprovementstoit.Becauseitnolongerresembledtheoriginalvieditorfoundin theUnixworld,thedevelopersalsorenamedit,toviimproved,orvim. Thissectionwalksyouthroughthebasicsofusingthevimeditortoedityourtextshell scriptfiles. Checkingyourvimpackage Beforeyoubeginyourexplorationofthevimeditor,it’sagoodideatounderstandwhat vim package your Linux system has installed. On some distributions, you will have the full vim package installed and an alias for the vi command, as shown on this CentOS distribution: $aliasvi aliasvi=‘vim’ $ $whichvim /usr/bin/vim $ $ls-l/usr/bin/vim -rwxr-xr-x.1rootroot1967072Apr52012/usr/bin/vim $ Noticethattheprogramfile’slonglistingdoesnotshowanylinkedfiles(seeChapter3 formoreinformationonlinkedfiles).Ifthevimprogramislinked,itmaybelinkedtoa lessthanfull-featurededitor.Thus,it’sagoodideatocheckforlinkedfiles. On other distributions, you will find various flavors of the vim editor. Notice on this Ubuntu distribution that not only is there no alias for the vi command, but the /usr/bin/viprogramfilebelongstoaseriesoffilelinks: $aliasvi -bash:alias:vi:notfound $ $whichvi /usr/bin/vi $ $ls-l/usr/bin/vi lrwxrwxrwx1rootroot20Apr2212:39 /usr/bin/vi->/etc/alternatives/vi $ $ls-l/etc/alternatives/vi lrwxrwxrwx1rootroot17Apr2212:33 /etc/alternatives/vi->/usr/bin/vim.tiny $ $ls-l/usr/bin/vim.tiny -rwxr-xr-x1rootroot884360Jan214:40 /usr/bin/vim.tiny $ $readlink-f/usr/bin/vi /usr/bin/vim.tiny Thus,whenthevicommandisentered,the/usr/bin/vim.tinyprogramisexecuted.The vim.tinyprogramprovidesonlyafewvimeditorfeatures.Ifyouareseriousaboutusing thevimeditorandhaveUbuntu,youshouldinstallatleastthebasicvimpackage. Note Noticeintheprecedingexamplethat,insteadofhavingtousethels-lcommand multipletimestofindaseriesoflinkedfiles’finalobject,youcanusethereadlink -fcommand.Itimmediatelyproducesthelinkedfileseries’finalobject. Software installations were covered in detail in Chapter 9. Installing the basic vim packageonthisUbuntudistributionisfairlystraightforward: $sudoapt-getinstallvim […] Thefollowingextrapackageswillbeinstalled: vim-runtime Suggestedpackages: ctagsvim-docvim-scripts ThefollowingNEWpackageswillbeinstalled: vimvim-runtime […] $ $readlink-f/usr/bin/vi /usr/bin/vim.basic $ The basic vim editor is now installed on this Ubuntu distribution, and the /usr/bin/vi program file’s link was automatically changed to point to /usr/bin/vim.basic. Thus, when the vi command is entered on this Ubuntu system, the basic vim editor is used insteadoftinyvim. Exploringvimbasics Thevimeditorworkswithdatainamemorybuffer.Tostartthevimeditor,justtypethe vimcommand(orviifthere’sanaliasorlinkedfile)andthenameofthefileyouwantto edit: $vimmyprog.c Ifyoustartvimwithoutafilename,orifthefiledoesn’texist,vimopensanewbufferarea forediting.Ifyouspecifyanexistingfileonthecommandline,vimreadstheentirefile’s contentsintoabufferarea,whereitisreadyforediting,asshowninFigure10.1. Figure10.1Thevimmainwindow The vim editor detects the terminal type for the session (see Chapter 2) and uses a fullscreenmodetousetheentireconsolewindowfortheeditorarea. Theinitialvimeditwindowshowsthecontentsofthefile(ifthereareany)alongwitha message line at the bottom of the window. If the file contents don’t take up the entire screen,vimplacesatildeonlinesthatarenotpartofthefile(asshowninFigure10.1). Themessagelineatthebottomindicatesinformationabouttheeditedfile,dependingon the file’s status, and the default settings in your vim installation. If the file is new, the message[NewFile]appears. Thevimeditorhastwomodesofoperation: Normalmode Insertmode Whenyoufirstopenafile(orstartanewfile)forediting,thevimeditorentersnormal mode.Innormalmode,thevimeditorinterpretskeystrokesascommands(moreonthose later). Ininsertmode,viminsertseverykeyyoutypeatthecurrentcursorlocationinthebuffer. Toenterinsertmode,presstheikey.Togetoutofinsertmodeandgobackintonormal mode,presstheEscapekeyonthekeyboard. Innormalmode,youcanmovethecursoraroundthetextareabyusingthearrowkeys(as long as your terminal type is detected properly by vim). If you happen to be on a flaky terminalconnectionthatdoesn’thavethearrowkeysdefined,allhopeisnotlost.Thevim commandsincludecommandsformovingthecursor: htomoveleftonecharacter jtomovedownoneline(thenextlineinthetext) ktomoveuponeline(thepreviouslineinthetext) ltomoverightonecharacter Moving around within large text files line by line can get tedious. Fortunately, vim providesafewcommandstohelpspeedthingsalong: PageDown(orCtrl+F)tomoveforwardonescreenofdata PageUp(orCtrl+B)tomovebackwardonescreenofdata Gtomovetothelastlineinthebuffer numGtomovetothelinenumbernuminthebuffer ggtomovetothefirstlineinthebuffer Thevimeditorhasaspecialfeaturewithinnormalmodecalledcommandlinemode.The commandlinemodeprovidesaninteractivecommandlinewhereyoucanenteradditional commands to control the actions in vim. To get to command line mode, press the colon key in normal mode. The cursor moves to the message line, and a colon (:) appears, waitingforyoutoenteracommand. Withinthecommandlinemodeareseveralcommandsforsavingthebuffertothefileand exitingvim: qtoquitifnochangeshavebeenmadetothebufferdata q!toquitanddiscardanychangesmadetothebufferdata wfilenametosavethefileunderadifferentfilename wqtosavethebufferdatatothefileandquit After seeing just a few basic vim commands, you might understand why some people absolutelyhatethevimeditor.Tobeabletousevimtoitsfullest,youmustknowplenty of obscure commands. However, after you get a few of the basic vim commands down, you can quickly edit files directly from the command line, no matter what type of environmentyou’rein.Plus,afteryougetcomfortabletypingcommands,italmostseems secondnaturetotypebothdataandeditingcommands,anditbecomesoddhavingtojump backtousingamouse! Editingdata Whileininsertmode,youcaninsertdataintothebuffer;however,sometimesyouneedto addorremovedataafteryou’vealreadyentereditintothebuffer.Whileinnormalmode, the vim editor provides several commands for editing the data in the buffer. Table10.1 listssomecommoneditingcommandsforvim. Table10.1vimEditingCommands Command x dd dw d$ J u a A rchar Rtext Description Deletesthecharacteratthecurrentcursorposition Deletesthelineatthecurrentcursorposition Deletesthewordatthecurrentcursorposition Deletestotheendofthelinefromthecurrentcursorposition Deletesthelinebreakattheendofthelineatthecurrentcursorposition (joinslines) Undoesthepreviouseditcommand Appendsdataafterthecurrentcursorposition Appendsdatatotheendofthelineatthecurrentcursorposition Replacesasinglecharacteratthecurrentcursorpositionwithchar Overwritesthedataatthecurrentcursorpositionwithtext,untilyoupress Escape Someoftheeditingcommandsalsoallowyoutouseanumericmodifiertoindicatehow many times to perform the command. For example, the command 2x deletes two characters, starting from the current cursor position, and the command 5dd deletes five lines,startingatthelinefromthecurrentcursorposition. Note BecarefulwhentryingtousethekeyboardBackspaceorDeletekeyswhileinthe vimeditor’snormalmode.ThevimeditorusuallyrecognizestheDeletekeyasthe functionalityofthexcommand,deletingthecharacteratthecurrentcursorlocation. Usually,thevimeditordoesn’trecognizetheBackspacekeyinnormalmode Copyingandpasting A standard editor feature is the ability to cut or copy data and paste it elsewhere in the document.Thevimeditorprovidesawaytodothis. Cutting and pasting is relatively easy. You’ve already seen the commands in Table10.1 thatcanremovedatafromthebuffer.However,whenvimremovesdata,itactuallykeeps itstoredinaseparateregister.Youcanretrievethatdatabyusingthepcommand. Forexample,youcanusethe ddcommandtodeletealineoftext,movethecursortothe bufferlocationwhereyouwanttoplaceit,andthenusethe pcommand.The pcommand inserts the text after the line at the current cursor position. You can do this with any commandthatremovestext. Copyingtextisalittlebittrickier.Thecopycommandinvimisy(foryank).Youcanuse thesamesecondcharacterwithyaswiththedcommand(ywtoyankaword,y$toyankto theendofaline).Afteryouyankthetext,movethecursortothelocationwhereyouwant toplacethetextandusethepcommand.Theyankedtextnowappearsatthatlocation. Yankingistrickyinthatyoucan’tseewhathappenedbecauseyou’renotaffectingthetext thatyouyank.Youneverknowforsurewhatyouyankeduntilyoupasteitsomewhere. Butthere’sanotherfeatureinvimthathelpsyououtwithyanking. Thevisualmodehighlightstextasyoumovethecursor.Youusevisualmodetoselecttext toyankforpasting.Toentervisualmode,movethecursortothelocationwhereyouwant tostartyanking,andpressv.Noticethatthetextatthecursorpositionisnowhighlighted. Next,movethecursortocoverthetextyouwanttoyank(youcanevenmovedownlines toyankmorethanonelineoftext).Asyoumovethecursor,vimhighlightsthetextinthe yankarea.Afteryou’vecoveredthetextyouwanttocopy,presstheykeytoactivatethe yankcommand.Nowthatyouhavethetextintheregister,justmovethecursortowhere youwanttopasteandusethepcommand. Searchingandsubstituting You can easily search for data in the buffer using the vim search command. To enter a searchstring,presstheforwardslash(/)key.Thecursorgoestothemessageline,andvim displaysaforwardslash.Enterthetextyouwanttofind,andpresstheEnterkey.Thevim editorrespondswithoneofthreeactions: Ifthewordappearsafterthecurrentcursorlocation,itjumpstothefirstlocation wherethetextappears. Iftheworddoesn’tappearafterthecurrentcursorlocation,itwrapsaroundtheendof thefiletothefirstlocationinthefilewherethetextappears(andindicatesthiswitha message). Itproducesanerrormessagestatingthatthetextwasnotfoundinthefile. Tocontinuesearchingforthesameword,presstheforwardslashcharacterandthenpress theEnterkey,oryoucanusethenkey,fornext. Thesubstitutecommandallowsyoutoquicklyreplace(substitute)onewordforanotherin the text. To get to the substitute command, you must be in command line mode. The formatforthesubstitutecommandis: :s/old/new/ Thevimeditorjumpstothefirstoccurrenceofthetext oldandreplacesitwiththetext new.Youcanmakeafewmodificationstothesubstitutecommandtosubstitutemorethan oneoccurrenceofthetext: :s/old/new/gtoreplacealloccurrencesofoldinaline :n,ms/old/new/gtoreplacealloccurrencesofoldbetweenlinenumbersnandm :%s/old/new/gtoreplacealloccurrencesofoldintheentirefile :%s/old/new/gctoreplacealloccurrencesofoldintheentirefile,butpromptfor eachoccurrence As you can see, for a console mode text editor, vim contains quite a few advanced features.BecauseeveryLinuxdistributionincludesit,it’sagoodideatoatleastknowthe basicsofthevimeditorsoyoucanalwayseditscripts,nomatterwhereyouareorwhat youhaveavailable. NavigatingthenanoEditor Although vim is a very complicated editor with many powerful features, nano is a very simpleeditor.Forindividualswhoneedasimpleconsolemodetexteditorthatiseasyto navigate,nanoisthetooltouse.It’salsoagreattexteditorforkidswhoarestartingon theirLinuxcommandlineadventure. ThenanotexteditorisacloneoftheUnixsystems’Picoeditor.AlthoughPicoalsoisa light and simple text editor, it is not licensed under the GPL. Not only is the nano text editorlicensedundertheGPL,itisalsopartoftheGNUproject. ThenanotexteditorisinstalledonmostLinuxdistributionsbydefault.Everythingabout thenanotexteditorissimple.Toopenafileatthecommandlinewithnano: $nanomyprog.c Ifyoustartnanowithoutafilename,orifthefiledoesn’texist,nanosimplyopensanew bufferareaforediting.Ifyouspecifyanexistingfileonthecommandline,nanoreadsthe entire contents of the file into a buffer area, where it is ready for editing, as shown in Figure10.2. Figure10.2Thenanoeditorwindow Noticeatthebottomofthenanoeditorwindowvariouscommandswithabriefdescription areshown.Thesecommandsarethenanocontrolcommands.Thecaret(∧)symbolshown representstheCtrlkey.Therefore,∧XstandsforthekeyboardsequenceCtrl+X. Tip Thoughthenanocontrolcommandslistcapitallettersinthekeyboardsequences,you canuseeitherlowercaseoruppercasecharactersforcontrolcommands. Havingallthebasiccommandslistedrightinfrontofyouisgreat.Noneedtomemorize what control command does what. Table 10.2 presents the various nano control commands. Table10.2nanoControlCommands Command Description CTRL+C Displaysthecursor’spositionwithinthetexteditingbuffer CTRL+G Displaysnano’smainhelpwindow CTRL+J Justifiesthecurrenttextparagraph CTRL+K Cutsthetextlineandstoresitincutbuffer CTRL+O Writesoutthecurrenttexteditingbuffertoafile CTRL+R Readsafileintothecurrenttexteditingbuffer CTRL+T Startstheavailablespellchecker CTRL+U Pastestextstoredincutbufferandplacesincurrentline CTRL+V Scrollstexteditingbuffertonextpage CTRL+W Searchesforwordorphraseswithintexteditingbuffer CTRL+X Closesthecurrenttexteditingbuffer,exitsnano,andreturnstotheshell CTRL+Y Scrollstexteditingbuffertopreviouspage ThecontrolcommandslistedinTable10.2arereallyallyouneed.However,ifyoudesire more powerful control features than those listed, nano has them. To see more control commands,typeCtrl+Ginthenanotexteditortodisplayitsmainhelpwindowcontaining additionalcontrolcommands. Note IfyoutrytousethenanospellcheckerviatheCtrl+Tcommandandgettheerror messageSpellcheckingfailed:Errorinvoking‘Spell’,therearesome potentialsolutions.Installthespellcheckersoftwarepackage,aspell,onyourLinux distributionusingChapter9asaguide. Ifinstallingtheaspellsoftwarepackagedoesnotsolvetheproblem,assuperuser editthe/etc/nanorcfile,usingyourfavoritetexteditor.Findtheline,#set speller“aspell-x-c”anddeletethehashmark(#)fromtheline.Saveandexit thefile. Additional powerful features are available at the command line. You can use command lineoptionstocontrolnanoeditorfeatures,suchascreatingabackupfilebeforeediting. Typemannanotoseetheseadditionalcommandlineoptionsforstartingnano. Thevimandnanotexteditorsofferachoicebetweenpowerfulandsimpleconsolemode texteditors.However,neitherofferstheabilitytousegraphicalfeaturesforediting.Some texteditorscanoperateinbothworlds,asexploredinthenextsection. ExploringtheemacsEditor The emacs editor is an extremely popular editor that appeared before even Unix was around.DeveloperslikeditsomuchthattheyportedittotheUnixenvironment,andnow it’sbeenportedtotheLinuxenvironment.Theemacseditorstartedoutlifeasaconsole editor,muchlikevi,buthasmigratedtothegraphicalworld. Theemacseditorstillprovidestheoriginalconsolemodeeditor,andnowitalsohasthe ability to use a graphical window to allow editing text in a graphical environment. Typically, when you start the emacs editor from a command line, the editor determines whetheryouhaveanavailablegraphicalsessionandstartsingraphicalmode.Ifyoudon’t, itstartsinconsolemode. This section describes both the console mode and graphical mode emacs editors so that you’llknowhowtouseeitheroneifyouwant(orneed)to. Checkingyouremacspackage Manydistributionsdonotcomewiththeemacseditorinstalledbydefault.Youcancheck yourRedHat-baseddistribution,byusingthewhichand/oryumlistcommandasshown onthisCentOSdistribution: $whichemacs /usr/bin/which:noemacsin(/usr/lib64/qt-3.3 /bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin: /usr/sbin:/sbin:/home/Christine/bin) $ $yumlistemacs […] AvailablePackages emacs.x86_641:23.1-25.el6base TheemacseditorpackageisnotcurrentlyinstalledonthisCentOSdistribution.However, it is available to be installed. (For a more thorough discussion on displaying installed software,seeChapter9). ForaDebian-baseddistribution,checkfortheemacseditorpackagebyusingthe which and/orapt-cacheshowcommandasshownonthisUbuntudistribution: $whichemacs $ $sudoapt-cacheshowemacs Package:emacs Priority:optional Section:editors Installed-Size:25 […] Description-en:GNUEmacseditor(metapackage) GNUEmacsistheextensibleself-documentingtexteditor. Thisisametapackagethatwillalwaysdependonthelatest recommendedEmacsrelease. Description-md5:21fb7da111336097a2378959f6d6e6a8 Bugs:https://bugs.launchpad.net/ubuntu/+filebug Origin:Ubuntu Supported:5y $ The whichcommandoperatesalittledifferentlyhere.Whenitdoesnotfindtheinstalled command,itsimplyreturnsthebashshellprompt.Theemacseditorpackageisoptional for this Ubuntu distribution, but is available to be installed. The following shows the emacseditorbeinginstalledonUbuntu: $sudoapt-getinstallemacs Readingpackagelists…Done Buildingdependencytree Readingstateinformation…Done Thefollowingextrapackageswillbeinstalled: […] Installemacsen-commonforemacs24 emacsen-common:Handlinginstallofemacsenflavoremacs24 Wrote/etc/emacs24/site-start.d/00debian-vars.elc Wrote/usr/share/emacs24/site-lisp/debian-startup.elc Settingupemacs(45.0ubuntu1)… Processingtriggersforlibc-bin(2.19-0ubuntu6)… $ $whichemacs /usr/bin/emacs $ Nowwhenthe which command is used, it points to the emacs program file. The emacs editorisreadytobeusedonthisUbuntudistribution. FortheCentOSdistribution,installtheemacseditorusingtheyuminstallcommand: $sudoyuminstallemacs [sudo]passwordforChristine: […] SettingupInstallProcess ResolvingDependencies […] Installed: emacs.x86_641:23.1-25.el6 DependencyInstalled: emacs-common.x86_641:23.1-25.el6 libotf.x86_640:0.9.9-3.1.el6 m17n-db-datafiles.noarch0:1.5.5-1.1.el6 Complete! $ $whichemacs /usr/bin/emacs $ $yumlistemacs […] InstalledPackages emacs.x86_641:23.1-25.el6@base $ WiththeemacseditorsuccessfullyinstalledonyourLinuxdistribution,youcanbeginto exploreitsdifferentfeatures,staringwithusingitontheconsole. Usingemacsontheconsole Theconsolemodeversionofemacsisanothereditorthatuseslotsofkeycommandsto performeditingfunctions.TheemacseditoruseskeycombinationsinvolvingtheControl key(theCtrlkeyonthekeyboard)andtheMetakey.Inmostterminalemulatorpackages, theMetakeyismappedtotheAltkey.TheofficialemacsdocumentsabbreviatetheCtrl key as C- and the Meta key as M-. Thus, if you enter a Ctrl+x key combination, the documentshowsC-x.Thischapterdoesthesamesoasnottoconfuseyou. Exploringthebasicsofemacs Toeditafileusingemacs,fromthecommandline,enter: $emacsmyprog.c Theemacsconsolemodewindowappearswithashortintroductionandhelpscreen.Don’t be alarmed; as soon as you press a key, emacs loads the file into the active buffer and displaysthetext,asshowninFigure10.3. Figure10.3Editingafileusingtheemacseditorinconsolemode You’ll notice that the top of the console mode window shows a typical menu bar. Unfortunately,youcan’tusethemenubarinconsolemode,onlyingraphicalmode. Note Somecommandsinthissectionworkdifferentlythandescribed,ifyourunemacsin agraphicaldesktopenvironment.Touseemac’sconsolemodeinagraphicaldesktop environment,usetheemacs-nwcommand.Ifyouwanttouseemacs’graphical features,seethesection“UsingemacsinaGUI.” Unlike the vim editor, where you have to move into and out of insert mode to switch between entering commands and inserting text, the emacs editor has only one mode. If youtypeaprintablecharacter,emacsinsertsitatthecurrentcursorposition.Ifyoutypea command,emacsexecutesthecommand. Tomovethecursoraroundthebufferarea,youcanusethearrowkeysandthePageUp and PageDown keys, assuming that emacs detected your terminal emulator correctly. If not,thesecommandsmovethecursoraround: C-pmovesuponeline(thepreviouslineinthetext). C-bmovesleft(back)onecharacter. C-fmovesright(forward)onecharacter. C-nmovesdownoneline(thenextlineinthetext). Thesecommandsmakelongerjumpswiththecursorwithinthetext: M-fmovesright(forward)tothenextword. M-bmovesleft(backward)tothepreviousword. C-amovestothebeginningofthecurrentline. C-emovestotheendofthecurrentline. M-amovestothebeginningofthecurrentsentence. M-emovestotheendofthecurrentsentence. M-vmovesbackonescreenofdata. C-vmovesforwardonescreenofdata. M-<movesthefirstlineofthetext. M->movestothelastlineofthetext. You should know these commands for saving the editor buffer back into the file and exitingemacs: C-xC-ssavesthecurrentbuffercontentstothefile. C-zexitsemacsbutkeepsitrunninginyoursessionsoyoucancomebacktoit. C-xC-cexitsemacsandstopstheprogram. You’llnoticethattwoofthesefeaturesrequiretwokeycommands.The C-xcommandis called the extendcommand. This provides yet another whole set of commands to work with. Editingdata Theemacseditorisprettyrobustaboutinsertinganddeletingtextinthebuffer.Toinsert text,justmovethecursortothelocationwhereyouwanttoinsertthetextandstarttyping. To delete text, emacs uses the Backspace key to delete the character before the current cursorpositionandtheDeletekeytodeletethecharacteratthecurrentcursorlocation. Theemacseditoralsohascommandsforkillingtext.Thedifferencebetweendeletingtext andkillingtextisthatwhenyoukilltext,emacsplacesitinatemporaryareawhereyou canretrieveit(seethenextsection,“Copyingandpasting”).Deletedtextisgoneforever. Thesecommandsareforkillingtextinthebuffer: M-Backspacekillsthewordbeforethecurrentcursorposition. M-dkillsthewordafterthecurrentcursorposition. C-kkillsfromthecurrentcursorpositiontotheendoftheline. M-kkillsfromthecurrentcursorpositiontotheendofthesentence. Theemacseditoralsoincludesafancywayofmass-killingtext.Justmovethecursorto thestartoftheareayouwanttokill,andpresseitherthe C-@or C-Spacebarkeys.Then movethecursortotheendoftheareayouwanttokill,andpressthe C-wcommandkeys. Allthetextbetweenthetwolocationsiskilled. If you happen to make a mistake when killing text, the C-/ command undoes the kill commandandreturnsthedatatothestateitwasinbeforeyoukilledit. Copyingandpasting You’veseenhowtocutdatafromtheemacsbufferarea;nowit’stimetoseehowtopaste itsomewhereelse.Unfortunately,ifyouusethevimeditor,thisprocessmayconfuseyou whenyouusetheemacseditor. Inanunfortunatecoincidence,pastingdatainemacsiscalledyanking.Inthevimeditor, copyingiscalledyanking,whichiswhatmakesthisadifficultthingtorememberifyou happentousebotheditors. Afteryoukilldatausingoneofthekillcommands,movethecursortothelocationwhere you want to paste the data, and use the C-y command. This yanks the text out of the temporaryareaandpastesitatthecurrentcursorposition.The C-ycommandyanksthe text from the last kill command. If you’ve performed multiple kill commands, you can cyclethroughthemusingtheM-ycommand. Tocopytext,justyankitbackintothesamelocationyoukilleditfromandthenmoveto thenewlocationandusethe C-ycommandagain.Youcanyanktextbackasmanytimes asyoudesire. SearchingandReplacing SearchingfortextintheemacseditorisdonebyusingtheC-sandC-rcommands.TheCscommandperformsaforwardsearchinthebufferareafromthecurrentcursorposition to the end of the buffer, whereas the C-r command performs a backward search in the bufferareafromthecurrentcursorpositiontothestartofthebuffer. When you enter either the C-s or C-r command, a prompt appears in the bottom line, queryingyouforthetexttosearch.Youcanperformtwotypesofsearchesinemacs. Inanincrementalsearch,theemacseditorperformsthetextsearchinreal-timemodeas youtypetheword.Whenyoutypethefirstletter,ithighlightsalltheoccurrencesofthat letterinthebuffer.Whenyoutypethesecondletter,ithighlightsalltheoccurrencesofthe two-lettercombinationinthetextandsoonuntilyoucompletethetextyou’researching for. In a non-incremental search, press the Enter key after the C-s or C-r commands. This locksthesearchqueryintothebottomlineareaandallowsyoutotypethesearchtextin fullbeforesearching. Toreplaceanexistingtextstringwithanewtextstring,youmustusethe M-xcommand. Thiscommandrequiresatextcommand,alongwithparameters. The text command is replace-string. After typing the command, press the Enter key, andemacsqueriesyoufortheexistingtextstring.Afterenteringthat,presstheEnterkey againandemacsqueriesyouforthenewreplacementtextstring. Usingbuffersinemacs The emacs editor allows you to edit multiple files at the same time by having multiple bufferareas.Youcanloadfilesintoabufferandswitchbetweenbufferswhileediting. Toloadanewfileintoabufferwhileyou’reinemacs,usethe C-x C-fkeycombination. This is the emacs Find a File mode. It takes you to the bottom line in the window and allowsyoutoenterthenameofthefileyouwanttostarttoedit.Ifyoudon’tknowthe nameorlocationofthefile,justpresstheEnterkey.Thisbringsupafilebrowserinthe editwindow,asshowninFigure10.4. Figure10.4TheemacsFindaFilemodebrowser Fromhere,youcanbrowsetothefileyouwanttoedit.Totraverseupadirectorylevel,go to the double dot entry and press the Enter key. To traverse down a directory, go to the directoryentryandpresstheEnterkey.Whenyou’vefoundthefileyouwanttoedit,press theEnterkeyandemacsloadsitintoanewbufferarea. You can list the active buffer areas by pressing the C-x C-b extended command combination.Theemacseditorsplitstheeditorwindowanddisplaysalistofbuffersinthe bottomwindow.emacsprovidestwobuffersinadditiontoyourmaineditingbuffer: Ascratchareacalled*scratch* Amessageareacalled*Messages* ThescratchareaallowsyoutoenterLISPprogrammingcommandsaswellasenternotes toyourself.Themessageareashowsmessagesgeneratedbyemacswhileoperating.Ifany errorsoccurwhileusingemacs,theyappearinthemessagearea. Youcanswitchtoadifferentbufferareainthewindowintwoways: UseC-xotoswitchtothebufferlistingwindow.Usethearrowkeystomovetothe bufferareayouwantandpresstheEnterkey. UseC-xbtotypeinthenameofthebufferareayouwanttoswitchto. Whenyouselecttheoptiontoswitchtothebufferlistingwindow,emacsopensthebuffer areainthenewwindowarea.Theemacseditorallowsyoutohavemultiplewindowsopen inasinglesession.Thefollowingsectiondiscusseshowtomanagemultiplewindowsin emacs. Usingwindowsinconsolemodeemacs The console mode emacs editor was developed many years before the idea of graphical windowsappeared.However,itwasadvancedforitstime,inthatitcouldsupportmultiple editingwindowswithinthemainemacswindow. You can split the emacs editing window into multiple windows by using one of two commands: C-x2splitsthewindowhorizontallyintotwowindows. C-x3splitsthewindowverticallyintotwowindows. To move from one window to another, use the C-x o command. Notice that when you create a new window, emacs uses the buffer area from the original window in the new window.Afteryoumoveintothenewwindow,youcanusetheC-xC-fcommandtoload a new file or use one of the commands to switch to a different buffer area in the new window. Tocloseawindow,movetoitandusethe C-x0(that’sazero)command.Ifyouwantto close all the windows except the one you’re in, use the C-x 1 (that’s a numerical one) command. UsingemacsinaGUI If you use emacs from a GUI environment (such as the Unity or GNOME desktops), it startsingraphicalmode,asshowninFigure10.5. Figure10.5Theemacsgraphicalwindow If you’ve already used emacs in console mode, you should be fairly familiar with the graphicalmode.Allthekeycommandsareavailableasmenubaritems.Theemacsmenu barcontainsthefollowingitems: Fileallowsyoutoopenfilesinthewindow,createnewwindows,closewindows, savebuffers,andprintbuffers. Editallowsyoutocutandcopyselectedtexttotheclipboard,pasteclipboarddatato thecurrentcursorposition,searchfortext,andreplacetext. Optionsprovidessettingsformanymoreemacsfeatures,suchashighlighting,word wrap,cursortype,andsettingfonts. Buffersliststhecurrentbuffersavailableandallowsyoutoeasilyswitchbetween bufferareas. Toolsprovidesaccesstotheadvancedfeaturesinemacs,suchasthecommandline interfaceaccess,spellchecking,comparingtextbetweenfiles(calleddiff),sending ane-mailmessage,calendar,andthecalculator. Helpprovidestheemacsmanualonlineforaccesstohelponspecificemacs functions. Inadditiontothenormalgraphicalemacsmenubaritems,thereisoftenaseparateitem specifictothefiletypeintheeditorbuffer.Figure10.5showsopeningaCprogram,so emacsprovidedaCmenuitem,allowingadvancedsettingsforhighlightingCsyntax,and compiling,running,anddebuggingthecodefromacommandprompt. The graphical emacs window is an example of an older console application making the migration to the graphical world. Now that many Linux distributions provide graphical desktops (even on servers that don’t need them), graphical editors are becoming more commonplace. Popular Linux desktop environments (such as KDE and GNOME) have alsoprovidedgraphicaltexteditorsspecificallyfortheirenvironments,whicharecovered intherestofthischapter. ExploringtheKDEFamilyofEditors Ifyou’reusingaLinuxdistributionthatusestheKDEdesktop(seeChapter1),youhavea coupleofoptionswhenitcomestotexteditors.TheKDEprojectofficiallysupportstwo populartexteditors: KWrite:Asingle-screentext-editingpackage Kate:Afull-featured,multi-windowtext-editingpackage Bothoftheseeditorsaregraphicaltexteditorsthatcontainmanyadvancedfeatures.The Kate editor provides more advanced features, plus extra niceties not often found in standard text editors. This section describes each of the editors and shows some of the featuresyoucanusetohelpwithyourshellscriptediting. LookingattheKWriteeditor ThebasiceditorfortheKDEenvironmentisKWrite.Itprovidessimpleword-processing– styletextediting,alongwithsupportforcodesyntaxhighlightingandediting.Thedefault KWriteeditingwindowisshowninFigure10.6. Figure10.6ThedefaultKWritewindoweditingashellscriptprogram You can’t tell from Figure 10.6, but the KWrite editor recognizes several types of programming languages and uses color coding to distinguish constants, functions, and comments.Also,noticethatthe forloophasaniconthatlinkstheopeningandclosing braces.Thisiscalledafoldingmarker.Byclickingtheicon,youcancollapsethefunction intoasingleline.Thisisagreatfeaturewhenworkingthroughlargeapplications. TheKWriteeditingwindowprovidesfullcutandpastecapabilities,usingthemouseand thearrowkeys.Asinawordprocessor,youcanhighlightandcut(orcopy)textanywhere inthebufferareaandpasteitatanyotherplace. ToeditafileusingKWrite,youcaneitherselectKWritefromtheKDEmenusystemon yourdesktop(someLinuxdistributionsevencreateaPaneliconforit)orstartitfromthe commandlineprompt: $kwritefactorial.sh The kwrite command has several command line parameters you can use to customize howitstarts: —stdincausesKWritetoreaddatafromthestandardinputdeviceinsteadofafile. —encodingspecifiesacharacterencodingtypetouseforthefile. —linespecifiesalinenumberinthefiletostartatintheeditorwindow. —columnspecifiesacolumnnumberinthefiletostartatintheeditorwindow. TheKWriteeditorprovidesbothamenubarandatoolbaratthetopoftheeditwindow, allowingyoutoselectfeaturesandchangeconfigurationsettingsoftheKWriteeditor. Themenubarcontainstheseitems: Fileloads,saves,prints,andexportstextfromfiles. Editmanipulatestextinthebufferarea. Viewmanageshowthetextappearsintheeditorwindow. Bookmarkshandlepointerstoreturntospecificlocationsinthetext;thisoptionmay needtobeenabledintheconfigurations. Toolscontainsspecializedfeaturestomanipulatethetext. Settingsconfiguresthewaytheeditorhandlestext. Helpgivesyouinformationabouttheeditorandcommands. The Edit menu bar item provides commands for all your text-editing needs. Instead of havingtoremembercryptickeycommands(whichbytheway,KWritealsosupports),you canjustselectitemsintheEditmenubar,asshowninTable10.3. Table10.3TheKWriteEditMenuItems Item Undo Redo Cut Copy Description Reversesthelastactionoroperation Reversesthelastundoaction Deletestheselectedtextandplacesitintheclipboard Copiestheselectedtexttotheclipboard Insertsthecurrentcontentsoftheclipboardatthecurrentcursor position SelectAll Selectsalltextintheeditor Deselect Deselectsanytextthatiscurrentlyselected Overwrite Togglesinsertmodetooverwritemode,replacingtextwithnewtyped Mode textinsteadofjustinsertingthenewtext ProducestheFindTextdialogbox,whichallowsyoutocustomizea Find textsearch FindNext Repeatsthelastfindoperationforwardinthebufferarea FindPrevious Repeatsthelastfindoperationbackwardsinthebufferarea ProducestheReplaceWithdialogbox,whichallowsyoutocustomizea Replace textsearchandreplace FindSelected Findsthenextoccurrenceoftheselectedtext FindSelected Findsthepreviousoccurrenceoftheselectedtext Backwards ProducestheGotodialogbox,whichallowsyoutoenteralinenumber. GotoLine Thecursormovestothespecifiedline Paste TheFindfeaturehastwomodes.Normalmodeperformssimpletextsearchesandpower searches. Replace mode lets you do advanced searching and replacing if necessary. You toggle between the two modes using the green arrow in the Find section, as shown in Figure10.7. Figure10.7TheKWriteFindsection The Find power mode allows you to search not only with words, but with a regular expression (discussed in Chapter 20) for the search. You can use some other options to customizethesearchaswell,indicating,forexample,whetherornottoperformacasesensitivesearchortolookonlyforwholewordsinsteadoffindingthetextwithinwords. TheToolsmenubaritemprovidesseveralhandyfeaturesforworkingwiththetextinthe bufferarea.Table10.4describesthetoolsavailableinKWrite. Table10.4TheKWriteTools Tool ReadOnly Mode Encoding Spelling Spelling (fromcursor) Spellcheck Selection Indent Unindent Clean Indentation Align Uppercase Lowercase Capitalize JoinLines WordWrap Document Description Locksthetextsonochangescanbemadewhileintheeditor Setsthecharactersetencodingusedbythetext Startsthespell-checkprogramatthestartofthetext Startsthespell-checkprogramfromthecurrentcursorposition Startsthespell-checkprogramonlyontheselectedsectionoftext Increasestheparagraphindentationbyone Decreasestheparagraphindentationbyone Returnsallparagraphindentationtotheoriginalsettings Forcesthecurrentlineortheselectedlinestoreturntothedefault indentationsettings Setstheselectedtext,orthecharacteratthecurrentcursorposition,to uppercase Setstheselectedtext,orthecharacteratthecurrentcursorposition,to lowercase Capitalizesthefirstletteroftheselectedtextorthewordatthecurrent cursorposition Combinestheselectedlines,orthelineatthecurrentcursorpositionand thenextline,intooneline Enableswordwrappinginthetext.Ifalineextendspasttheeditor windowedge,thelinecontinuesonthenextline. Therearelotsoftoolsforasimpletexteditor! TheSettingsmenuincludestheConfigureEditordialogbox,showninFigure10.8. Figure10.8TheKWriteConfigureEditordialogbox The Configuration dialog box uses icons on the left side for you to select the feature in KWritetoconfigure.Whenyouselectanicon,therightsideofthedialogboxshowsthe configurationsettingsforthefeature. The Appearance feature allows you to set several features that control how the text appears in the text editor window. You can enable word wrap, line numbers (great for programmers), and the folder markers from here. With the Fonts & Colors feature, you cancustomizethecompletecolorschemefortheeditor,determiningwhatcolorstomake eachcategoryoftextintheprogramcode. LookingattheKateeditor TheKateeditoristheflagshipeditorfortheKDEProject.Itusesthesametexteditoras theKWriteapplication(somostofthosefeaturesarethesame),butitincorporateslotsof otherfeaturesintoasinglepackage. Tip IfyoufindthattheKateeditorhasnotbeeninstalledwithyourKDEdesktop environment,youcaneasilyinstallit(seeChapter9).Thepackagenamethat containsKateiskdesdk. WhenyoustarttheKateeditorfromtheKDEmenusystem,thefirstthingyounoticeis thattheeditordoesn’tstart!Instead,yougetadialogbox,asshowninFigure10.9. Figure10.9TheKatesessiondialogbox TheKateeditorhandlesfilesinsessions.Youcanhavemultiplefilesopeninasession, andyoucanhavemultiplesessionssaved.WhenyoustartKate,itprovidesyouwiththe choiceofwhichsessiontoreturnto.WhenyoucloseyourKatesession,itremembersthe documentsyouhadopenanddisplaysthemthenexttimeyoustartKate.Thisallowsyou to easily manage files from multiple projects by using separate workspaces for each project. Afterselectingasession,youseethemainKateeditorwindow,showninFigure10.10. Figure10.10ThemainKateeditingwindow The left side frame shows the documents currently open in the session. You can switch between documents just by clicking the document name. To edit a new file, click the FilesystemBrowsertabontheleftside.Theleftframeisnowafullgraphicalfilesystem browser,allowingyoutographicallybrowsetolocateyourfiles. AgreatfeatureoftheKateeditoristhebuilt-interminalwindow,showninFigure10.11. Figure10.11TheKatebuilt-interminalwindow The terminal tab at the bottom of the text editor window starts the built-in terminal emulator in Kate (using the KDE Konsole terminal emulator). This feature horizontally splitsthecurrenteditingwindow,creatinganewwindowwithKonsolerunninginit.You can now enter command line commands, start programs, or check on system settings without having to leave the editor! To close the terminal window, just type exit at the commandprompt. As you can tell from the terminal feature, Kate also supports multiple windows. The Windowmenubaritem(View)providesoptionstoperformthesetasks: CreateanewKatewindowusingthecurrentsession Splitthecurrentwindowverticallytocreateanewwindow Splitthecurrentwindowhorizontallytocreateanewwindow Closethecurrentwindow TosettheconfigurationsettingsinKate,selecttheConfigureKateitemundertheSettings menubaritem.TheConfigurationdialogbox,showninFigure10.12,appears. Figure10.12TheKateconfigurationsettingsdialogbox NoticethattheEditorsettingsareaisexactlythesameasforKWrite.Thisisbecausethe twoeditorssharethesametexteditorengine.TheApplicationsettingsareaallowsyouto configuresettingsfortheKateitems,suchascontrollingsessions(showninFigure10.12), the documents list, and the filesystem browser. Kate also supports external plug-in applications,whichcanbeactivatedhere. ExploringtheGNOMEEditor Ifyou’reworkingonaLinuxsystemusingtheGNOMEorUnitydesktopenvironment, there’sagraphicaltexteditorthatyoucanuseaswell.Thegedittexteditorisabasictext editor,withafewadvancedfeaturesthrowninjustforfun.Thissectionwalksyouthrough thefeaturesofgeditanddemonstrateshowtouseitforyourshellscriptprogramming. Startinggedit MostGNOMEdesktopenvironmentsincludegeditintheAccessoriesPanelmenuitem. FortheUnitydesktopenvironment,gotoDash⇨Searchandtypegedit.Ifyoucan’tfind gedit via the menu system, you can start it from the command line prompt in a GUI terminalemulator: $geditfactorial.shmyprog.c When you start gedit with multiple files, it loads all the files into separate buffers and displayseachoneasatabbedwindowwithinthemaineditorwindow,asshowninFigure 10.13. Figure10.13Thegeditmaineditorwindow The left frame in the gedit main editor window shows the documents you’re currently editing. If your gedit doesn’t show the left frame when started, you can press the F9 functionkeyorenableSidePanefromtheViewmenu. Note Differentdesktopsmayhavegeditoptionsthatareavailableinslightlydifferent menulocationsthanshowninthesefigures.Additionaloptionsmayalsobe available.Consultyourdistribution’sgeditHelpmenuformoreassistance. Therightsideshowsthetabbedwindowsthatcontainthebuffertext.Ifyouhoveryour mousepointerovereachtab,adialogboxappears,showingthefullpathnameofthefile, theMIMEtype,andthecharactersetencodingituses. Understandingbasicgeditfeatures Inadditiontotheeditorwindows,geditusesbothamenubarandtoolbarthatallowyouto setfeaturesandconfiguresettings.Thetoolbarprovidesquickaccesstomenubaritems. Thesemenubaritemsareavailable: Filehandlesnewfiles,savesexistingfiles,andprintsfiles. Editmanipulatestextintheactivebufferareaandsetstheeditorpreferences. Viewsetstheeditorfeaturestodisplayinthewindowandsetsthetexthighlighting mode. Searchfindsandreplacestextintheactiveeditorbufferarea. Toolsaccessesplug-intoolsinstalledingedit. Documentsmanagesfilesopeninthebufferareas. Helpprovidesaccesstothefullgeditmanual. Thereshouldn’tbeanythingtoosurprisinghere.TheEditmenucontainsthestandardcut, copy,andpastefunctions,alongwithaneatfeaturethatallowsyoutoeasilyenterthedate andtimeinthetextinseveraldifferentformats.TheSearchmenuprovidesastandardfind function,whichproducesadialogboxwhereyoucanenterthetexttofind,alongwiththe capabilitytoselecthowthefindfeatureshouldwork(matchingcase,matchingthewhole word, and the search direction). It also provides an incremental search feature, which worksinreal-timemode,findingtextasyoutypethecharactersoftheword. Settingpreferences TheEditmenucontainsaPreferencesitem,whichproducesthegeditPreferencesdialog box,showninFigure10.14. Figure10.14TheGNOMEdesktopgeditPreferencesdialogbox Thisiswhereyoucancustomizetheoperationofthegediteditor.ThePreferencesdialog boxcontainsfivetabbedareasforsettingthefeaturesandbehavioroftheeditor. SettingViewpreferences TheViewtabprovidesoptionsforhowgeditdisplaysthetextintheeditorwindow: TextWrapping:Determineshowtohandlelonglinesoftextintheeditor.The Enablingtextwrappingoptionwrapslonglinestothenextlineoftheeditor.TheDo NotSplitWordsOverTwoLinesoptionpreventstheauto-insertingofhyphensinto longwords,topreventthembeingsplitbetweentwolines. LineNumbers:Displayslinenumbersintheleftmarginintheeditorwindow. CurrentLine:Highlightsthelinewherethecursoriscurrentlypositioned,enabling youtoeasilyfindthecursorposition. RightMargin:Enablestherightmarginandallowsyoutosethowmanycolumns shouldbeintheeditorwindow.Thedefaultvalueis80columns. BracketMatching:Whenenabled,highlightsbracketpairsinprogrammingcode, allowingyoutoeasilymatchbracketsinif-thenstatements,forandwhileloops, andothercodingelementsthatusebrackets. The line-numbering and bracket-matching features provide an environment for programmerstotroubleshootcodethat’snotoftenfoundintexteditors. SettingEditorpreferences The Editor tab provides options for how the gedit editor handles tabs and indentation, alongwithhowfilesaresaved: TabStops:SetsthenumberofspacesskippedwhenyoupresstheTabkey.The defaultvalueiseight.Thisfeaturealsoincludesacheckboxthat,whenselected, insertsspacesinsteadofatabskip. AutomaticIndentation:Whenenabled,causesgedittoautomaticallyindentlinesin thetextforparagraphsandcodeelements(suchasif-thenstatementsandloops). FileSaving:Providestwofeaturesforsavingfiles:whetherornottocreateabackup copyofthefilewhenopenedintheeditwindow,andwhetherornottoautomatically savethefileatapreselectedinterval. The auto-save feature is a great way to ensure that your changes are saved on a regular basistopreventcatastrophesfromcrashesorpoweroutages. SettingFont&Colorpreferences TheFont&Colorstaballowsyoutoconfigure(notsurprisingly)twoitems: Font:Allowsyoutoselectthedefaultfont,ortoselectacustomizedfontandfont sizefromadialogbox. ColorScheme:Allowsyoutoselectthedefaultcolorschemeusedfortext, background,selectedtext,andselectioncolors,orchooseacustomcolorforeach category. ThedefaultcolorsforgeditnormallymatchthestandardGNOMEdesktopthemeselected forthedesktop.Thesecolorswillchangetomatchtheschemeyouselectforthedesktop. Managingplug-ins The Plugins tab provides control over the plug-ins used in gedit. Plug-ins are separate programsthatcaninterfacewithgedittoprovideadditionalfunctionality. Severalplug-insareavailableforgedit,butnotallofthemareinstalledbydefault.Table 10.5describestheplug-insthatarecurrentlyavailableintheGNOMEdesktop’sgedit. Table10.5TheGNOMEdesktopgeditPlug-ins Plug-In Description Change Changesthecaseofselectedtext Case Document Reportsthenumberofwords,lines,characters,andnon-spacecharacters Statistics External Providesashellenvironmentintheeditortoexecutecommandsandscripts Tools File Browser Pane Indent Lines Insert Date/Time Modelines Python Console Quick Open Snippets Sort Spell Checker TagList Providesasimplefilebrowsertomakeselectingfilesforeditingeasier Providesselectedlinestobeindentedorun-indented Insertsthecurrentdateandtimeinseveralformatsatthecurrentcursor position Providesemacs-stylemessagelinesatthebottomoftheeditorwindow Providesaninteractiveconsoleatthebottomoftheeditorwindowfor enteringcommandsusingthePythonprogramminglanguage Opensfilesdirectlyinthegediteditwindow Allowsyoutostoreoften-usedpiecesoftextforeasyretrievalanywherein thetext Quicklysortstheentirefileorselectedtext Providesdictionaryspellcheckingforthetextfile Providesalistofcommonlyusedstringsyoucaneasilyenterintoyourtext Plug-insthatareenabledshowacheckmarkinthecheckboxnexttotheirname.Some plug-ins,suchastheExternalToolsplug-in,alsoprovideadditionalconfigurationfeatures afteryouselectthem.Itallowsyoutosetashortcutkeytostarttheterminal,wheregedit displaysoutput,andthecommandtousetostarttheshellsession. Unfortunately,notallplug-insareinstalledinthesameplaceinthegeditmenubar.Some plug-insappearintheToolsmenubaritem(suchastheSpellCheckerandExternalTools plug-ins), while others appear in the Edit menu bar item (such as the Change Case and InsertDate/Timeplug-ins). ThischapterhascoveredjustafewofthetexteditorsavailableonLinux.Ifyoufindthat thetexteditorsdescribedheredon’tmeetyourneeds,youhaveoptions.ManymoreLinux editorsareavailable,suchasgeany,Eclipse,jed,Bluefish,andleafpadtonameafew.All theseeditorscanhelpyouasyoubeginyourbashshellscriptwritingjourney. Summary Whenitcomestocreatingshellscripts,youneedsometypeoftexteditor.Severalpopular texteditorsareavailablefortheLinuxenvironment.ThemostpopulareditorintheUnix world,vi,hasbeenportedtotheLinuxworldasthevimeditor.Thevimeditorprovides simpletexteditingfromtheconsole,usingarudimentaryfull-screengraphicalmode.The vim editor provides many advanced editor features, such as text searching and replacement. AnothereditorthathasbeenportedfromtheUnixworldtoLinuxisthenanotexteditor. The vim editor can be rather complex, but the nano editor offers simplicity. The nano editorallowsquicktexteditinginconsolemode. AnotherpopularUnixeditor—emacs—hasalsomadeitswaytotheLinuxworld.The Linux version of emacs has both console and a graphical mode, making it the bridge between the old world and the new. The emacs editor provides multiple buffer areas, allowingyoutoeditmultiplefilessimultaneously. TheKDEProjectcreatedtwoeditorsforuseintheKDEdesktop.TheKWriteeditorisa simple editor that provides the basic text-editing features, along with a few advanced features, such as syntax highlighting for programming code, line numbering, and code folding. The Kate editor provides more advanced features for programmers. One great feature in Kate is a built-in terminal window. You can open a command line interface session directly in the Kate editor without having to open a separate terminal emulator window. The Kate editor also allows you to open multiple files, providing different windowsforeachopenedfile. TheGNOMEProjectalsoprovidesasimpletexteditorforprogrammers.Thegediteditor is a basic text editor that provides some advanced features such as code syntax highlightingandlinenumbering,butitwasdesignedtobeabare-boneseditor.Tospruce up the gedit editor, developers created plug-ins, which expand the features available in gedit.Currentplug-insincludeaspell-checker,aterminalemulator,andafilebrowser. ThiswrapsupthebackgroundchaptersonworkingwiththecommandlineinLinux.The next part of the book dives into the shell-scripting world. The next chapter starts off by showingyouhowtocreateashellscriptfileandhowtorunitonyourLinuxsystem.It also shows you the basics of shell scripts, allowing you to create simple programs by stringingmultiplecommandstogetherintoascriptyoucanrun. PartII ShellScriptingBasics InThisPart 1. Chapter11BasicScriptBuilding 1. Chapter12UsingStructuredCommands 1. Chapter13MoreStructuredCommands 1. Chapter14HandlingUserInput 1. Chapter15PresentingData 1. Chapter16ScriptControl Chapter11 BasicScriptBuilding InThisChapter 1. Usingmultiplecommands 2. Creatingascriptfile 3. Displayingmessages 4. Usingvariables 5. Redirectinginputandoutput 6. Pipes 7. Performingmath 8. Exitingthescript Nowthatwe’vecoveredthebasicsoftheLinuxsystemandthecommandline,it’s timetostartcoding.Thischapterdiscussesthebasicsofwritingshellscripts.You needtoknowthesebasicconceptsbeforeyoucanstartwritingyourownshellscript masterpieces. UsingMultipleCommands So far you’ve seen how to use the command line interface (CLI) prompt of the shell to entercommandsandviewthecommandresults.Thekeytoshellscriptsistheabilityto enter multiple commands and process the results from each command, even possibly passingtheresultsofonecommandtoanother.Theshellallowsyoutochaincommands togetherintoasinglestep. Ifyouwanttoruntwocommandstogether,youcanenterthemonthesamepromptline, separatedwithasemicolon: $date;who MonFeb2115:36:09EST2014 Christinetty22014-02-2115:26 Samanthatty32014-02-2115:26 Timothytty12014-02-2115:26 usertty72014-02-1914:03(:0) userpts/02014-02-2115:21(:0.0) $ Congratulations,youjustwroteashellscript!Thissimplescriptusesjusttwo bashshell commands.The datecommandrunsfirst,displayingthecurrentdateandtime,followed by the output of the who command, showing who is currently logged on to the system. Usingthistechnique,youcanstringtogetherasmanycommandsasyouwish,uptothe maximumcommandlinecharactercountof255characters. Usingthistechniqueisfineforsmallscripts,butithasamajordrawback:Youmustenter the entire command at the command prompt every time you want to run it. Instead of having to manually enter the commands onto a command line, you can combine the commandsintoasimpletextfile.Whenyouneedtorunthecommands,justsimplyrun thetextfile. CreatingaScriptFile Toplaceshellcommandsinatextfile,firstyouneedtouseatexteditor(seeChapter10) tocreateafileandthenenterthecommandsintothefile. Whencreatingashellscriptfile,youmustspecifytheshellyouareusinginthefirstline ofthefile.Here’stheformatforthis: #!/bin/bash Inanormalshellscriptline,thepoundsign(#)isusedasacommentline.Acommentline inashellscriptisn’tprocessedbytheshell.However,thefirstlineofashellscriptfileisa special case, and the pound sign followed by the exclamation point tells the shell what shelltorunthescriptunder(yes,youcanbeusingabashshellandrunyourscriptusing anothershell). Afterindicatingtheshell,commandsareenteredontoeachlineofthefile,followedbya carriage return. As mentioned, comments can be added by using the pound sign. An examplelookslikethis: #!/bin/bash #Thisscriptdisplaysthedateandwho’sloggedon date who And that’s all there is to it. You can use the semicolon and put both commands on the same line if you want to, but in a shell script, you can list commands on separate lines. Theshellprocessescommandsintheorderinwhichtheyappearinthefile. Alsonoticethatanotherlinewasincludedthatstartswiththepoundsymbolandaddsa comment. Lines that start with the pound symbol (other than the first #! line) aren’t interpretedbytheshell.Thisisagreatwaytoleavecommentsforyourselfaboutwhat’s happening in the script, so when you come back to it two years later, you can easily rememberwhatyoudid. Savethisscriptinafilecalled test1,andyouarealmostready.Youneedtodoacouple ofthingsbeforeyoucanrunyournewshellscriptfile. Ifyoutryrunningthefilenow,you’llbesomewhatdisappointedtoseethis: $test1 bash:test1:commandnotfound $ Thefirsthurdletojumpisgettingthebashshelltofindyourscriptfile.Ifyouremember fromChapter6,theshellusesanenvironmentvariablecalled PATHtofindcommands.A quicklookatthePATHenvironmentvariabledemonstratesourproblem: $echo$PATH /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/bin:/usr/bin :/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/user/bin$ The PATH environment variable is set to look for commands only in a handful of directories.Togettheshelltofindthetest1script,weneedtodooneoftwothings: AddthedirectorywhereourshellscriptfileislocatedtothePATHenvironment variable. Useanabsoluteorrelativefilepathtoreferenceourshellscriptfileintheprompt. Tip SomeLinuxdistributionsaddthe$HOME/bindirectorytothePATHenvironment variable.Thiscreatesaplaceineveryuser’sHOMEdirectorytoplacefileswherethe shellcanfindthemtoexecute. Forthisexample,weusethesecondmethodtotelltheshellexactlywherethescriptfileis located.Rememberthattoreferenceafileinthecurrentdirectory,youcanusethesingle dotoperatorintheshell: $./test1 bash:./test1:Permissiondenied $ The shell found the shell script file just fine, but there’s another problem. The shell indicated that you don’t have permission to execute the file. A quick look at the file permissionsshouldshowwhat’sgoingonhere: $ls-ltest1 -rw-rw-r—1useruser73Sep2419:56test1 $ Whenthenew test1filewascreated,the umaskvaluedeterminedthedefaultpermission settings for the new file. Because the umask variable is set to 002 (see Chapter 7) in Ubuntu,thesystemcreatedthefilewithonlyread/writepermissionsforthefile’sowner andgroup. The next step is to give the file owner permission to execute the file, using the chmod command(seeChapter7): $chmodu+xtest1 $./test1 MonFeb2115:38:19EST2014 Christinetty22014-02-2115:26 Samanthatty32014-02-2115:26 Timothytty12014-02-2115:26 usertty72014-02-1914:03(:0) userpts/02014-02-2115:21(:0.0)$ Success!Nowallthepiecesareintherightplacestoexecutethenewshellscriptfile. DisplayingMessages Most shell commands produce their own output, which is displayed on the console monitorwherethescriptisrunning.Manytimes,however,youwillwanttoaddyourown textmessagestohelpthescriptuserknowwhatishappeningwithinthescript.Youcando thiswiththe echocommand.The echocommandcandisplayasimpletextstringifyou addthestringfollowingthecommand: $echoThisisatest Thisisatest $ Notice that by default you don’t need to use quotes to delineate the string you’re displaying. However, sometimes this can get tricky if you are using quotes within your string: $echoLet’sseeifthis’llwork Letsseeifthisllwork $ Theechocommanduseseitherdoubleorsinglequotestodelineatetextstrings.Ifyouuse themwithinyourstring,youneedtouseonetypeofquotewithinthetextandtheother typetodelineatethestring: $echo“Thisisatesttoseeifyou’repayingattention” Thisisatesttoseeifyou’repayingattention $echo‘Richsays“scriptingiseasy”.’ Richsays“scriptingiseasy”. $ Nowallthequotationmarksappearproperlyintheoutput. You can add echo statements anywhere in your shell scripts where you need to display additionalinformation: $cattest1 #!/bin/bash #Thisscriptdisplaysthedateandwho’sloggedon echoThetimeanddateare: date echo“Let’sseewho’sloggedintothesystem:” who $ Whenyourunthisscript,itproducesthefollowingoutput: $./test1 Thetimeanddateare: MonFeb2115:41:13EST2014 Let’sseewho’sloggedintothesystem: Christinetty22014-02-2115:26 Samanthatty32014-02-2115:26 Timothytty12014-02-2115:26 usertty72014-02-1914:03(:0) userpts/02014-02-2115:21(:0.0) $ That’s nice, but what if you want to echo a text string on the same line as a command output?Youcanusethe -nparameterforthe echostatementtodothat.Justchangethe firstechostatementlinetothis: echo-n“Thetimeanddateare:“ Youneedtousequotesaroundthestringtoensurethatthere’saspaceattheendofthe echoed string. The command output begins exactly where the string output stops. The outputnowlookslikethis: $./test1 Thetimeanddateare:MonFeb2115:42:23EST2014 Let’sseewho’sloggedintothesystem: Christinetty22014-02-2115:26 Samanthatty32014-02-2115:26 Timothytty12014-02-2115:26 usertty72014-02-1914:03(:0) userpts/02014-02-2115:21(:0.0) $ Perfect! The echo command is a crucial piece of shell scripts that interact with users. You’llfindyourselfusingitinmanysituations,especiallywhenyouwanttodisplaythe valuesofscriptvariables.Let’slookatthatnext. UsingVariables Just running individual commands from the shell script is useful, but this has its limitations.Often,you’llwanttoincorporateotherdatainyourshellcommandstoprocess information.Youcandothisbyusingvariables.Variablesallowyoutotemporarilystore informationwithintheshellscriptforusewithothercommandsinthescript.Thissection showshowtousevariablesinyourshellscripts. Environmentvariables You’ve already seen one type of Linux variable in action. Chapter 6 described the environment variables available in the Linux system. You can access these values from yourshellscriptsaswell. Theshellmaintainsenvironmentvariablesthattrackspecificsysteminformation,suchas thenameofthesystem,thenameoftheuserloggedintothesystem,theuser’ssystemID (calledUID),thedefaulthomedirectoryoftheuser,andthesearchpathusedbytheshell to find programs. You can display a complete list of active environment variables availablebyusingthesetcommand: $set BASH=/bin/bash […] HOME=/home/Samantha HOSTNAME=localhost.localdomain HOSTTYPE=i386 IFS=$’\t\n’ IMSETTINGS_INTEGRATE_DESKTOP=yes IMSETTINGS_MODULE=none LANG=en_US.utf8 LESSOPEN=’|/usr/bin/lesspipe.sh%s’ LINES=24 LOGNAME=Samantha […] You can tap into these environment variables from within your scripts by using the environment variable’s name preceded by a dollar sign. This is demonstrated in the followingscript: $cattest2 #!/bin/bash #displayuserinformationfromthesystem. echo“Userinfoforuserid:$USER” echoUID:$UID echoHOME:$HOME $ The $USER, $UID, and $HOME environment variables are used to display the pertinent informationaboutthelogged-inuser.Theoutputshouldlooksomethinglikethis: $chmodu+xtest2 $./test2 Userinfoforuserid:Samantha UID:1001 HOME:/home/Samantha $ Noticethattheenvironmentvariablesinthe echocommandsarereplacedbytheircurrent values when the script runs. Also notice that we were able to place the $USER system variable within the double quotation marks in the first string, and the shell script still figuredoutwhatwemeant.Thereisadrawbacktousingthismethod,however.Lookat whathappensinthisexample: $echo“Thecostoftheitemis$15” Thecostoftheitemis5 That is obviously not what was intended. Whenever the script sees a dollar sign within quotes,itassumesyou’rereferencingavariable.Inthisexample,thescriptattemptedto display the variable $1 (which was not defined) and then the number 5. To display an actualdollarsign,youmustprecedeitwithabackslashcharacter: $echo“Thecostoftheitemis\$15” Thecostoftheitemis$15 That’sbetter.Thebackslashallowedtheshellscripttointerpretthedollarsignasanactual dollarsignandnotavariable.Thenextsectionshowshowtocreateyourownvariablesin yourscripts. Note Youmayalsoseevariablesreferencedusingtheformat${variable}.Theextrabraces aroundthevariablenameareoftenusedtohelpidentifythevariablenamefromthe dollarsign. Uservariables Inadditiontotheenvironmentvariables,ashellscriptallowsyoutosetanduseyourown variableswithinthescript.Settingvariablesallowsyoutotemporarilystoredataanduseit throughoutthescript,makingtheshellscriptmorelikearealcomputerprogram. Uservariablescanbeanytextstringofupto20letters,digits,oranunderscorecharacter. Uservariablesarecasesensitive,sothevariable Var1isdifferentfromthevariable var1. Thislittleruleoftengetsnovicescriptprogrammersintrouble. Valuesareassignedtouservariablesusinganequalsign.Nospacescanappearbetween thevariable,theequalsign,andthevalue(anothertroublespotfornovices).Herearea fewexamplesofassigningvaluestouservariables: var1=10 var2=-57 var3=testing var4=“stillmoretesting” The shell script automatically determines the data type used for the variable value. Variables defined within the shell script maintain their values throughout the life of the shellscriptbutaredeletedwhentheshellscriptcompletes. Justlikesystemvariables,uservariablescanbereferencedusingthedollarsign: $cattest3 #!/bin/bash #testingvariables days=10 guest=“Katie” echo“$guestcheckedin$daysdaysago” days=5 guest=“Jessica” echo“$guestcheckedin$daysdaysago” $ Runningthescriptproducesthefollowingoutput: $chmodu+xtest3 $./test3 Katiecheckedin10daysago Jessicacheckedin5daysago $ Each time the variable is referenced, it produces the value currently assigned to it. It’s importanttorememberthatwhenreferencingavariablevalueyouusethedollarsign,but whenreferencingthevariabletoassignavaluetoit,youdonotusethedollarsign.Here’s anexampleofwhatImean: $cattest4 #!/bin/bash #assigningavariablevaluetoanothervariable value1=10 value2=$value1 echoTheresultingvalueis$value2 $ Whenyouusethevalueofthevalue1variableintheassignmentstatement,youmuststill usethedollarsign.Thiscodeproducesthefollowingoutput: $chmodu+xtest4 $./test4 Theresultingvalueis10 $ Ifyouforgetthedollarsignandmakethevalue2assignmentlinelooklikethis: value2=value1 yougetthefollowingoutput: $./test4 Theresultingvalueisvalue1 $ Without the dollar sign, the shell interprets the variable name as a normal text string, whichismostlikelynotwhatyouwanted. Commandsubstitution Oneofthemostusefulfeaturesofshellscriptsistheabilitytoextractinformationfrom the output of a command and assign it to a variable. After you assign the output to a variable, you can use that value anywhere in your script. This comes in handy when processingdatainyourscripts. Therearetwowaystoassigntheoutputofacommandtoavariable: Thebacktickcharacter(`) The$()format Becarefulwiththebacktickcharacter;itisnotthenormalsinglequotationmarkcharacter youareusedtousingforstrings.Becauseitisnotusedveryoftenoutsideofshellscripts, youmaynotevenknowwheretofinditonyourkeyboard.Youshouldbecomefamiliar withitbecauseit’sacrucialcomponentofmanyshellscripts.Hint:OnaU.S.keyboard,it isusuallyonthesamekeyasthetildesymbol(∼). Commandsubstitutionallowsyoutoassigntheoutputofashellcommandtoavariable. Althoughthisdoesn’tseemlikemuch,itisamajorbuildingblockinscriptprogramming. Youmusteithersurroundtheentirecommandlinecommandwithtwobacktickcharacters: testing=‘date’ orusethe$()format: testing=$(date) Theshellrunsthecommandwithinthecommandsubstitutioncharactersandassignsthe output to the variable testing. Notice that there are no spaces between the assignment equal sign and the command substitution character. Here’s an example of creating a variableusingtheoutputfromanormalshellcommand: $cattest5 #!/bin/bash testing=$(date) echo“Thedateandtimeare:”$testing $ Thevariabletestingreceivestheoutputfromthedatecommand,anditisusedintheecho statementtodisplayit.Runningtheshellscriptproducesthefollowingoutput: $chmodu+xtest5 $./test5 Thedateandtimeare:MonJan3120:23:25EDT2014 $ That’snotallthatexcitinginthisexample(youcouldjustaseasilyjustputthecommand intheechostatement),butafteryoucapturethecommandoutputinavariable,youcando anythingwithit. Here’sapopularexampleofhowcommandsubstitutionisusedtocapturethecurrentdate anduseittocreateauniquefilenameinascript: #!/bin/bash #copythe/usr/bindirectorylistingtoalogfile today=$(date+%y%m%d) ls/usr/bin-al>log.$today Thetodayvariableisassignedtheoutputofaformatteddatecommand.Thisisacommon techniqueusedtoextractdateinformationforlogfilenames.The+%y%m%dformatinstructs thedatecommandtodisplaythedateasatwo-digityear,month,andday: $date+%y%m%d 140131 $ Thescriptassignsthevaluetoavariable,whichisthenusedaspartofafilename.Thefile itself contains the redirected output (discussed in the “Redirecting Input and Output” section)ofadirectorylisting.Afterrunningthescript,youshouldseeanewfileinyour directory: -rw-r—r—1useruser769Jan3110:15log.140131 Thelogfileappearsinthedirectoryusingthevalueofthe $todayvariableaspartofthe filename.Thecontentsofthelogfilearethedirectorylistingfromthe/usr/bindirectory. Ifthescriptrunsthenextday,thelogfilenameislog.140201,thuscreatinganewfilefor thenewday. Caution Commandsubstitutioncreateswhat’scalledasubshelltoruntheenclosed command.Asubshellisaseparatechildshellgeneratedfromtheshellthat’srunning thescript.Becauseofthat,anyvariablesyoucreateinthescriptaren’tavailableto thesubshellcommand. Subshellsarealsocreatedifyourunacommandfromthecommandpromptusingthe ./path,buttheyaren’tcreatedifyoujustrunthecommandwithoutapath.However, ifyouuseabuilt-inshellcommand,thatdoesn’tgenerateasubshell.Becarefulwhen runningscriptsfromthecommandprompt! RedirectingInputandOutput Sometimes, you want to save the output from a command instead of just having it displayedonthemonitor.Thebashshellprovidesafewdifferentoperatorsthatallowyou to redirect the output of a command to an alternative location (such as a file). Redirection can be used for input as well as output, redirecting a file to a command for input.Thissectiondescribeswhatyouneedtodotouseredirectioninyourshellscripts. Outputredirection Themostbasictypeofredirectionissendingoutputfromacommandtoafile.Thebash shellusesthegreater-thansymbol(>)forthis: command>outputfile Anything that would appear on the monitor from the command instead is stored in the outputfilespecified: $date>test6 $ls-ltest6 -rw-r—r—1useruser29Feb1017:56test6 $cattest6 ThuFeb1017:56:58EDT2014 $ The redirect operator created the file test6 (using the default umask settings) and redirectedtheoutputfromthedatecommandtothe test6file.Iftheoutputfilealready exists,theredirectoperatoroverwritestheexistingfilewiththenewfiledata: $who>test6 $cattest6 userpts/0Feb1017:55 $ Nowthecontentsofthetest6filecontaintheoutputfromthewhocommand. Sometimes,insteadofoverwritingthefile’scontents,youmayneedtoappendoutputfrom acommandtoanexistingfile—forexample,ifyou’recreatingalogfiletodocumentan actiononthesystem.Inthissituation,youcanusethedoublegreater-thansymbol(>>)to appenddata: $date>>test6 $cattest6 userpts/0Feb1017:55 ThuFeb1018:02:14EDT2014 $ The test6filestillcontainstheoriginaldatafromthe whocommandprocessedearlier— andnowitcontainsthenewoutputfromthedatecommand. Inputredirection Input redirection is the opposite of output redirection. Instead of taking the output of a command and redirecting it to a file, input redirection takes the content of a file and redirectsittoacommand. Theinputredirectionsymbolistheless-thansymbol(<): command<inputfile Theeasywaytorememberthisisthatthecommandisalwayslistedfirstinthecommand line, and the redirection symbol “points” to the way the data is flowing. The less-than symbolindicatesthatthedataisflowingfromtheinputfiletothecommand. Here’sanexampleofusinginputredirectionwiththewccommand: $wc<test6 21160 $ Thewccommandprovidesacountoftextinthedata.Bydefault,itproducesthreevalues: Thenumberoflinesinthetext Thenumberofwordsinthetext Thenumberofbytesinthetext Byredirectingatextfiletothewccommand,youcangetaquickcountofthelines,words, andbytesinthefile.Theexampleshowsthatthereare2lines,11words,and60bytesin thetest6file. Anothermethodofinputredirectioniscalledinlineinputredirection.Thismethodallows youtospecifythedataforinputredirectiononthecommandlineinsteadofinafile.This mayseemsomewhatoddatfirst,butafewapplicationsareavailableforthisprocess(such asthoseshowninthe“PerformingMath”section). The inline input redirection symbol is the double less-than symbol (<<). Besides this symbol,youmustspecifyatextmarkerthatdelineatesthebeginningandendofthedata usedforinput.Youcanuseanystringvalueforthetextmarker,butitmustbethesameat thebeginningofthedataandtheendofthedata: command<<marker data marker Whenusinginlineinputredirectiononthecommandline,theshellpromptsfordatausing the secondary prompt, defined in the PS2 environment variable (see Chapter 6). Here’s howthislookswhenyouuseit: $wc<<EOF >teststring1 >teststring2 >teststring3 >EOF 3942 $ Thesecondarypromptcontinuestopromptformoredatauntilyouenterthestringvalue forthetextmarker.The wccommandperformstheline,word,andbytecountsofthedata suppliedbytheinlineinputredirection. Pipes Sometimes, you need to send the output of one command to the input of another command.Thisispossibleusingredirection,butsomewhatclunky: $rpm-qa>rpm.list $sort<rpm.list abrt-1.1.14-1.fc14.i686 abrt-addon-ccpp-1.1.14-1.fc14.i686 abrt-addon-kerneloops-1.1.14-1.fc14.i686 abrt-addon-python-1.1.14-1.fc14.i686 abrt-desktop-1.1.14-1.fc14.i686 abrt-gui-1.1.14-1.fc14.i686 abrt-libs-1.1.14-1.fc14.i686 abrt-plugin-bugzilla-1.1.14-1.fc14.i686 abrt-plugin-logger-1.1.14-1.fc14.i686 abrt-plugin-runapp-1.1.14-1.fc14.i686 acl-2.2.49-8.fc14.i686 […] TherpmcommandmanagesthesoftwarepackagesinstalledonsystemsusingtheRedHat Package Management system (RPM), such as the Fedora system as shown. When used with the -qa parameters, it produces a list of the existing packages installed, but not necessarily in any specific order. If you’re looking for a specific package or group of packages,itcanbedifficulttofinditusingtheoutputoftherpmcommand. Usingthestandardoutputredirection,theoutputwasredirectedfromtherpmcommandto afile,called rpm.list.Afterthecommandfinished,the rpm.listfilecontainedalistof alltheinstalledsoftwarepackagesonmysystem.Next,inputredirectionwasusedtosend the contents of the rpm.list file to the sort command to sort the package names alphabetically. Thatwasuseful,butagain,asomewhatclunkywayofproducingtheinformation.Instead of redirecting the output of a command to a file, you can redirect the output to another command.Thisprocessiscalledpiping. Likethecommandsubstitutionbacktick,thesymbolforpipingisnotusedoftenoutsideof shellscripting.Thesymbolistwoverticallines,oneabovetheother.However,the pipe symboloftenlookslikeasingleverticallineinprint(|).OnaU.S.keyboard,itisusually onthesamekeyasthebackslash(∖).The pipeisputbetweenthecommandstoredirect theoutputfromonetotheother: command1|command2 Don’tthinkofpipingasrunningtwocommandsbacktoback.TheLinuxsystemactually runsbothcommandsatthesametime,linkingthemtogetherinternallyinthesystem.As the first command produces output, it’s sent immediately to the second command. No intermediatefilesorbufferareasareusedtotransferthedata. Now,usingpipingyoucaneasilypipetheoutputoftherpmcommanddirectlytothesort commandtoproduceyourresults: $rpm-qa|sort abrt-1.1.14-1.fc14.i686 abrt-addon-ccpp-1.1.14-1.fc14.i686 abrt-addon-kerneloops-1.1.14-1.fc14.i686 abrt-addon-python-1.1.14-1.fc14.i686 abrt-desktop-1.1.14-1.fc14.i686 abrt-gui-1.1.14-1.fc14.i686 abrt-libs-1.1.14-1.fc14.i686 abrt-plugin-bugzilla-1.1.14-1.fc14.i686 abrt-plugin-logger-1.1.14-1.fc14.i686 abrt-plugin-runapp-1.1.14-1.fc14.i686 acl-2.2.49-8.fc14.i686 […] Unless you’re a (very) quick reader, you probably couldn’t keep up with the output generatedbythiscommand.Becausethepipingfeatureoperatesinrealtime,assoonas the rpmcommandproducesdata,the sortcommandgetsbusysortingit.Bythetimethe rpmcommandfinishesoutputtingdata,the sortcommandalreadyhasthedatasortedand startsdisplayingitonthemonitor. There’s no limit to the number of pipes you can use in a command. You can continue pipingtheoutputofcommandstoothercommandstorefineyouroperation. Inthiscase,becausetheoutputofthe sortcommandzoomsbysoquickly,youcanuse one of the text paging commands (such as less or more) to force the output to stop at everyscreenofdata: $rpm-qa|sort|more Thiscommandsequencerunsthe rpmcommand, pipestheoutputtothe sortcommand, andthen pipesthatoutputtothe morecommandtodisplaythedata,stoppingafterevery screen of information. This now lets you pause and read what’s on the display before continuing,asshowninFigure11.1. Figure11.1Usingpipingtosenddatatothemorecommand Togetevenfancier,youcanuseredirectionalongwithpipingtosaveyouroutputtoafile: $rpm-qa|sort>rpm.list $morerpm.list abrt-1.1.14-1.fc14.i686 abrt-addon-ccpp-1.1.14-1.fc14.i686 abrt-addon-kerneloops-1.1.14-1.fc14.i686 abrt-addon-python-1.1.14-1.fc14.i686 abrt-desktop-1.1.14-1.fc14.i686 abrt-gui-1.1.14-1.fc14.i686 abrt-libs-1.1.14-1.fc14.i686 abrt-plugin-bugzilla-1.1.14-1.fc14.i686 abrt-plugin-logger-1.1.14-1.fc14.i686 abrt-plugin-runapp-1.1.14-1.fc14.i686 acl-2.2.49-8.fc14.i686 […] Asexpected,thedataintherpm.listfileisnowsorted! By far one of the most popular uses of piping is piping the results of commands that produce long output to the more command. This is especially common with the ls command,asshowninFigure11.2. Figure11.2Usingthemorecommandwiththelscommand Thels-lcommandproducesalonglistingofallthefilesinthedirectory.Fordirectories withlotsoffiles,thiscanbequitealisting.Bypipingtheoutputtothe morecommand, youforcetheoutputtostopattheendofeveryscreenofdata. PerformingMath Another feature crucial to any programming language is the ability to manipulate numbers. Unfortunately, for shell scripts this process is a bit awkward. There are two differentwaystoperformmathematicaloperationsinyourshellscripts. Theexprcommand Originally, the Bourne shell provided a special command that was used for processing mathematicalequations.Theexprcommandallowedtheprocessingofequationsfromthe commandline,butitisextremelyclunky: $expr1+5 6 The exprcommandrecognizesafewdifferentmathematicalandstringoperators,shown inTable11.1. Table11.1TheexprCommandOperators Operator ARG1|ARG2 Description ReturnsARG1ifneitherargumentisnullorzero;otherwise,returns ARG2 STRING:REGEXP ReturnsARG1ifneitherargumentisnullorzero;otherwise,returns 0 Returns1ifARG1islessthanARG2;otherwise,returns0 Returns1ifARG1islessthanorequaltoARG2;otherwise,returns0 Returns1ifARG1isequaltoARG2;otherwise,returns0 Returns1ifARG1isnotequaltoARG2;otherwise,returns0 Returns1ifARG1isgreaterthanorequaltoARG2;otherwise, returns0 Returns1ifARG1isgreaterthanARG2;otherwise,returns0 ReturnsthearithmeticsumofARG1andARG2 ReturnsthearithmeticdifferenceofARG1andARG2 ReturnsthearithmeticproductofARG1andARG2 ReturnsthearithmeticquotientofARG1dividedbyARG2 ReturnsthearithmeticremainderofARG1dividedbyARG2 ReturnsthepatternmatchifREGEXPmatchesapatterninSTRING matchSTRING REGEXP ReturnsthepatternmatchifREGEXPmatchesapatterninSTRING substrSTRINGPOS LENGTH ReturnsthesubstringLENGTHcharactersinlength,startingat positionPOS(startingat1) ReturnspositioninSTRINGwhereCHARSisfound;otherwise, returns0 ARG1&ARG2 ARG1<ARG2 ARG1<=ARG2 ARG1=ARG2 ARG1!=ARG2 ARG1>=ARG2 ARG1>ARG2 ARG1+ARG2 ARG1-ARG2 ARG1*ARG2 ARG1/ARG2 ARG1%ARG2 indexSTRINGCHARS lengthSTRING +TOKEN (EXPRESSION) ReturnsthenumericlengthofthestringSTRING InterpretsTOKENasastring,evenifit’sakeyword ReturnsthevalueofEXPRESSION Although the standard operators work fine in the expr command, the problem occurs when using them from a script or the command line. Many of the expr command operatorshaveothermeaningsintheshell(suchastheasterisk).Usingtheminthe expr commandproducesoddresults: $expr5*2 expr:syntaxerror $ To solve this problem, you need to use the shell escape character (the backslash) to identifyanycharactersthatmaybemisinterpretedbytheshellbeforebeingpassedtothe exprcommand: $expr5\*2 10 $ Nowthat’sreallystartingtogetugly!Usingtheexprcommandinashellscriptisequally cumbersome: $cattest6 #!/bin/bash #Anexampleofusingtheexprcommand var1=10 var2=20 var3=$(expr$var2/$var1) echoTheresultis$var3 Toassigntheresultofamathematicalequationtoavariable,youhavetousecommand substitutiontoextracttheoutputfromtheexprcommand: $chmodu+xtest6 $./test6 Theresultis2 $ Fortunately,thebashshellhasanimprovementforprocessingmathematicaloperatorsas youshallseeinthenextsection. Usingbrackets The bash shell includes the expr command to stay compatible with the Bourne shell; however, it also provides a much easier way of performing mathematical equations. In bash, when assigning a mathematical value to a variable, you can enclose the mathematicalequationusingadollarsignandsquarebrackets($[operation]): $var1=$[1+5] $echo$var1 6 $var2=$[$var1*2] $echo$var2 12 $ Using brackets makes shell math much easier than with the expr command. This same techniquealsoworksinshellscripts: $cattest7 #!/bin/bash var1=100 var2=50 var3=45 var4=$[$var1*($var2-$var3)] echoThefinalresultis$var4 $ Runningthisscriptproducestheoutput: $chmodu+xtest7 $./test7 Thefinalresultis500 $ Also, notice that when using the square brackets method for calculating equations, you don’t need to worry about the multiplication symbol, or any other characters, being misinterpretedbytheshell.Theshellknowsthatit’snotawildcardcharacterbecauseitis withinthesquarebrackets. There’s one major limitation to performing math in the bash shell script. Look at this example: $cattest8 #!/bin/bash var1=100 var2=45 var3=$[$var1/$var2] echoThefinalresultis$var3 $ Nowrunitandseewhathappens: $chmodu+xtest8 $./test8 Thefinalresultis2 $ The bash shell mathematical operators support only integer arithmetic. This is a huge limitationifyou’retryingtodoanysortofreal-worldmathematicalcalculations. Note Thezshell(zsh)providesfullfloating-pointarithmeticoperations.Ifyourequire floating-pointcalculationsinyourshellscripts,youmightconsidercheckingoutthe zshell(discussedinChapter23). Afloating-pointsolution You can use several solutions for overcoming the bash integer limitation. The most popularsolutionusesthebuilt-inbashcalculator,calledbc. Thebasicsofbc Thebashcalculatorisactuallyaprogramminglanguagethatallowsyoutoenterfloatingpointexpressionsatacommandlineandtheninterpretstheexpressions,calculatesthem, andreturnstheresult.Thebashcalculatorrecognizesthese: Numbers(bothintegerandfloatingpoint) Variables(bothsimplevariablesandarrays) Comments(linesstartingwithapoundsignortheClanguage/**/pair) Expressions Programmingstatements(suchasif-thenstatements) Functions Youcanaccessthebashcalculatorfromtheshellpromptusingthebccommand: $bc bc1.06.95 Copyright1991-1994,1997,1998,2000,2004,2006FreeSoftwareFoundation, Inc. ThisisfreesoftwarewithABSOLUTELYNOWARRANTY. Fordetailstype‘warranty’. 12*5.4 64.8 3.156*(3+5) 25.248 quit $ Theexamplestartsoutbyenteringtheexpression 12*5.4.Thebashcalculatorreturns the answer. Each subsequent expression entered into the calculator is evaluated, and the resultisdisplayed.Toexitthebashcalculator,youmustenterquit. The floating-point arithmetic is controlled by a built-in variable called scale.Youmust setthisvaluetothedesirednumberofdecimalplacesyouwantinyouranswers,oryou won’tgetwhatyouwerelookingfor: $bc-q 3.44/5 0 scale=4 3.44/5 .6880 quit $ Thedefaultvalueforthe scalevariableiszero.Beforethe scalevalueisset,thebash calculator provides the answer to zero decimal places. After you set the scalevariable value to four, the bash calculator displays the answer to four decimal places. The -q commandlineparametersuppressesthelengthywelcomebannerfromthebashcalculator. Inadditiontonormalnumbers,thebashcalculatoralsounderstandsvariables: $bc-q var1=10 var1*4 40 var2=var1/5 printvar2 2 quit $ Afteravariablevalueisdefined,youcanusethevariablethroughoutthebashcalculator session.Theprintstatementallowsyoutoprintvariablesandnumbers. Usingbcinscripts Nowyoumaybewonderinghowthebashcalculatorisgoingtohelpyouwithfloatingpointarithmeticinyourshellscripts.Doyouremembercommandsubstitution?Yes,you canusethecommandsubstitutioncharactertorunabccommandandassigntheoutputto avariable!Thebasicformattouseisthis: variable=$(echo“options;expression”|bc) Thefirstportion, options,allowsyoutosetvariables.Ifyouneedtosetmorethanone variable, separate them using the semicolon. The expression parameter defines the mathematicalexpressiontoevaluateusing bc.Here’saquickexampleofdoingthisina script: $cattest9 #!/bin/bash var1=$(echo“scale=4;3.44/5”|bc) echoTheansweris$var1 $ Thisexamplesetsthescalevariabletofourdecimalplacesandthenspecifiesaspecific calculationfortheexpression.Runningthisscriptproducesthefollowingoutput: $chmodu+xtest9 $./test9 Theansweris.6880 $ Nowthat’sfancy!Youaren’tlimitedtojustusingnumbersfortheexpressionvalue.You canalsousevariablesdefinedintheshellscript: $cattest10 #!/bin/bash var1=100 var2=45 var3=$(echo“scale=4;$var1/$var2”|bc) echoTheanswerforthisis$var3 $ The script defines two variables, which are used within the expression sent to the bc command.Remembertousethedollarsigntosignifythevalueforthevariablesandnot thevariablesthemselves.Theoutputofthisscriptisasfollows: $./test10 Theanswerforthisis2.2222 $ And of course, after a value is assigned to a variable, that variable can be used in yet anothercalculation: $cattest11 #!/bin/bash var1=20 var2=3.14159 var3=$(echo“scale=4;$var1*$var1”|bc) var4=$(echo“scale=4;$var3*$var2”|bc) echoThefinalresultis$var4 $ This method works fine for short calculations, but sometimes you need to get more involvedwithyournumbers.Ifyouhavemorethanjustacoupleofcalculations,itgets confusingtryingtolistmultipleexpressionsonthesamecommandline. There’sasolutiontothisproblem.Thebccommandrecognizesinputredirection,allowing you to redirect a file to the bc command for processing. However, this also can get confusing,becauseyou’dneedtostoreyourexpressionsinafile. The best method is to use inline input redirection, which allows you to redirect data directlyfromthecommandline.Intheshellscript,youassigntheoutputtoavariable: variable=$(bc<<EOF options statements expressions EOF ) The EOF text string indicates the beginning and end of the inline redirection data. Rememberthatthecommandsubstitutioncharactersarestillneededtoassigntheoutput ofthebccommandtothevariable. Now you can place all the individual bash calculator elements on separate lines in the scriptfile.Here’sanexampleofusingthistechniqueinascript: $cattest12 #!/bin/bash var1=10.46 var2=43.67 var3=33.2 var4=71 var5=$(bc<<EOF scale=4 a1=($var1*$var2) b1=($var3*$var4) a1+b1 EOF ) echoThefinalanswerforthismessis$var5 $ Placingeachoptionandexpressiononaseparatelineinyourscriptmakesthingscleaner and easier to read and follow. The EOF string indicates the start and end of the data to redirecttothebccommand.Ofcourse,youmustusethecommandsubstitutioncharacters toindicatethecommandtoassigntothevariable. You’llalsonoticeinthisexamplethatyoucanassignvariableswithinthebashcalculator. It’simportanttorememberthatanyvariablescreatedwithinthebashcalculatorarevalid onlywithinthebashcalculatorandcan’tbeusedintheshellscript. ExitingtheScript Sofarinoursamplescripts,weterminatedthingsprettyabruptly.Whenwewerefinished with our last command, we just ended the script. There’s a more elegant way of completingthingsavailabletous. Everycommandthatrunsintheshellusesanexitstatustoindicatetotheshellthatit’s finishedprocessing.Theexitstatusisanintegervaluebetween0and255that’spassedby thecommandtotheshellwhenthecommandfinishesrunning.Youcancapturethisvalue anduseitinyourscripts. Checkingtheexitstatus Linux provides the $? special variable that holds the exit status value from the last command that executed. You must view or use the $? variable immediately after the command you want to check. It changes values to the exit status of the last command executedbytheshell: $date SatJan1510:01:30EDT2014 $echo$? 0 $ By convention, the exit status of a command that successfully completes is zero. If a commandcompleteswithanerror,thenapositiveintegervalueisplacedintheexitstatus: $asdfg -bash:asdfg:commandnotfound $echo$? 127 $ The invalid command returns an exit status of 127. There’s not much of a standard conventiontoLinuxerrorexitstatuscodes.However,youcanusetheguidelinesshownin Table11.2. Table11.2LinuxExitStatusCodes Code Description 0 Successfulcompletionofthecommand 1 Generalunknownerror 2 Misuseofshellcommand 126 Thecommandcan’texecute 127 Commandnotfound 128 Invalidexitargument 128+x FatalerrorwithLinuxsignalx 130 CommandterminatedwithCtrl+C 255 Exitstatusoutofrange Anexitstatusvalueof126indicatesthattheuserdidn’thavetheproperpermissionssetto executethecommand: $./myprog.c -bash:./myprog.c:Permissiondenied $echo$? 126 $ Another common error you’ll encounter occurs if you supply an invalid parameter to a command: $date%t date:invaliddate‘%t’ $echo$? 1 $ Thisgeneratesthegeneralexitstatuscodeof1,indicatingthatanunknownerroroccurred inthecommand. Theexitcommand Bydefault,yourshellscriptexitswiththeexitstatusofthelastcommandinyourscript: $./test6 Theresultis2 $echo$? 0 $ Youcanchangethattoreturnyourownexitstatuscode.Theexitcommandallowsyouto specifyanexitstatuswhenyourscriptends: $cattest13 #!/bin/bash #testingtheexitstatus var1=10 var2=30 var3=$[$var1+$var2] echoTheansweris$var3 exit5 $ Whenyouchecktheexitstatusofthescript,yougetthevalueusedastheparameterofthe exitcommand: $chmodu+xtest13 $./test13 Theansweris40 $echo$? 5 $ Youcanalsousevariablesintheexitcommandparameter: $cattest14 #!/bin/bash #testingtheexitstatus var1=10 var2=30 var3=$[$var1+$var2] exit$var3 $ Whenyourunthiscommand,itproducesthefollowingexitstatus: $chmodu+xtest14 $./test14 $echo$? 40 $ Youshouldbecarefulwiththisfeature,however,becausetheexitstatuscodescanonlygo upto255.Watchwhathappensinthisexample: $cattest14b #!/bin/bash #testingtheexitstatus var1=10 var2=30 var3=$[$var1*$var2] echoThevalueis$var3 exit$var3 $ Nowwhenyourunit,yougetthefollowing: $./test14b Thevalueis300 $echo$? 44 $ Theexitstatuscodeisreducedtofitinthe0to255range.Theshelldoesthisbyusing moduloarithmetic.The moduloofavalueistheremainderafteradivision.Theresulting numberistheremainderofthespecifiednumberdividedby256.Inthecaseof300(the resultvalue),theremainderis44,whichiswhatappearsastheexitstatuscode. InChapter12,you’llseehowyoucanusethe if-thenstatementtochecktheerrorstatus returnedbyacommandtoseewhetherthecommandwassuccessful. Summary Thebashshellscriptallowsyoutostringcommandstogetherintoascript.Themostbasic way to create a script is to separate multiple commands on the command line using a semicolon. The shell executes each command in order, displaying the output of each commandonthemonitor. Youcanalsocreateashellscriptfile,placingmultiplecommandsinthefilefortheshell toexecuteinorder.Theshellscriptfilemustdefinetheshellusedtorunthescript.Thisis doneinthefirstlineofthescriptfile,usingthe#!symbol,followedbythefullpathofthe shell. Within the shell script you can reference environment variable values by using a dollar sign in front of the variable. You can also define your own variables for use within the script, and assign values and even the output of a command by using the backtick characterorthe$()format.Thevariablevaluecanbeusedwithinthescriptbyplacinga dollarsigninfrontofthevariablename. The bash shell allows you to redirect both the input and output of a command from the standardbehavior.Youcanredirecttheoutputofanycommandfromthemonitordisplay toafilebyusingthegreater-thansymbol,followedbythenameofthefiletocapturethe output.Youcanappendoutputdatatoanexistingfilebyusingtwogreater-thansymbols. Theless-thansymbolisusedtoredirectinputtoacommand.Youcanredirectinputfroma filetoacommand. TheLinux pipecommand(thebrokenbarsymbol)allowsyoutoredirecttheoutputofa command directly to the input of another command. The Linux system runs both commandsatthesametime,sendingtheoutputofthefirstcommandtotheinputofthe secondcommandwithoutusinganyredirectfiles. Thebashshellprovidesacoupleofwaysforyoutoperformmathematicaloperationsin yourshellscripts.Theexprcommandisasimplewaytoperformintegermath.Inthebash shell, you can also perform basic math calculations by enclosing equations in square brackets, preceded by a dollar sign. To perform floating-point arithmetic, you need to utilizethebccalculatorcommand,redirectinginputfrominlinedataandstoringtheoutput inauservariable. Finally, the chapter discussed how to use the exit status in your shell script. Every commandthatrunsintheshellproducesanexitstatus.Theexitstatusisanintegervalue between0and255thatindicatesifthecommandcompletedsuccessfully,andifnot,what the reason may have been. An exit status of 0 indicates that the command completed successfully.Youcanusethe exitcommandinyourshellscripttodeclareaspecificexit statusuponthecompletionofyourscript. So far in your shell scripts, things have proceeded in an orderly fashion from one command to the next. In the next chapter, you’ll see how you can use some logic flow controltoalterwhichcommandsareexecutedwithinthescript. Chapter12 UsingStructuredCommands InThisChapter 1. Workingwiththeif-thenstatement 2. Nestingifs 3. Understandingthetestcommand 4. Testingcompoundconditions 5. Usingdoublebracketsandparentheses 6. Lookingatcase IntheshellscriptspresentedinChapter11,theshellprocessedeachindividual commandintheshellscriptintheorderitappeared.Thisworksoutfinefor sequentialoperations,whereyouwantallthecommandstoprocessintheproper order.However,thisisn’thowallprogramsoperate. Manyprogramsrequiresomesortoflogicflowcontrolbetweenthecommandsinthe script.Thereisawholecommandclassthatallowsthescripttoskipoverexecuted commandsbasedontestedconditions.Thesecommandsaregenerallyreferredtoas structuredcommands. Thestructuredcommandsallowyoutoaltertheoperationflowofaprogram.Quitea fewstructuredcommandsareavailableinthebashshell,sowe’lllookatthem individually.Inthischapter,welookatif-thenandcasestatements. Workingwiththeif-thenStatement The most basic type of structured command is the if-then statement. The if-then statementhasthefollowingformat: ifcommand then commands fi Ifyou’reusing if-thenstatementsinotherprogramminglanguages,thisformatmaybe somewhatconfusing.Inotherprogramminglanguages,theobjectaftertheifstatementis anequationthatisevaluatedfora TRUEor FALSEvalue.That’snothowthebashshell if statementworks. Thebashshell ifstatementrunsthecommanddefinedonthe ifline.Iftheexitstatusof the command (see Chapter 11) is zero (the command completed successfully), the commandslistedunderthethensectionareexecuted.Iftheexitstatusofthecommandis anything else, the then commands aren’t executed, and the bash shell moves on to the nextcommandinthescript.Thefistatementdelineatestheif-thenstatement’send. Here’sasimpleexampletodemonstratethisconcept: $cattest1.sh #!/bin/bash #testingtheifstatement ifpwd then echo“Itworked” fi $ Thisscriptusesthe pwdcommandonthe ifline.Ifthecommandcompletessuccessfully, the echo statement should display the text string. When you run this script from the commandline,yougetthefollowingresults: $./test1.sh /home/Christine Itworked $ The shell executed the pwd command listed on the if line. Because the exit status was zero,italsoexecutedtheechostatementlistedinthethensection. Here’sanotherexample: $cattest2.sh #!/bin/bash #testingabadcommand ifIamNotaCommand then echo“Itworked” fi echo“Weareoutsidetheifstatement” $ $./test2.sh ./test2.sh:line3:IamNotaCommand:commandnotfound Weareoutsidetheifstatement $ Inthisexample,wedeliberatelyusedacommand, IamNotaCommand,thatdoesnotworkin theifstatementline.Becausethisisabadcommand,itproducesanexitstatusthat’snonzero,andthebashshellskipsthe echostatementinthe thensection.Alsonoticethatthe errormessagegeneratedfromrunningthecommandintheifstatementstillappearsinthe script’s output. There may be times when you don’t want an error statement to appear. Chapter15discusseshowthiscanbeavoided. Note Youmightseeanalternativeformoftheif-thenstatementusedinsomescripts: ifcommand;then commands fi Byputtingasemicolonattheendofthecommandtoevaluate,youcanincludethe thenstatementonthesameline,whichlooksclosertohowif-thenstatementsare handledinsomeotherprogramminglanguages. Youarenotlimitedtojustonecommandinthe thensection.Youcanlistcommandsjust asintherestoftheshellscript.Thebashshelltreatsthecommandsasablock,executing all of them when the command in the if statement line returns a zero exit status or skippingallofthemwhenthecommandreturnsanon-zeroexitstatus: $cattest3.sh #!/bin/bash #testingmultiplecommandsinthethensection # testuser=Christine # ifgrep$testuser/etc/passwd then echo“Thisismyfirstcommand” echo“Thisismysecondcommand” echo“Icanevenputinothercommandsbesidesecho:” ls-a/home/$testuser/.b* fi $ The ifstatementlineusesthe grepcommenttosearchthe /etc/passwd file to see if a specificusernameiscurrentlyusedonthesystem.Ifthere’sauserwiththatlogonname, thescriptdisplayssometextandthenliststhebashfilesintheuser’sHOMEdirectory: $./test3.sh Christine:x:501:501:ChristineB:/home/Christine:/bin/bash Thisismyfirstcommand Thisismysecondcommand Icanevenputinothercommandsbesidesecho: /home/Christine/.bash_history/home/Christine/.bash_profile /home/Christine/.bash_logout/home/Christine/.bashrc $ However, if you set the testuser variable to a user that doesn’t exist on the system, nothinghappens: $cattest3.sh #!/bin/bash #testingmultiplecommandsinthethensection # testuser=NoSuchUser # ifgrep$testuser/etc/passwd then echo“Thisismyfirstcommand” echo“Thisismysecondcommand” echo“Icanevenputinothercommandsbesidesecho:” ls-a/home/$testuser/.b* fi $ $./test3.sh $ It’snotallthatexciting.Itwouldbeniceifwecoulddisplayalittlemessagesayingthat theusernamewasn’tfoundonthesystem.Well,wecan,usinganotherfeatureofthe ifthenstatement. Exploringtheif-then-elseStatement Intheif-thenstatement,youhaveonlyoneoptionforwhetheracommandissuccessful. If the command returns a non-zero exit status code, the bash shell just moves on to the next command in the script. In this situation, it would be nice to be able to execute an alternatesetofcommands.That’sexactlywhattheif-then-elsestatementisfor. Theif-then-elsestatementprovidesanothergroupofcommandsinthestatement: ifcommand then commands else commands fi When the command in the if statement line returns with a zero exit status code, the commandslistedinthe thensectionareexecuted,justasinanormal if-thenstatement. Whenthecommandinthe ifstatementlinereturnsanon-zeroexitstatuscode,thebash shellexecutesthecommandsintheelsesection. Nowyoucancopyandmodifythetestscripttoincludeanelsesection: $cptest3.shtest4.sh $ $nanotest4.sh $ $cattest4.sh #!/bin/bash #testingtheelsesection # testuser=NoSuchUser # ifgrep$testuser/etc/passwd then echo“Thebashfilesforuser$testuserare:” ls-a/home/$testuser/.b* echo else echo“Theuser$testuserdoesnotexistonthissystem.” echo fi $ $./test4.sh TheuserNoSuchUserdoesnotexistonthissystem. $ That’smoreuser-friendly.Justlikethethensection,theelsesectioncancontainmultiple commands.Thefistatementdelineatestheendoftheelsesection. Nestingifs Sometimes,youmustcheckforseveralsituationsinyourscriptcode.Forthesesituations, youcannesttheif-thenstatements: Tocheckifalogonnameisnotinthe /etc/passwdfileandyetadirectoryforthatuser stillexists,useanested if-thenstatement.Inthiscase,thenested if-thenstatementis withintheprimaryif-then-elsestatement’selsecodeblock: $ls-d/home/NoSuchUser/ /home/NoSuchUser/ $ $cattest5.sh #!/bin/bash #Testingnestedifs # testuser=NoSuchUser # ifgrep$testuser/etc/passwd then echo“Theuser$testuserexistsonthissystem.” else echo“Theuser$testuserdoesnotexistonthissystem.” ifls-d/home/$testuser/ then echo“However,$testuserhasadirectory.” fi fi $ $./test5.sh TheuserNoSuchUserdoesnotexistonthissystem. /home/NoSuchUser/ However,NoSuchUserhasadirectory. $ The script correctly finds that although the login name has been removed from the /etc/passwdfile,theuser’sdirectoryisstillonthesystem.Theproblemwithusingthis mannerofnested if-thenstatementsinascriptisthatthecodecangethardtoread,and thelogicflowbecomesdifficulttofollow. Insteadofhavingtowriteseparateif-thenstatements,youcanuseanalternativeversion oftheelsesection,called elif.Theelifcontinuesanelsesectionwithanother if-then statement: ifcommand1 then commands elifcommand2 then morecommands fi The elifstatementlineprovidesanothercommandtoevaluate,similartotheoriginal if statementline.Iftheexitstatuscodefromthe elifcommandiszero,bashexecutesthe commands in the second then statement section. Using this method of nesting provides cleanercodewithaneasier-to-followlogicflow: $cattest5.sh #!/bin/bash #Testingnestedifs-useelif # testuser=NoSuchUser # ifgrep$testuser/etc/passwd then echo“Theuser$testuserexistsonthissystem.” # elifls-d/home/$testuser then echo“Theuser$testuserdoesnotexistonthissystem.” echo“However,$testuserhasadirectory.” # fi $ $./test5.sh /home/NoSuchUser TheuserNoSuchUserdoesnotexistonthissystem. However,NoSuchUserhasadirectory. $ Youcaneventakethisscriptastepfurtherandhaveitcheckforbothanon-existentuser with a directory and a non-existent user without a directory. This is accomplished by addinganelsestatementwithinthenestedelif: $cattest5.sh #!/bin/bash #Testingnestedifs-useelif&else # testuser=NoSuchUser # ifgrep$testuser/etc/passwd then echo“Theuser$testuserexistsonthissystem.” # elifls-d/home/$testuser then echo“Theuser$testuserdoesnotexistonthissystem.” echo“However,$testuserhasadirectory.” # else echo“Theuser$testuserdoesnotexistonthissystem.” echo“And,$testuserdoesnothaveadirectory.” fi $ $./test5.sh /home/NoSuchUser TheuserNoSuchUserdoesnotexistonthissystem. However,NoSuchUserhasadirectory. $ $sudormdir/home/NoSuchUser [sudo]passwordforChristine: $ $./test5.sh ls:cannotaccess/home/NoSuchUser:Nosuchfileordirectory TheuserNoSuchUserdoesnotexistonthissystem. And,NoSuchUserdoesnothaveadirectory. $ Beforethe/home/NoSuchUserdirectorywasremovedandthetestscriptexecutedtheelif statement, a zero exit status was returned. Thus, the statements within the elif‘s then code block were executed. After the /home/NoSuchUser directory was removed, a nonzero exit status was returned for the elif statement. This caused the statements in the elseblockwithintheelifblocktobeexecuted. Tip Keepinmindthat,withanelifstatement,anyelsestatementsimmediately followingitareforthatelifcodeblock.Theyarenotpartofaprecedingif-then statementcodeblock. You can continue to string elif statements together, creating one huge if-then-elif conglomeration: ifcommand1 then commandset1 elifcommand2 then commandset2 elifcommand3 then commandset3 elifcommand4 then commandset4 fi Eachblockofcommandsisexecuteddependingonwhichcommandreturnsthezeroexit statuscode.Rememberthatthebashshellexecutesthe if statements in order, and only thefirstonethatreturnsazeroexitstatusresultsinthethensectionbeingexecuted. Even though the code looks cleaner with elif statements, it still can be confusing to followthescript’slogic.Laterinthe“ConsideringthecaseCommand”section,you’llsee howtousethecasecommandinsteadofhavingtonestlotsofif-thenstatements. TryingthetestCommand Sofar,allyou’veseeninthe ifstatementlinearenormalshellcommands.Youmightbe wondering if the bash if-then statement has the ability to evaluate any condition other thanacommand’sexitstatuscode. Theanswerisno,itcan’t.However,there’saneatutilityavailableinthebashshellthat helpsyouevaluateotherthings,usingtheif-thenstatement. Thetestcommandprovidesawaytotestdifferentconditionsinanif-thenstatement.If theconditionlistedinthetestcommandevaluatestoTRUE,thetestcommandexitswith azeroexitstatuscode.Thismakesthe if-thenstatementbehaveinmuchthesameway thatif-thenstatementsworkinotherprogramminglanguages.IftheconditionisFALSE, the test command exits with a non-zero exit status code, which causes the if-then statementtoexit. Theformatofthetestcommandisprettysimple: testcondition The condition is a series of parameters and values that the test command evaluates. Whenusedinanif-thenstatement,thetestcommandlookslikethis: iftestcondition then commands fi If you leave out the condition portion of the test command statement, it exits with a non-zeroexitstatuscodeandtriggersanyelseblockstatements: $cattest6.sh #!/bin/bash #Testingthetestcommand # iftest then echo“NoexpressionreturnsaTrue” else echo“NoexpressionreturnsaFalse” fi $ $./test6.sh NoexpressionreturnsaFalse $ Whenyouaddinacondition,itistestedbythe testcommand.Forexample,usingthe test command, you can determine whether a variable has content. A simple condition expressionisneededtodeterminewhetheravariablehascontent: $cattest6.sh #!/bin/bash #Testingthetestcommand # my_variable=“Full” # iftest$my_variable then echo“The$my_variableexpressionreturnsaTrue” # else echo“The$my_variableexpressionreturnsaFalse” fi $ $./test6.sh TheFullexpressionreturnsaTrue $ Thevariablemy_variablecontainscontent(Full),sowhenthetestcommandchecksthe condition,theexitstatusreturnsazero.Thistriggersthestatementinthethencodeblock. Asyouwouldsuspect,theoppositeoccurswhenthevariabledoesnotcontaincontent: $cattest6.sh #!/bin/bash #Testingthetestcommand # my_variable=”” # iftest$my_variable then echo“The$my_variableexpressionreturnsaTrue” # else echo“The$my_variableexpressionreturnsaFalse” fi $ $./test6.sh TheexpressionreturnsaFalse $ The bash shell provides an alternative way of testing a condition without declaring the testcommandinanif-thenstatement: if[condition] then commands fi Thesquarebracketsdefinethetestcondition.Becareful;youmusthaveaspaceafterthe firstbracketandaspacebeforethelastbracket,oryou’llgetanerrormessage. Thetestcommandandtestconditionscanevaluatethreeclassesofconditions: Numericcomparisons Stringcomparisons Filecomparisons The next sections describe how to use each of these test classes in your if-then statements. Usingnumericcomparisons The most common test evaluation method is to perform a comparison of two numeric values.Table12.1showsthelistofconditionparametersusedfortestingtwovalues. Table12.1ThetestNumericComparisons Comparison n1-eqn2 n1-gen2 n1-gtn2 n1-len2 n1-ltn2 n1-nen2 Description Checksifn1isequalton2 Checksifn1isgreaterthanorequalton2 Checksifn1isgreaterthann2 Checksifn1islessthanorequalton2 Checksifn1islessthann2 Checksifn1isnotequalton2 Thenumerictestconditionscanbeusedtoevaluatebothnumbersandvariables.Here’san exampleofdoingthat: $catnumeric_test.sh #!/bin/bash #Usingnumerictestevaluations # value1=10 value2=11 # if[$value1-gt5] then echo“Thetestvalue$value1isgreaterthan5” fi # if[$value1-eq$value2] then echo“Thevaluesareequal” else echo“Thevaluesaredifferent” fi # $ Thefirsttestcondition: if[$value1-gt5] testsifthevalueofthevariablevalue1isgreaterthan5.Thesecondtestcondition: if[$value1-eq$value2] testsifthevalueofthevariablevalue1isequaltothevalueofthevariablevalue2.Both numerictestconditionsevaluateasexpected: $./numeric_test.sh Thetestvalue10isgreaterthan5 Thevaluesaredifferent $ Thereisalimitationtothetestnumericconditionsconcerningfloating-pointvalues: $catfloating_point_test.sh #!/bin/bash #Usingfloatingpointnumbersintestevaluations # value1=5.555 # echo“Thetestvalueis$value1” # if[$value1-gt5] then echo“Thetestvalue$value1isgreaterthan5” fi # $./floating_point_test.sh Thetestvalueis5.555 ./floating_point_test.sh:line8: [:5.555:integerexpressionexpected $ Thisexampleusesafloating-pointvalue,storedinthevalue1variable.Next,itevaluates thevalue.Somethingobviouslywentwrong. Remember that the only numbers the bash shell can handle are integers. This works perfectlyfineifallyouneedtodoisdisplaytheresult,usinganechostatement.However, thisdoesn’tworkinnumeric-orientedfunctions,suchasournumerictestcondition.The bottomlineisthatyoucannotusefloating-pointvaluesfortestconditions. Usingstringcomparisons Test conditions also allow you to perform comparisons on string values. Performing comparisons on strings can get tricky, as you’ll see. Table 12.2 shows the comparison functionsyoucanusetoevaluatetwostringvalues. Table12.2ThetestStringComparisons Comparison str1=str2 str1!=str2 str1<str2 str1>str2 -nstr1 -zstr1 Description Checksifstr1isthesameasstringstr2 Checksifstr1isnotthesameasstr2 Checksifstr1islessthanstr2 Checksifstr1isgreaterthanstr2 Checksifstr1hasalengthgreaterthanzero Checksifstr1hasalengthofzero Thefollowingsectionsdescribethedifferentstringcomparisonsavailable. Lookingatstringequality Theequalandnotequalconditionsarefairlyself-explanatorywithstrings.It’sprettyeasy toknowwhentwostringvaluesarethesameornot: $cattest7.sh #!/bin/bash #testingstringequality testuser=rich # if[$USER=$testuser] then echo“Welcome$testuser” fi $ $./test7.sh Welcomerich $ Also,usingthenotequalsstringcomparisonallowsyoutodetermineiftwostringshave thesamevalueornot: $cattest8.sh #!/bin/bash #testingstringequality testuser=baduser # if[$USER!=$testuser] then echo“Thisisnot$testuser” else echo“Welcome$testuser” fi $ $./test8.sh Thisisnotbaduser $ Keepinmindthatthetestcomparisontakesallpunctuationandcapitalizationintoaccount whencomparingstringsforequality. Lookingatstringorder Tryingtodetermineifonestringislessthanorgreaterthananotheriswherethingsstart getting tricky. Two problems often plague shell programmers when trying to use the greater-thanorless-thanfeaturesoftestconditions: Thegreater-thanandless-thansymbolsmustbeescaped,ortheshellusesthemas redirectionsymbols,withthestringvaluesasfilenames. Thegreater-thanandless-thanorderisnotthesameasthatusedwiththesort command. Thefirstitemcanresultinahugeproblemthatoftengoesundetectedwhenprogramming your scripts. Here’s an example of what sometimes happens to novice shell script programmers: $catbadtest.sh #!/bin/bash #mis-usingstringcomparisons # val1=baseball val2=hockey # if[$val1>$val2] then echo“$val1isgreaterthan$val2” else echo“$val1islessthan$val2” fi $ $./badtest.sh baseballisgreaterthanhockey $ls-lhockey -rw-r—r—1richrich0Sep3019:08hockey $ Byjustusingthegreater-thansymbolitselfinthescript,noerrorsaregenerated,butthe resultsarewrong.Thescriptinterpretedthegreater-thansymbolasanoutputredirection (seeChapter15).Thus,itcreatedafilecalledhockey.Becausetheredirectioncompleted successfully, the test condition returns a zero exit status code, which the if statement evaluatesasthoughthingscompletedsuccessfully! Tofixthisproblem,youneedtoproperlyescapethegreater-thansymbol: $cattest9.sh #!/bin/bash #mis-usingstringcomparisons # val1=baseball val2=hockey # if[$val1\>$val2] then echo“$val1isgreaterthan$val2” else echo“$val1islessthan$val2” fi $ $./test9.sh baseballislessthanhockey $ Now that answer is more along the lines of what you would expect from the string comparison. Thesecondissueisalittlemoresubtle,andyoumaynotevenrunacrossitunlessyouare working with uppercase and lowercase letters. The sort command handles uppercase lettersoppositetothewaythetestconditionsconsiderthem: $cattest9b.sh #!/bin/bash #testingstringsortorder val1=Testing val2=testing # if[$val1\>$val2] then echo“$val1isgreaterthan$val2” else echo“$val1islessthan$val2” fi $ $./test9b.sh Testingislessthantesting $ $sorttestfile testing Testing $ Capitalizedlettersaretreatedaslessthanlowercaselettersintestcomparisons.However, thesortcommanddoestheopposite.Whenyouputthesamestringsinafileandusethe sort command, the lowercase letters appear first. This is due to different ordering techniques. Test comparisons use standard ASCII ordering, using each character’s ASCII numeric valuetodeterminethesortorder.Thesortcommandusesthesortingorderdefinedforthe systemlocalelanguagesettings.FortheEnglishlanguage,thelocalesettingsspecifythat lowercaselettersappearbeforeuppercaselettersinsortedorder. Note Thetestcommandandtestexpressionsusethestandardmathematicalcomparison symbolsforstringcomparisonsandtextcodesfornumericalcomparisons.Thisisa subtlefeaturethatmanyprogrammersmanagetogetreversed.Ifyouusethe mathematicalcomparisonsymbolsfornumericvalues,theshellinterpretsthemas stringvaluesandmaynotproducethecorrectresults. Lookingatstringsize The-nand-zcomparisonsarehandywhentryingtoevaluatewhetheravariablecontains data: $cattest10.sh #!/bin/bash #testingstringlength val1=testing val2=” # if[-n$val1] then echo“Thestring‘$val1’isnotempty” else echo“Thestring‘$val1’isempty” fi # if[-z$val2] then echo“Thestring‘$val2’isempty” else echo“Thestring‘$val2’isnotempty” fi # if[-z$val3] then echo“Thestring‘$val3’isempty” else echo“Thestring‘$val3’isnotempty” fi $ $./test10.sh Thestring‘testing’isnotempty Thestring”isempty Thestring”isempty $ This example creates two string variables. The val1 variable contains a string, and the val2variableiscreatedasanemptystring.Thefollowingcomparisonsaremadeasshown below: if[-n$val1] Theprecedingcodedetermineswhethertheval1variableisnon-zeroinlength,whichitis, soitsthensectionisprocessed. if[-z$var2] Thisprecedingcodedetermineswhethertheval2variableiszeroinlength,whichitis,so itsthensectionisprocessed. if[-z$val3] Theprecedingdetermineswhethertheval3 variable is zero in length. This variable was never defined in the shell script, so it indicates that the string length is still zero, even thoughitwasn’tdefined. Tip Emptyanduninitializedvariablescanhavecatastrophiceffectsonyourshellscript tests.Ifyou’renotsureofthecontentsofavariable,it’salwaysbesttotestifthe variablecontainsavalueusing-nor-zbeforeusingitinanumericorstring comparison. Usingfilecomparisons Thelastcategoryoftestcomparisonsisquitepossiblythemostpowerfulandmostused comparisons in shell scripting. This category allows you to test the status of files and directoriesontheLinuxfilesystem.Table12.3liststhesecomparisons. Table12.3ThetestFileComparisons Comparison -dfile -efile -ffile -rfile -sfile -wfile -xfile -Ofile -Gfile Description Checksiffileexistsandisadirectory Checksiffileexists Checksiffileexistsandisafile Checksiffileexistsandisreadable Checksiffileexistsandisnotempty Checksiffileexistsandiswritable Checksiffileexistsandisexecutable Checksiffileexistsandisownedbythecurrentuser Checksiffileexistsandthedefaultgroupisthesameasthecurrent user file1-nt file2 Checksiffile1isnewerthanfile2 file1-ot file2 Checksiffile1isolderthanfile2 Theseconditionsgiveyoutheabilitytocheckfilesystemfileswithinshellscripts.They areoftenusedinscriptsthataccessfiles.Becausethey’reusedsooften,let’slookateach oftheseindividually. Checkingdirectories The -d test checks to see if a specified directory exists on the system. This is usually a goodthingtodoifyou’retryingtowriteafiletoadirectoryorbeforeyoutrytochangeto adirectorylocation: $cattest11.sh #!/bin/bash #Lookbeforeyouleap # jump_directory=/home/arthur # if[-d$jump_directory] then echo“The$jump_directorydirectoryexists” cd$jump_directory ls else echo“The$jump_directorydirectorydoesnotexist” fi # $ $./test11.sh The/home/arthurdirectorydoesnotexist $ The-dtestconditioncheckstoseeifthe jump_directoryvariable’sdirectoryexists.Ifit does,itproceedstousethecdcommandtochangetothecurrentdirectoryandperformsa directorylisting.Ifitdoesnot,thescriptemitsawarningmessageandexitsthescript. Checkingwhetheranobjectexists The-ecomparisonallowsyoutocheckifeitherafileordirectoryobjectexistsbeforeyou attempttouseitinyourscript: $cattest12.sh #!/bin/bash #Checkifeitheradirectoryorfileexists # location=$HOME file_name=“sentinel” # if[-e$location] then#Directorydoesexist echo“OKonthe$locationdirectory.” echo“Nowcheckingonthefile,$file_name.” # if[-e$location/$file_name] then#Filedoesexist echo“OKonthefilename” echo“UpdatingCurrentDate…” date>>$location/$file_name # else#Filedoesnotexist echo“Filedoesnotexist” echo“Nothingtoupdate” fi # else#Directorydoesnotexist echo“The$locationdirectorydoesnotexist.” echo“Nothingtoupdate” fi # $ $./test12.sh OKonthe/home/Christinedirectory. Nowcheckingonthefile,sentinel. Filedoesnotexist Nothingtoupdate $ $touchsentinel $ $./test12.sh OKonthe/home/Christinedirectory. Nowcheckingonthefile,sentinel. OKonthefilename UpdatingCurrentDate… $ The first check uses the -e comparison to determine whether the user has a $HOME directory. If so, the next -e comparison checks to determine whether the sentinel file existsinthe $HOMEdirectory.Ifthefiledoesn’texist,theshellscriptnotesthatthefileis missingandthatthereisnothingtoupdate. Toensurethattheupdatewillwork,thesentinelfilewascreatedandtheshellscriptwas run a second time. This time when the conditions are tested, both the $HOME and the sentinelfilearefound,andthecurrentdateandtimeisappendedtothefile. Checkingforafile The -e comparison works for both files and directories. To be sure that the object specifiedisafileandnotadirectory,youmustusethe-fcomparison: $cattest13.sh #!/bin/bash #Checkifeitheradirectoryorfileexists # item_name=$HOME echo echo“Theitembeingchecked:$item_name” echo # if[-e$item_name] then#Itemdoesexist echo“Theitem,$item_name,doesexist.” echo“Butisitafile?” echo # if[-f$item_name] then#Itemisafile echo“Yes,$item_nameisafile.” # else#Itemisnotafile echo“No,$item_nameisnotafile.” fi # else#Itemdoesnotexist echo“Theitem,$item_name,doesnotexist.” echo“Nothingtoupdate” fi # $./test13.sh Theitembeingchecked:/home/Christine Theitem,/home/Christine,doesexist. Butisitafile? No,/home/Christineisnotafile. $ This little script does lots of checking! First, it uses the -e comparison to test whether $HOMEexists.Ifitdoes,ituses-ftotestwhetherit’safile.Ifitisn’tafile(whichofcourse itisn’t),amessageisdisplayedstatingthatitisnotafile. Aslightmodificationtothevariable,item_name,replacingthedirectory$HOMEwithafile, $HOME/sentinel,causesadifferentoutcome: $nanotest13.sh $ $cattest13.sh #!/bin/bash #Checkifeitheradirectoryorfileexists # item_name=$HOME/sentinel […] $ $./test13.sh Theitembeingchecked:/home/Christine/sentinel Theitem,/home/Christine/sentinel,doesexist. Butisitafile? Yes,/home/Christine/sentinelisafile. $ The test13.shscriptlistingissnipped,becausetheonlyitemchangedintheshellscript was the item_name variable’s value. Now when the script is run, the -f test on $HOME/sentinel exits with a zero status, triggering the then statement, which in turn outputsthemessageYes,/home/Christine/sentinelisafile. Checkingforreadaccess Beforetryingtoreaddatafromafile,it’susuallyagoodideatotestwhetheryoucanread fromthefilefirst.Youdothiswiththe-rcomparison: $cattest14.sh #!/bin/bash #testingifyoucanreadafile pwfile=/etc/shadow # #first,testifthefileexists,andisafile if[-f$pwfile] then #nowtestifyoucanreadit if[-r$pwfile] then tail$pwfile else echo“Sorry,Iamunabletoreadthe$pwfilefile” fi else echo“Sorry,thefile$filedoesnotexist” fi $ $./test14.sh Sorry,Iamunabletoreadthe/etc/shadowfile $ The /etc/shadow file contains the encrypted passwords for system users, so it’s not readablebynormalusersonthesystem.The-rcomparisondeterminedthatreadaccessto thefilewasn’tallowed,sothetestcommandfailedandthebashshellexecutedthe else sectionoftheif-thenstatement. Checkingforemptyfiles Youshoulduse -s comparison to check whether a file is empty, especially if you don’t wanttoremoveanon-emptyfile.Becarefulbecausewhenthe-scomparisonsucceeds,it indicatesthatafilehasdatainit: $cattest15.sh #!/bin/bash #Testingifafileisempty # file_name=$HOME/sentinel # if[-f$file_name] then if[-s$file_name] then echo”The$file_namefileexistsandhasdatainit.” echo”Willnotremovethisfile.” # else echo”The$file_namefileexists,butisempty.” echo”Deletingemptyfile…” rm$file_name fi else echo”File,$file_name,doesnotexist.” fi # $ls-l$HOME/sentinel -rw-rw-r—.1ChristineChristine29Jun2505:32/home/Christine/sentinel $ $./test15.sh The/home/Christine/sentinelfileexistsandhasdatainit. Willnotremovethisfile. $ First,the-fcomparisontestswhetherthefileexists.Ifitdoesexist,the -scomparisonis triggeredtodeterminewhetherthefileisempty.Anemptyfilewillbedeleted.Youcan seefromthe ls-lthatthe sentinelfileisnotempty,andthereforethescriptdoesnot deleteit. Checkingwhetheryoucanwritetoafile The -w comparison determines whether you have permission to write to a file. The test16.sh script is simply an update of the test13.sh script. Now instead of just checkingwhetherthe item_nameexistsandisafile,thescriptalsocheckstoseewhether ithaspermissiontowritetothefile: $cattest16.sh #!/bin/bash #Checkifafileiswritable. # item_name=$HOME/sentinel echo echo“Theitembeingchecked:$item_name” echo […] echo“Yes,$item_nameisafile.” echo“Butisitwritable?” echo # if[-w$item_name] then#Itemiswritable echo“Writingcurrenttimeto$item_name” date+%H%M>>$item_name # else#Itemisnotwritable echo“Unabletowriteto$item_name” fi # else#Itemisnotafile echo“No,$item_nameisnotafile.” fi […] $ $ls-lsentinel -rw-rw-r—.1ChristineChristine0Jun2705:38sentinel $ $./test16.sh Theitembeingchecked:/home/Christine/sentinel Theitem,/home/Christine/sentinel,doesexist. Butisitafile? Yes,/home/Christine/sentinelisafile. Butisitwritable? Writingcurrenttimeto/home/Christine/sentinel $ $catsentinel 0543 $ The item_namevariableissetto $HOME/sentinel, and this file allows user write access (seeChapter7formoreinformationonfilepermissions).Thus,whenthescriptisrun,the -w test expressions returns a non-zero exit status and the then code block is executed, whichwritesatimestampintothesentinelfile. When the sentinel file user’s write access is removed via chmod, the -w test expression returnsanon-zerostatus,andatimestampisnotwrittentothefile: $chmodu-wsentinel $ $ls-lsentinel -r—rw-r—.1ChristineChristine5Jun2705:43sentinel $ $./test16.sh Theitembeingchecked:/home/Christine/sentinel Theitem,/home/Christine/sentinel,doesexist. Butisitafile? Yes,/home/Christine/sentinelisafile. Butisitwritable? Unabletowriteto/home/Christine/sentinel $ The chmodcommandcouldbeusedagaintograntthewritepermissionbackfortheuser. This would make the write test expression return a zero exit status and allow a write attempttothefile. Checkingwhetheryoucanrunafile The-xcomparisonisahandywaytodeterminewhetheryouhaveexecutepermissionfor a specific file. Although this may not be needed for most commands, if you run lots of scriptsfromyourshellscripts,itcouldbeuseful: $cattest17.sh #!/bin/bash #testingfileexecution # if[-xtest16.sh] then echo“Youcanrunthescript:“ ./test16.sh else echo“Sorry,youareunabletoexecutethescript” fi $ $./test17.sh Youcanrunthescript: […] $ $chmodu-xtest16.sh $ $./test17.sh Sorry,youareunabletoexecutethescript $ Thisexampleshellscriptusesthe -xcomparisontotestwhetheryouhavepermissionto execute the test16.sh script. If so, it runs the script. After successfully running the test16.sh script the first time, the permissions were changed. This time, the -x comparison failed, because execute permission had been removed for the test16.sh script. Checkingownership The-Ocomparisonallowsyoutoeasilytestwhetheryou’retheownerofafile: $cattest18.sh #!/bin/bash #checkfileownership # if[-O/etc/passwd] then echo“Youaretheownerofthe/etc/passwdfile” else echo“Sorry,youarenottheownerofthe/etc/passwdfile” fi $ $./test18.sh Sorry,youarenottheownerofthe/etc/passwdfile $ Thescriptusesthe -Ocomparisontotestwhethertheuserrunningthescriptistheowner ofthe/etc/passwdfile.Thescriptisrununderanormaluseraccount,sothetestfails. Checkingdefaultgroupmembership The -G comparison checks the default group of a file, and it succeeds if it matches the groupofthedefaultgroupfortheuser.Thiscanbesomewhatconfusingbecausethe -G comparison checks the default groups only and not all the groups to which the user belongs.Here’sanexampleofthis: $cattest19.sh #!/bin/bash #checkfilegrouptest # if[-G$HOME/testing] then echo“Youareinthesamegroupasthefile” else echo“Thefileisnotownedbyyourgroup” fi $ $ls-l$HOME/testing -rw-rw-r—1richrich582014-07-3015:51/home/rich/testing $ $./test19.sh Youareinthesamegroupasthefile $ $chgrpsharing$HOME/testing $ $./test19 Thefileisnotownedbyyourgroup $ Thefirsttimethescriptisrun,the $HOME/testingfileisinthe richgroup,andthe -G comparisonsucceeds.Next,thegroupischangedtothe sharinggroup,ofwhichtheuser isalsoamember.However,the-Gcomparisonfailed,becauseitcomparesonlythedefault groups,notanyadditionalgroupmemberships. Checkingfiledate The last set of comparisons deal with comparing the creation times of two files. This comes in handy when writing scripts to install software. Sometimes, you don’t want to installafilethatisolderthanafilealreadyinstalledonthesystem. The-ntcomparisondetermineswhetherafileisnewerthananotherfile.Ifafileisnewer, ithasamorerecentfilecreationtime.The -otcomparisondetermineswhetherafileis olderthananotherfile.Ifthefileisolder,ithasanolderfilecreationtime: $cattest20.sh #!/bin/bash #testingfiledates # if[test19.sh-nttest18.sh] then echo“Thetest19fileisnewerthantest18” else echo“Thetest18fileisnewerthantest19” fi if[test17.sh-ottest19.sh] then echo“Thetest17fileisolderthanthetest19file” fi $ $./test20.sh Thetest19fileisnewerthantest18 Thetest17fileisolderthanthetest19file $ $ls-ltest17.shtest18.shtest19.sh -rwxrw-r—1richrich1672014-07-3016:31test17.sh -rwxrw-r—1richrich1852014-07-3017:46test18.sh -rwxrw-r—1richrich1672014-07-3017:50test19.sh $ Thefilepathsusedinthecomparisonsarerelativetothedirectoryfromwhichyourunthe script. This can cause problems if the files being checked are moved around. Another problemisthatneitherofthesecomparisonscheckswhetherthefileexistsfirst.Trythis test: $cattest21.sh #!/bin/bash #testingfiledates # if[badfile1-ntbadfile2] then echo“Thebadfile1fileisnewerthanbadfile2” else echo“Thebadfile2fileisnewerthanbadfile1” fi $ $./test21.sh Thebadfile2fileisnewerthanbadfile1 $ This little example demonstrates that if the files don’t exist, the -nt comparison just returnsafailedcondition.It’simperativetoensurethatthefilesexistbeforetryingtouse theminthe-ntor-otcomparison. ConsideringCompoundTesting The if-then statement allows you to use Boolean logic to combine tests. You can use thesetwoBooleanoperators: [condition1]&&[condition2] [condition1]||[condition2] The first Boolean operation uses the AND Boolean operator to combine two conditions. Bothconditionsmustbemetforthethensectiontoexecute. Tip BooleanlogicisamethodthatreducesthepotentialreturnedvaluestobeeitherTRUE orFALSE. ThesecondBooleanoperationusestheORBooleanoperatortocombinetwoconditions.If eitherconditionevaluatestoaTRUEcondition,thethensectionisexecuted. ThefollowingshowstheANDBooleanoperatorinuse: $cattest22.sh #!/bin/bash #testingcompoundcomparisons # if[-d$HOME]&&[-w$HOME/testing] then echo“Thefileexistsandyoucanwritetoit” else echo“Icannotwritetothefile” fi $ $./test22.sh Icannotwritetothefile $ $touch$HOME/testing $ $./test22.sh Thefileexistsandyoucanwritetoit $ Using the AND Boolean operator, both of the comparisons must be met. The first comparison checks to see if the $HOME directory exists for the user. The second comparisoncheckstoseeifthere’safilecalled testingintheuser’s $HOMEdirectory, andiftheuserhaswritepermissionsforthefile.Ifeitherofthesecomparisonsfails,the if statement fails and the shell executes the else section. If both of the comparisons succeed,theifstatementsucceeds,andtheshellexecutesthethensection. WorkingwithAdvancedif-thenFeatures Two additions to the bash shell provide advanced features that you can use in if-then statements: Doubleparenthesesformathematicalexpressions Doublesquarebracketsforadvancedstringhandlingfunctions Thefollowingsectionsdescribeeachofthesefeaturesinmoredetail. Usingdoubleparentheses The double parentheses command allows you to incorporate advanced mathematical formulas in your comparisons. The test command allows for only simple arithmetic operations in the comparison. The double parentheses command provides more mathematicalsymbols,whichprogrammerswhohaveusedotherprogramminglanguages maybefamiliarwithusing.Here’stheformatofthedoubleparenthesescommand: ((expression)) The expression term can be any mathematical assignment or comparison expression. Besides the standard mathematical operators that the test command uses, Table 12.4 shows the list of additional operators available for use in the double parentheses command. Table12.4TheDoubleParenthesesCommandSymbols Symbol val++ val— ++val —val ! ∼ ** << >> & | && || Description Post-increment Post-decrement Pre-increment Pre-decrement Logicalnegation Bitwisenegation Exponentiation Leftbitwiseshift Rightbitwiseshift BitwiseBooleanAND BitwiseBooleanOR LogicalAND LogicalOR Youcanusethedoubleparenthesescommandinan if statement,aswellasinanormal commandinthescriptforassigningvalues: $cattest23.sh #!/bin/bash #usingdoubleparenthesis # val1=10 # if(($val1**2>90)) then ((val2=$val1**2)) echo“Thesquareof$val1is$val2” fi $ $./test23.sh Thesquareof10is100 $ Noticethatyoudon’tneedtoescapethegreater-thansymbolintheexpressionwithinthe double parentheses. This is yet another advanced feature besides the double parentheses command. Usingdoublebrackets Thedoublebracketcommandprovidesadvancedfeaturesforstringcomparisons.Here’s thedoublebracketcommandformat: [[expression]] The double bracketed expression uses the standard string comparison used in the test evaluations.However,itprovidesanadditionalfeaturethatthetestevaluationsdon’t— patternmatching. Note Doublebracketsworkfineinthebashshell.Beaware,however,thatnotallshells supportdoublebrackets. In pattern matching, you can define a regular expression (discussed in detail in Chapter 20)that’smatchedagainstthestringvalue: $cattest24.sh #!/bin/bash #usingpatternmatching # if[[$USER==r*]] then echo“Hello$USER” else echo“Sorry,Idonotknowyou” fi $ $./test24.sh Hellorich $ Noticeintheprecedingscriptthatdoubleequalsigns(==)areused.Thesedoubleequal signs designate the string to the right (r*) as a pattern, and pattern matching rules are applied. The double bracket command matches the $USER environment variable to see whetheritstartswiththeletter r.Ifso,thecomparisonsucceeds,andtheshellexecutes thethensectioncommands. ConsideringthecaseCommand Often, you’ll find yourself trying to evaluate a variable’s value, looking for a specific valuewithinasetofpossiblevalues.Inthisscenario,youenduphavingtowritealengthy if-then-elsestatement,likethis: $cattest25.sh #!/bin/bash #lookingforapossiblevalue # if[$USER=“rich”] then echo“Welcome$USER” echo“Pleaseenjoyyourvisit” elif[$USER=“barbara”] then echo“Welcome$USER” echo“Pleaseenjoyyourvisit” elif[$USER=“testing”] then echo“Specialtestingaccount” elif[$USER=“jessica”] then echo“Donotforgettologoutwhenyou’redone” else echo“Sorry,youarenotallowedhere” fi $ $./test25.sh Welcomerich Pleaseenjoyyourvisit $ The elifstatementscontinuethe if-thenchecking,lookingforaspecificvalueforthe singlecomparisonvariable. Insteadofhavingtowriteallthe elifstatementstocontinuecheckingthesamevariable value, you can use the casecommand.The case command checks multiple values of a singlevariableinalist-orientedformat: casevariablein pattern1|pattern2)commands1;; pattern3)commands2;; *)defaultcommands;; esac The case command compares the variable specified against the different patterns. If the variable matches the pattern, the shell executes the commands specified for the pattern. You can list more than one pattern on a line, using the bar operator to separate each pattern.Theasterisksymbolisthecatch-allforvaluesthatdon’tmatchanyofthelisted patterns. Here’s an example of converting the if-then-else program to using the case command: $cattest26.sh #!/bin/bash #usingthecasecommand # case$USERin rich|barbara) echo“Welcome,$USER” echo“Pleaseenjoyyourvisit”;; testing) echo“Specialtestingaccount”;; jessica) echo“Donotforgettologoffwhenyou’redone”;; *) echo“Sorry,youarenotallowedhere”;; esac $ $./test26.sh Welcome,rich Pleaseenjoyyourvisit $ The case command provides a much cleaner way of specifying the various options for eachpossiblevariablevalue. Summary Structured commands allow you to alter the normal flow of shell script execution. The most basic structured command is the if-then statement. This statement provides a command evaluation and performs other commands based on the evaluated command’s output. You can expand the if-then statement to include a set of commands the bash shell executes if the specified command fails as well. The if-then-else statement executes commandsonlyifthecommandbeingevaluatedreturnsanon-zeroexitstatuscode. Youcanalsolink if-then-elsestatementstogether,usingthe elifstatement.The elif isequivalenttousinganelseifstatement,providingforadditionalcheckingofwhether theoriginalcommandthatwasevaluatedfailed. Inmostscripts,insteadofevaluatingacommand,you’llwanttoevaluateacondition,such asanumericvalue,thecontentsofastring,orthestatusofafileordirectory.The test commandprovidesaneasywayforyoutoevaluatealltheseconditions.Ifthecondition evaluatestoa TRUEcondition,the testcommandproducesazeroexitstatuscodeforthe if-then statement. If the condition evaluates to a FALSE condition, the test command producesanon-zeroexitstatuscodefortheif-thenstatement. Thesquarebracketisaspecialbashcommandthatisasynonymforthe testcommand. You can enclose a test condition in square brackets in the if-then statement to test for numeric,string,andfileconditions. The double parentheses command provides advanced mathematical evaluations using additional operators. The double square bracket command allows you to perform advancedstringpattern-matchingevaluations. Finally,thechapterdiscussedthecasecommand,whichisashorthandwayofperforming multipleif-then-elsecommands,checkingthevalueofasinglevariableagainstalistof values. Thenextchaptercontinuesthediscussionofstructuredcommandsbyexaminingtheshell loopingcommands.Theforandwhilecommandsletyoucreateloopsthatiteratethrough commandsforagivenperiodoftime. Chapter13 MoreStructuredCommands InThisChapter 1. Loopingwiththeforstatement 2. Iteratingwiththeuntilstatement 3. Usingthewhilestatement 4. Combiningloops 5. Redirectingloopoutput Inthepreviouschapter,yousawhowtomanipulatetheflowofashellscriptprogram bycheckingtheoutputofcommandsandthevaluesofvariables.Inthischapter,we continuetolookatstructuredcommandsthatcontroltheflowofyourshellscripts. You’llseehowyoucanperformrepeatingprocesses,commandsthatcanloop throughasetofcommandsuntilanindicatedconditionhasbeenmet.Thischapter discussesanddemonstratesthefor,while,anduntilbashshellloopingcommands. TheforCommand Iterating through a series of commands is a common programming practice. Often, you need to repeat a set of commands until a specific condition has been met, such as processingallthefilesinadirectory,alltheusersonasystem,orallthelinesinatextfile. The bash shell provides the for command to allow you to create a loop that iterates throughaseriesofvalues.Eachiterationperformsadefinedsetofcommandsusingoneof thevaluesintheseries.Here’sthebasicformatofthebashshellforcommand: forvarinlist do commands done Yousupplytheseriesofvaluesusedintheiterationsinthelistparameter.Youcanspecify thevaluesinthelistinseveralways. Ineachiteration,thevariablevarcontainsthecurrentvalueinthelist.Thefirstiteration usesthefirstiteminthelist,theseconditerationtheseconditem,andsoonuntilallthe itemsinthelisthavebeenused. Thecommandsenteredbetweenthe doand donestatementscanbeoneormorestandard bash shell commands. Within the commands, the $var variable contains the current list itemvaluefortheiteration. Note Ifyouprefer,youcanincludethedostatementonthesamelineastheforstatement, butyoumustseparateitfromthelistitemsusingasemicolon:forvarinlist; do. We mentioned that there are several different ways to specify the values in the list. The followingsectionsshowthevariouswaystodothat. Readingvaluesinalist Themostbasicuseoftheforcommandistoiteratethroughalistofvaluesdefinedwithin theforcommanditself: $cattest1 #!/bin/bash #basicforcommand fortestinAlabamaAlaskaArizonaArkansasCaliforniaColorado do echoThenextstateis$test done $./test1 ThenextstateisAlabama ThenextstateisAlaska ThenextstateisArizona ThenextstateisArkansas ThenextstateisCalifornia ThenextstateisColorado $ Each time the for command iterates through the list of values provided, it assigns the $testvariablethenextvalueinthelist.The$testvariablecanbeusedjustlikeanyother script variable within the for command statements. After the last iteration, the $test variable remains valid throughout the remainder of the shell script. It retains the last iterationvalue(unlessyouchangeitsvalue): $cattest1b #!/bin/bash #testingtheforvariableafterthelooping fortestinAlabamaAlaskaArizonaArkansasCaliforniaColorado do echo“Thenextstateis$test” done echo“Thelaststatewevisitedwas$test” test=Connecticut echo“Wait,nowwe’revisiting$test” $./test1b ThenextstateisAlabama ThenextstateisAlaska ThenextstateisArizona ThenextstateisArkansas ThenextstateisCalifornia ThenextstateisColorado ThelaststatewevisitedwasColorado Wait,nowwe’revisitingConnecticut $ The$testvariableretaineditsvalueandallowedustochangethevalueanduseitoutside oftheforcommandloop,asanyothervariablewould. Readingcomplexvaluesinalist Thingsaren’talwaysaseasyastheyseemwiththe forloop.Therearetimeswhenyou runintodatathatcausesproblems.Here’saclassicexampleofwhatcancauseproblems forshellscriptprogrammers: $catbadtest1 #!/bin/bash #anotherexampleofhownottousetheforcommand fortestinIdon’tknowifthis’llwork do echo“word:$test” done $./badtest1 word:I word:dontknowifthisll word:work $ Ouch, that hurts. The shell saw the single quotation marks within the list values and attemptedtousethemtodefineasingledatavalue,anditreallymessedthingsupinthe process. Youhavetwowaystosolvethisproblem: Usetheescapecharacter(thebackslash)toescapethesinglequotationmark. Usedoublequotationmarkstodefinethevaluesthatusesinglequotationmarks. Neithersolutionisallthatfantastic,buteachonehelpssolvetheproblem: $cattest2 #!/bin/bash #anotherexampleofhownottousetheforcommand fortestinIdon'tknowif“this’ll”work do echo“word:$test” done $./test2 word:I word:don’t word:know word:if word:this’ll word:work $ In the first problem value, you added the backslash character to escape the single quotationmarkinthedon’tvalue.Inthesecondproblemvalue,youenclosedthethis’ll valueindoublequotationmarks.Bothmethodsworkedfinetodistinguishthevalue. Another problem you may run into is multi-word values. Remember that the for loop assumes that each value is separated with a space. If you have data values that contain spaces,yourunintoyetanotherproblem: $catbadtest2 #!/bin/bash #anotherexampleofhownottousetheforcommand fortestinNevadaNewHampshireNewMexicoNewYorkNorthCarolina do echo“Nowgoingto$test” done $./badtest1 NowgoingtoNevada NowgoingtoNew NowgoingtoHampshire NowgoingtoNew NowgoingtoMexico NowgoingtoNew NowgoingtoYork NowgoingtoNorth NowgoingtoCarolina $ Oops,that’snotexactlywhatwewanted.The forcommandseparateseachvalueinthe listwithaspace.Iftherearespacesintheindividualdatavalues,youmustaccommodate themusingdoublequotationmarks: $cattest3 #!/bin/bash #anexampleofhowtoproperlydefinevalues fortestinNevada“NewHampshire”“NewMexico”“NewYork” do echo“Nowgoingto$test” done $./test3 NowgoingtoNevada NowgoingtoNewHampshire NowgoingtoNewMexico NowgoingtoNewYork $ Nowtheforcommandcanproperlydistinguishbetweenthedifferentvalues.Also,notice that when you use double quotation marks around a value, the shell doesn’t include the quotationmarksaspartofthevalue. Readingalistfromavariable Often what happens in a shell script is that you accumulate a list of values stored in a variableandthenneedtoiteratethroughthelist.Youcandothisusingthe forcommand aswell: $cattest4 #!/bin/bash #usingavariabletoholdthelist list=“AlabamaAlaskaArizonaArkansasColorado” list=$list”Connecticut” forstatein$list do echo“Haveyouevervisited$state?” done $./test4 HaveyouevervisitedAlabama? HaveyouevervisitedAlaska? HaveyouevervisitedArizona? HaveyouevervisitedArkansas? HaveyouevervisitedColorado? HaveyouevervisitedConnecticut? $ The$listvariablecontainsthestandardtextlistofvaluestousefortheiterations.Notice thatthecodealsousesanotherassignmentstatementtoadd(orconcatenate)anitemtothe existinglistcontainedinthe $listvariable.Thisisacommonmethodforaddingtextto theendofanexistingtextstringstoredinavariable. Readingvaluesfromacommand Anotherwaytogeneratevaluesforuseinthelististousetheoutputofacommand.You usecommandsubstitutiontoexecuteanycommandthatproducesoutputandthenusethe outputofthecommandintheforcommand: $cattest5 #!/bin/bash #readingvaluesfromafile file=“states” forstatein$(cat$file) do echo“Visitbeautiful$state” done $catstates Alabama Alaska Arizona Arkansas Colorado Connecticut Delaware Florida Georgia $./test5 VisitbeautifulAlabama VisitbeautifulAlaska VisitbeautifulArizona VisitbeautifulArkansas VisitbeautifulColorado VisitbeautifulConnecticut VisitbeautifulDelaware VisitbeautifulFlorida VisitbeautifulGeorgia $ Thisexampleusesthe catcommandinthecommandsubstitutiontodisplaythecontents of the file states. Notice that the states file includes each state on a separate line, not separated by spaces. The for command still iterates through the output of the cat commandonelineatatime,assumingthateachstateisonaseparateline.However,this doesn’tsolvetheproblemofhavingspacesindata.Ifyoulistastatewithaspaceinit,the forcommandstilltakeseachwordasaseparatevalue.There’sareasonforthis,whichwe lookatinthenextsection. Note Thetest5codeexampleassignedthefilenametothevariableusingjustthefilename withoutapath.Thisrequiresthatthefilebeinthesamedirectoryasthescript.Ifthis isn’tthecase,youneedtouseafullpathname(eitherabsoluteorrelative)to referencethefilelocation. Changingthefieldseparator ThecauseofthisproblemisthespecialenvironmentvariableIFS,calledtheinternalfield separator.TheIFSenvironmentvariabledefinesalistofcharactersthebashshellusesas field separators. By default, the bash shell considers the following characters as field separators: Aspace Atab Anewline Ifthebashshellseesanyofthesecharactersinthedata,itassumesthatyou’restartinga new data field in the list. When working with data that can contain spaces (such as filenames),thiscanbeannoying,asyousawinthepreviousscriptexample. Tosolvethisproblem,youcantemporarilychangetheIFSenvironmentvariablevaluesin yourshellscripttorestrictthecharactersthebashshellrecognizesasfieldseparators.For example,ifyouwanttochangetheIFSvaluetorecognizeonlythenewlinecharacter,you needtodothis: IFS=$’\n’ Addingthisstatementtoyourscripttellsthebashshelltoignorespacesandtabsindata values.Applyingthistechniquetothepreviousscriptyieldsthefollowing: $cattest5b #!/bin/bash #readingvaluesfromafile file=“states” IFS=$’\n’ forstatein$(cat$file) do echo“Visitbeautiful$state” done $./test5b VisitbeautifulAlabama VisitbeautifulAlaska VisitbeautifulArizona VisitbeautifulArkansas VisitbeautifulColorado VisitbeautifulConnecticut VisitbeautifulDelaware VisitbeautifulFlorida VisitbeautifulGeorgia VisitbeautifulNewYork VisitbeautifulNewHampshire VisitbeautifulNorthCarolina $ Nowtheshellscriptcanusevaluesinthelistthatcontainspaces. Caution Whenworkingonlongscripts,it’spossibletochangetheIFSvalueinoneplace,and thenforgetaboutitandassumethedefaultvalueelsewhereinthescript.Asafe practicetogetintoistosavetheoriginalIFSvaluebeforechangingitandthen restoreitwhenyou’refinished. Thistechniquecanbecodedlikethis: IFS.OLD=$IFS IFS=$‘∖n’ <usethenewIFSvalueincode> IFS=$IFS.OLD ThisensuresthattheIFSvalueisreturnedtothedefaultvalueforfutureoperations withinthescript. Other excellent applications of the IFS environment variable are possible. Suppose you want to iterate through values in a file that are separated by a colon (such as in the /etc/passwdfile).YoujustneedtosettheIFSvaluetoacolon: IFS=: If you want to specify more than one IFS character, just string them together on the assignmentline: IFS=$’\n’:;” Thisassignmentusesthenewline,colon,semicolon,anddoublequotationmarkcharacters as field separators. There’s no limit to how you can parse your data using the IFS characters. Readingadirectoryusingwildcards Finally,youcanusetheforcommandtoautomaticallyiteratethroughadirectoryoffiles. Todothis,youmustuseawildcardcharacterinthefileorpathname.Thisforcestheshell tousefileglobbing.Fileglobbingistheprocessofproducingfilenamesorpathnamesthat matchaspecifiedwildcardcharacter. This feature is great for processing files in a directory when you don’t know all the filenames: $cattest6 #!/bin/bash #iteratethroughallthefilesinadirectory forfilein/home/rich/test/* do if[-d“$file”] then echo“$fileisadirectory” elif[-f“$file”] then echo“$fileisafile” fi done $./test6 /home/rich/test/dir1isadirectory /home/rich/test/myprog.cisafile /home/rich/test/myprogisafile /home/rich/test/myscriptisafile /home/rich/test/newdirisadirectory /home/rich/test/newfileisafile /home/rich/test/newfile2isafile /home/rich/test/testdirisadirectory /home/rich/test/testingisafile /home/rich/test/testprogisafile /home/rich/test/testprog.cisafile $ Theforcommanditeratesthroughtheresultsofthe/home/rich/test/*listing.Thecode testseachentryusingthe testcommand(usingthesquarebracketmethod)toseeifit’sa directory,usingthe-dparameter,orafile,usingthe-fparameter(SeeChapter12). Noticeinthisexamplethatwedidsomethingdifferentintheifstatementtests: if[-d“$file”] In Linux, it’s perfectly legal to have directory and filenames that contain spaces. To accommodate that, you should enclose the $file variable in double quotation marks. If youdon’t,you’llgetanerrorifyourunintoadirectoryorfilenamethatcontainsspaces: ./test6:line6:[:toomanyarguments ./test6:line9:[:toomanyarguments The bash shell interprets the additional words as arguments within the test command, causinganerror. Youcanalsocombineboththedirectorysearchmethodandthelistmethodinthesame forstatementbylistingaseriesofdirectorywildcardsintheforcommand: $cattest7 #!/bin/bash #iteratingthroughmultipledirectories forfilein/home/rich/.b*/home/rich/badtest do if[-d“$file”] then echo“$fileisadirectory” elif[-f“$file”] then echo“$fileisafile” else echo“$filedoesn’texist” fi done $./test7 /home/rich/.backup.timestampisafile /home/rich/.bash_historyisafile /home/rich/.bash_logoutisafile /home/rich/.bash_profileisafile /home/rich/.bashrcisafile /home/rich/badtestdoesn’texist $ Theforstatementfirstusesfileglobbingtoiteratethroughthelistoffilesthatresultfrom thewildcardcharacter;thenititeratesthroughthenextfileinthelist.Youcancombine anynumberofwildcardentriesinthelisttoiteratethrough. Caution Noticethatyoucanenteranythinginthelistdata.Evenifthefileordirectorydoesn’t exist,theforstatementattemptstoprocesswhateveryouplaceinthelist.Thiscan beaproblemwhenworkingwithfilesanddirectories.Youhavenowayofknowing ifyou’retryingtoiteratethroughanonexistentdirectory:It’salwaysagoodideato testeachfileordirectorybeforetryingtoprocessit. TheC-StyleforCommand If you’ve done any programming using the C programming language, you’re probably surprisedbythewaythebashshellusesthe forcommand.IntheClanguage,a forloop normally defines a variable, which it then alters automatically during each iteration. Typically,programmersusethisvariableasacounterandeitherincrementordecrement the counter by one in each iteration. The bash for command can also provide this functionality.ThissectionshowsyouhowtouseaC-style forcommandinabashshell script. TheClanguageforcommand TheClanguageforcommandhasaspecificmethodforspecifyingavariable,acondition thatmustremaintruefortheiterationstocontinue,andamethodforalteringthevariable for each iteration. When the specified condition becomes false, the for loop stops. The conditionequationisdefinedusingstandardmathematicalsymbols.Forexample,consider thefollowingClanguagecode: for(i=0;i<10;i++) { printf(“Thenextnumberis%d\n”,i); } Thiscodeproducesasimpleiterationloop,wherethevariable iisusedasacounter.The first section assigns a default value to the variable. The middle section defines the conditionunderwhichtheloopwilliterate.Whenthedefinedconditionbecomesfalse,the for loop stops iterations. The last section defines the iteration process. After each iteration, the expression defined in the last section is executed. In this example, the i variableisincrementedbyoneaftereachiteration. ThebashshellalsosupportsaversionoftheforloopthatlookssimilartotheC-stylefor loop,althoughitdoeshavesomesubtledifferences,includingacoupleofthingsthatwill confuseshellscriptprogrammers.Here’sthebasicformatoftheC-stylebashforloop: for((variableassignment;condition;iterationprocess)) TheformatoftheC-style for loop can be confusing for bash shell script programmers, because it uses C-style variable references instead of the shell-style variable references. Here’swhataC-styleforcommandlookslike: for((a=1;a<10;a++)) Notice that there are a couple of things that don’t follow the standard bash shell for method: Theassignmentofthevariablevaluecancontainspaces. Thevariableintheconditionisn’tprecededwithadollarsign. Theequationfortheiterationprocessdoesn’tusetheexprcommandformat. The shell developers created this format to more closely resemble the C-style for command. Although this is great for C programmers, it can throw even expert shell programmersintoatizzy.BecarefulwhenusingtheC-styleforloopinyourscripts. Here’sanexampleofusingtheC-styleforcommandinabashshellprogram: $cattest8 #!/bin/bash #testingtheC-styleforloop for((i=1;i<=10;i++)) do echo“Thenextnumberis$i” done $./test8 Thenextnumberis1 Thenextnumberis2 Thenextnumberis3 Thenextnumberis4 Thenextnumberis5 Thenextnumberis6 Thenextnumberis7 Thenextnumberis8 Thenextnumberis9 Thenextnumberis10 $ The for loop iterates through the commands using the variable defined in the for loop (theletteriinthisexample).Ineachiteration,the $ivariablecontainsthevalueassigned inthe forloop.Aftereachiteration,theloopiterationprocessisappliedtothevariable, whichinthisexample,incrementsthevariablebyone. Usingmultiplevariables TheC-style forcommandalsoallowsyoutousemultiplevariablesfortheiteration.The loophandleseachvariableseparately,allowingyoutodefineadifferentiterationprocess for each variable. Although you can have multiple variables, you can define only one conditionintheforloop: $cattest9 #!/bin/bash #multiplevariables for((a=1,b=10;a<=10;a++,b—)) do echo“$a-$b” done $./test9 1-10 2-9 3-8 4-7 5-6 6-5 7-4 8-3 9-2 10-1 $ The a and b variables are each initialized with different values, and different iteration processesaredefined.Whiletheloopincreasesthe avariable,itdecreasesthe bvariable foreachiteration. ThewhileCommand The whilecommandissomewhatofacrossbetweenthe if-thenstatementandthe for loop.Thewhilecommandallowsyoutodefineacommandtotestandthenloopthrough asetofcommandsforaslongasthedefinedtestcommandreturnsazeroexitstatus.It teststhe testcommandatthestartofeachiteration.Whenthe testcommandreturnsa non-zeroexitstatus,thewhilecommandstopsexecutingthesetofcommands. Basicwhileformat Here’sfheformatofthewhilecommand: whiletestcommand do othercommands done Thetestcommanddefinedinthe whilecommandistheexactsameformatasin if-then statements(seeChapter12).Asinthe if-thenstatement,youcanuseanynormal bash shellcommand,oryoucanusethe testcommandtotestforconditions,suchasvariable values. Thekeytothe whilecommandisthattheexitstatusofthe testcommandspecifiedmust change,basedonthecommandsrunduringtheloop.Iftheexitstatusneverchanges,the whileloopwillgetstuckinaninfiniteloop. Themostcommonuseofthe testcommandistousebracketstocheckavalueofashell variablethat’susedintheloopcommands: $cattest10 #!/bin/bash #whilecommandtest var1=10 while[$var1-gt0] do echo$var1 var1=$[$var1-1] done $./test10 10 9 8 7 6 5 4 3 2 1 $ Thewhilecommanddefinesthetestconditiontocheckforeachiteration: while[$var1-gt0] As long as the test condition is true, the while command continues to loop through the commandsdefined.Withinthecommands,thevariableusedinthetestconditionmustbe modified, or you’ll have an infinite loop. In this example, we use shell arithmetic to decreasethevariablevaluebyone: var1=$[$var1-1] Thewhileloopstopswhenthetestconditionisnolongertrue. Usingmultipletestcommands Thewhilecommandallowsyoutodefinemultipletestcommandsonthewhilestatement line. Only the exit status of the last test command is used to determine when the loop stops.Thiscancausesomeinterestingresultsifyou’renotcareful.Here’sanexampleof whatwemean: $cattest11 #!/bin/bash #testingamulticommandwhileloop var1=10 whileecho$var1 [$var1-ge0] do echo“Thisisinsidetheloop” var1=$[$var1-1] done $./test11 10 Thisisinsidetheloop 9 Thisisinsidetheloop 8 Thisisinsidetheloop 7 Thisisinsidetheloop 6 Thisisinsidetheloop 5 Thisisinsidetheloop 4 Thisisinsidetheloop 3 Thisisinsidetheloop 2 Thisisinsidetheloop 1 Thisisinsidetheloop 0 Thisisinsidetheloop -1 $ Paycloseattentiontowhathappenedinthisexample.Twotestcommandsweredefinedin thewhilestatement: whileecho$var1 [$var1-ge0] Thefirsttestsimplydisplaysthecurrentvalueofthe var1variable.Thesecondtestuses brackets to determine the value of the var1 variable. Inside the loop, an echostatement displaysasimplemessage,indicatingthattheloopwasprocessed.Noticewhenyourun theexamplehowtheoutputends: Thisisinsidetheloop -1 $ Thewhileloopexecutedtheechostatementwhenthevar1variablewasequaltozeroand then decreased the var1 variable value. Next, the test commands were executed for the next iteration. The echo test command was executed, displaying the value of the var1 variable, which is now less than zero. It’s not until the shell executes the test test commandthatthewhileloopterminates. This demonstrates that in a multi-command while statement, all the test commands are executedineachiteration,includingthelastiterationwhenthelasttestcommandfails.Be careful of this. Another thing to be careful of is how you specify the multiple test commands.Notethateachtestcommandisonaseparateline! TheuntilCommand The until command works in exactly the opposite way from the while command. The untilcommandrequiresthatyouspecifyatestcommandthatnormallyproducesanonzeroexitstatus.Aslongastheexitstatusofthetestcommandisnon-zero,thebashshell executes the commands listed in the loop. When the test command returns a zero exit status,theloopstops. Asyouwouldexpect,theformatoftheuntilcommandis: untiltestcommands do othercommands done Similartothe whilecommand,youcanhavemorethanonetestcommandinthe until commandstatement.Onlytheexitstatusofthelastcommanddeterminesifthebashshell executestheothercommandsdefined. Thefollowingisanexampleofusingtheuntilcommand: $cattest12 #!/bin/bash #usingtheuntilcommand var1=100 until[$var1-eq0] do echo$var1 var1=$[$var1-25] done $./test12 100 75 50 25 $ Thisexampleteststhe var1 variable to determine when the until loop should stop. As soonasthevalueofthevariableisequaltozero,the untilcommandstopstheloop.The same caution as for the while command applies when you use multiple test commands withtheuntilcommand: $cattest13 #!/bin/bash #usingtheuntilcommand var1=100 untilecho$var1 [$var1-eq0] do echoInsidetheloop:$var1 var1=$[$var1-25] done $./test13 100 Insidetheloop:100 75 Insidetheloop:75 50 Insidetheloop:50 25 Insidetheloop:25 0 $ Theshellexecutesthetestcommandsspecifiedandstopsonlywhenthelastcommandis true. NestingLoops Aloopstatementcanuseanyothertypeofcommandwithintheloop,includingotherloop commands.Thisiscalleda nestedloop. Care should be taken when using nested loops, becauseyou’reperforminganiterationwithinaniteration,whichmultipliesthenumberof times commands are being run. If you don’t pay close attention to this, it can cause problemsinyourscripts. Here’sasimpleexampleofnestingaforloopinsideanotherforloop: $cattest14 #!/bin/bash #nestingforloops for((a=1;a<=3;a++)) do echo“Startingloop$a:” for((b=1;b<=3;b++)) do echo“Insideloop:$b” done done $./test14 Startingloop1: Insideloop:1 Insideloop:2 Insideloop:3 Startingloop2: Insideloop:1 Insideloop:2 Insideloop:3 Startingloop3: Insideloop:1 Insideloop:2 Insideloop:3 $ Thenestedloop(alsocalledtheinnerloop)iteratesthroughitsvaluesforeachiterationof theouterloop.Noticethatthere’snodifferencebetweenthe doand donecommandsfor the two loops. The bash shell knows when the first done command is executed that it referstotheinnerloopandnottheouterloop. The same applies when you mix loop commands, such as placing a for loop inside a whileloop: $cattest15 #!/bin/bash #placingaforloopinsideawhileloop var1=5 while[$var1-ge0] do echo“Outerloop:$var1” for((var2=1;$var2<3;var2++)) do var3=$[$var1*$var2] echo“Innerloop:$var1*$var2=$var3” done var1=$[$var1-1] done $./test15 Outerloop:5 Innerloop:5*1=5 Innerloop:5*2=10 Outerloop:4 Innerloop:4*1=4 Innerloop:4*2=8 Outerloop:3 Innerloop:3*1=3 Innerloop:3*2=6 Outerloop:2 Innerloop:2*1=2 Innerloop:2*2=4 Outerloop:1 Innerloop:1*1=1 Innerloop:1*2=2 Outerloop:0 Innerloop:0*1=0 Innerloop:0*2=0 $ Again,theshelldistinguishedbetweenthe doand donecommandsoftheinner forloop fromthesamecommandsintheouterwhileloop. Ifyoureallywanttotestyourbrain,youcanevencombineuntilandwhileloops: $cattest16 #!/bin/bash #usinguntilandwhileloops var1=3 until[$var1-eq0] do echo“Outerloop:$var1” var2=1 while[$var2-lt5] do var3=$(echo“scale=4;$var1/$var2”|bc) echo“Innerloop:$var1/$var2=$var3” var2=$[$var2+1] done var1=$[$var1-1] done $./test16 Outerloop:3 Innerloop:3/1=3.0000 Innerloop:3/2=1.5000 Innerloop:3/3=1.0000 Innerloop:3/4=.7500 Outerloop:2 Innerloop:2/1=2.0000 Innerloop:2/2=1.0000 Innerloop:2/3=.6666 Innerloop:2/4=.5000 Outerloop:1 Innerloop:1/1=1.0000 Innerloop:1/2=.5000 Innerloop:1/3=.3333 Innerloop:1/4=.2500 $ Theouter untilloopstartswithavalueof3andcontinuesuntilthevalueequals0.The innerwhileloopstartswithavalueof1andcontinuesaslongasthevalueislessthan5. Each loop must change the value used in the test condition, or the loop will get stuck infinitely. LoopingonFileData Often,youmustiteratethroughitemsstoredinsideafile.Thisrequirescombiningtwoof thetechniquescovered: Usingnestedloops ChangingtheIFSenvironmentvariable BychangingtheIFSenvironmentvariable,youcanforcetheforcommandtohandleeach line in the file as a separate item for processing, even if the data contains spaces. After you’veextractedanindividuallineinthefile,youmayhavetoloopagaintoextractdata containedwithinit. Theclassicexampleofthisisprocessingdatainthe /etc/passwdfile.Thisrequiresthat you iterate through the /etc/passwd file line by line and then change the IFS variable valuetoacolonsoyoucanseparatetheindividualcomponentsineachline. Thefollowingisanexampleofdoingjustthat: #!/bin/bash #changingtheIFSvalue IFS.OLD=$IFS IFS=$’\n’ forentryin$(cat/etc/passwd) do echo“Valuesin$entry–” IFS=: forvaluein$entry do echo“$value” done done $ Thisscriptusestwodifferent IFSvaluestoparsethedata.Thefirst IFSvalueparsesthe individuallinesinthe/etc/passwdfile.Theinnerforloopnextchangesthe IFSvalueto thecolon,whichallowsyoutoparsetheindividualvalueswithinthe/etc/passwdlines. Whenyourunthisscript,yougetoutputsomethinglikethis: Valuesinrich:x:501:501:RichBlum:/home/rich:/bin/bashrich x 501 501 RichBlum /home/rich /bin/bash Valuesinkatie:x:502:502:KatieBlum:/home/katie:/bin/bashkatie x 506 509 KatieBlum /home/katie /bin/bash Theinnerloopparseseachindividualvalueinthe /etc/passwdentry.Thisisalsoagreat waytoprocesscomma-separateddata,acommonwaytoimportspreadsheetdata. ControllingtheLoop You might be tempted to think that after you start a loop, you’re stuck until the loop finishes all its iterations. This is not true. A couple of commands help us control what happensinsideofaloop: Thebreakcommand Thecontinuecommand Eachcommandhasadifferentuseinhowtocontroltheoperationofaloop.Thefollowing sectionsdescribehowyoucanusethesecommandstocontroltheoperationofyourloops. Thebreakcommand Thebreakcommandisasimplewaytoescapealoopinprogress.Youcanusethe break commandtoexitanytypeofloop,includingwhileanduntilloops. Youcanusethe breakcommandinseveralsituations.Thissectionshowseachofthese methods. Breakingoutofasingleloop When the shell executes a break command, it attempts to break out of the loop that’s currentlyprocessing: $cattest17 #!/bin/bash #breakingoutofaforloop forvar1in12345678910 do if[$var1-eq5] then break fi echo“Iterationnumber:$var1” done echo“Theforloopiscompleted” $./test17 Iterationnumber:1 Iterationnumber:2 Iterationnumber:3 Iterationnumber:4 Theforloopiscompleted $ The for loop should normally have iterated through all the values specified in the list. However, when the if-then condition was satisfied, the shell executed the break command,whichstoppedtheforloop. Thistechniquealsoworksforwhileanduntilloops: $cattest18 #!/bin/bash #breakingoutofawhileloop var1=1 while[$var1-lt10] do if[$var1-eq5] then break fi echo“Iteration:$var1” var1=$[$var1+1] done echo“Thewhileloopiscompleted” $./test18 Iteration:1 Iteration:2 Iteration:3 Iteration:4 Thewhileloopiscompleted $ The while loop terminated when the if-then condition was met, executing the break command. Breakingoutofaninnerloop Whenyou’reworkingwithmultipleloops,the breakcommandautomaticallyterminates theinnermostloopyou’rein: $cattest19 #!/bin/bash #breakingoutofaninnerloop for((a=1;a<4;a++)) do echo“Outerloop:$a” for((b=1;b<100;b++)) do if[$b-eq5] then break fi echo“Innerloop:$b” done done $./test19 Outerloop:1 Innerloop:1 Innerloop:2 Innerloop:3 Innerloop:4 Outerloop:2 Innerloop:1 Innerloop:2 Innerloop:3 Innerloop:4 Outerloop:3 Innerloop:1 Innerloop:2 Innerloop:3 Innerloop:4 $ Theforstatementintheinnerloopspecifiestoiterateuntilthebvariableisequalto100. However,theif-thenstatementintheinnerloopspecifiesthatwhenthe bvariablevalue isequalto5,the breakcommandisexecuted.Noticethateventhoughtheinnerloopis terminatedwiththebreakcommand,theouterloopcontinuesworkingasspecified. Breakingoutofanouterloop There may be times when you’re in an inner loop but need to stop the outer loop. The breakcommandincludesasinglecommandlineparametervalue: breakn wheren indicates the level of the loop to break out of. By default, n is 1, indicating to breakoutofthecurrentloop.Ifyousetntoavalueof2,the breakcommandstopsthe nextleveloftheouterloop: $cattest20 #!/bin/bash #breakingoutofanouterloop for((a=1;a<4;a++)) do echo“Outerloop:$a” for((b=1;b<100;b++)) do if[$b-gt4] then break2 fi echo“Innerloop:$b” done done $./test20 Outerloop:1 Innerloop:1 Innerloop:2 Innerloop:3 Innerloop:4 $ Nowwhentheshellexecutesthebreakcommand,theouterloopstops. Thecontinuecommand The continue command is a way to prematurely stop processing commands inside of a loopbutnotterminatetheloopcompletely.Thisallowsyoutosetconditionswithinaloop wheretheshellwon’texecutecommands.Here’sasimpleexampleofusingthecontinue commandinaforloop: $cattest21 #!/bin/bash #usingthecontinuecommand for((var1=1;var1<15;var1++)) do if[$var1-gt5]&&[$var1-lt10] then continue fi echo“Iterationnumber:$var1” done $./test21 Iterationnumber:1 Iterationnumber:2 Iterationnumber:3 Iterationnumber:4 Iterationnumber:5 Iterationnumber:10 Iterationnumber:11 Iterationnumber:12 Iterationnumber:13 Iterationnumber:14 $ Whentheconditionsoftheif-thenstatementaremet(thevalueisgreaterthan5andless than10),theshellexecutesthecontinuecommand,whichskipstherestofthecommands intheloop,butkeepstheloopgoing.Whentheif-thenconditionisnolongermet,things returntonormal. Youcanusethe continuecommandin whileand untilloops,butbeextremelycareful withwhatyou’redoing.Rememberthatwhentheshellexecutesthe continuecommand, it skips the remaining commands. If you’re incrementing your test condition variable in oneofthoseconditions,badthingshappen: $catbadtest3 #!/bin/bash #improperlyusingthecontinuecommandinawhileloop var1=0 whileecho“whileiteration:$var1” [$var1-lt15] do if[$var1-gt5]&&[$var1-lt10] then continue fi echo“Insideiterationnumber:$var1” var1=$[$var1+1] done $./badtest3|more whileiteration:0 Insideiterationnumber:0 whileiteration:1 Insideiterationnumber:1 whileiteration:2 Insideiterationnumber:2 whileiteration:3 Insideiterationnumber:3 whileiteration:4 Insideiterationnumber:4 whileiteration:5 Insideiterationnumber:5 whileiteration:6 whileiteration:6 whileiteration:6 whileiteration:6 whileiteration:6 whileiteration:6 whileiteration:6 whileiteration:6 whileiteration:6 whileiteration:6 whileiteration:6 $ You’ll want to make sure you redirect the output of this script to the morecommandso youcanstopthings.Everythingseemstobegoingjustfineuntilthe if-thenconditionis met,andtheshellexecutesthecontinuecommand.Whentheshellexecutesthecontinue command,itskipstheremainingcommandsinthewhileloop.Unfortunately,that’swhere the $var1countervariablethatistestedinthe whiletestcommandisincremented.That meansthatthevariableisn’tincremented,asyoucanseefromthecontinuallydisplaying output. Aswiththe breakcommand,the continuecommandallowsyoutospecifywhatlevelof looptocontinuewithacommandlineparameter: continuen wherendefinestheloopleveltocontinue.Here’sanexampleofcontinuinganouter for loop: $cattest22 #!/bin/bash #continuinganouterloop for((a=1;a<=5;a++)) do echo“Iteration$a:” for((b=1;b<3;b++)) do if[$a-gt2]&&[$a-lt4] then continue2 fi var3=$[$a*$b] echo“Theresultof$a*$bis$var3” done done $./test22 Iteration1: Theresultof1*1is1 Theresultof1*2is2 Iteration2: Theresultof2*1is2 Theresultof2*2is4 Iteration3: Iteration4: Theresultof4*1is4 Theresultof4*2is8 Iteration5: Theresultof5*1is5 Theresultof5*2is10 $ Theif-thenstatement: if[$a-gt2]&&[$a-lt4] then continue2 fi usesthecontinuecommandtostopprocessingthecommandsinsidetheloopbutcontinue theouterloop.Noticeinthescriptoutputthattheiterationforthevalue3doesn’tprocess anyinnerloopstatements,becausethe continuecommandstoppedtheprocessing,butit continueswiththeouterloopprocessing. ProcessingtheOutputofaLoop Finally,youcaneitherpipeorredirecttheoutputofaloopwithinyourshellscript.Youdo thisbyaddingtheprocessingcommandtotheendofthedonecommand: forfilein/home/rich/* do if[-d“$file”] then echo“$fileisadirectory” elif echo“$fileisafile” fi done>output.txt Insteadofdisplayingtheresultsonthemonitor,theshellredirectstheresultsofthe for commandtothefileoutput.txt. Considerthefollowingexampleofredirectingtheoutputofaforcommandtoafile: $cattest23 #!/bin/bash #redirectingtheforoutputtoafile for((a=1;a<10;a++)) do echo“Thenumberis$a” done>test23.txt echo“Thecommandisfinished.” $./test23 Thecommandisfinished. $cattest23.txt Thenumberis1 Thenumberis2 Thenumberis3 Thenumberis4 Thenumberis5 Thenumberis6 Thenumberis7 Thenumberis8 Thenumberis9 $ Theshellcreatesthefiletest23.txtandredirectstheoutputoftheforcommandonlyto thefile.Theshelldisplaystheechostatementaftertheforcommandjustasnormal. Thissametechniquealsoworksforpipingtheoutputofalooptoanothercommand: $cattest24 #!/bin/bash #pipingalooptoanothercommand forstatein“NorthDakota”ConnecticutIllinoisAlabamaTennessee do echo“$stateisthenextplacetogo” done|sort echo“Thiscompletesourtravels” $./test24 Alabamaisthenextplacetogo Connecticutisthenextplacetogo Illinoisisthenextplacetogo NorthDakotaisthenextplacetogo Tennesseeisthenextplacetogo Thiscompletesourtravels $ Thestatevaluesaren’tlistedinanyparticularorderinthe forcommandlist.Theoutput ofthe forcommandispipedtothe sortcommand,whichchangestheorderofthe for command output. Running the script indeed shows that the output was properly sorted withinthescript. PracticalExamples Nowthatyou’veseenhowtousethedifferentwaystocreateloopsinshellscripts,let’s lookatsomepracticalexamplesofhowtousethem.Loopingisacommonwaytoiterate throughdataonthesystem,whetherit’sfilesinfoldersordatacontainedinafile.Hereare acoupleofexamplesthatdemonstrateusingsimpleloopstoworkwithdata. Findingexecutablefiles Whenyourunaprogramfromthecommandline,theLinuxsystemsearchesaseriesof folderslookingforthatfile.ThosefoldersaredefinedinthePATHenvironmentvariable.If youwanttofindoutjustwhatexecutablefilesareavailableonyoursystemforyoutouse, justscanallthefoldersinthe PATHenvironmentvariable.Thatmaytakesometimetodo manually,butit’sabreezeworkingoutasmallshellscripttodothat. The first step is to create a for loop to iterate through the folders stored in the PATH environmentvariable.Whenyoudothat,don’tforgettosettheIFSseparatorcharacter: IFS=: forfolderin$PATH do Nowthatyouhavetheindividualfoldersinthe$foldervariable,youcanuseanotherfor looptoiteratethroughallthefilesinsidethatparticularfolder: forfilein$folder/* do Thelaststepistocheckwhethertheindividualfileshavetheexecutablepermissionset, whichyoucandousingtheif-thentestfeature: if[-x$file] then echo“$file” fi Andthereyouhaveit!Puttingallthepiecestogetherintoascriptlookslikethis: $cattest25 #!/bin/bash #findingfilesinthePATH IFS=: forfolderin$PATH do echo“$folder:” forfilein$folder/* do if[-x$file] then echo“$file” fi done done $ Whenyourunthecode,yougetalistingoftheexecutablefilesthatyoucanusefromthe commandline: $./test25|more /usr/local/bin: /usr/bin: /usr/bin/Mail /usr/bin/Thunar /usr/bin/X /usr/bin/Xorg /usr/bin/[ /usr/bin/a2p /usr/bin/abiword /usr/bin/ac /usr/bin/activation-client /usr/bin/addr2line … The output shows all the executable files found in all the folders defined in the PATH environmentvariable,whichisquiteafew! Creatingmultipleuseraccounts Thegoalofshellscriptsistomakelifeeasierforthesystemadministrator.Ifyouhappen toworkinanenvironmentwithlotsofusers,oneofthemostboringtaskscanbecreating new user accounts. Fortunately, you can use the while loop to make your job a little easier! Insteadofhavingtomanuallyenter useraddcommandsforeverynewuseraccountyou needtocreate,youcanplacethenewuseraccountsinatextfileandcreateasimpleshell scripttodothatworkforyou.Theformatofthetextfilethatwe’lluselookslikethis: userid,username Thefirstentryistheuseridyouwanttouseforthenewuseraccount.Thesecondentryis thefullnameoftheuser.Thetwovaluesareseparatedbyacomma,makingthisacommaseparatedfileformat,or.csv.Thisisaverycommonfileformatusedinspreadsheets,so you can easily create the user account list in a spreadsheet program and save it in .csv formatforyourshellscripttoreadandprocess. Toreadthefiledata,we’regoingtousealittleshellscriptingtrick.We’llactuallysetthe IFSseparatorcharactertoacommaasthetestpartofthe whilestatement.Thentoread theindividuallines,we’llusethereadcommand.Thatlookslikethis: whileIFS=’,’read–ruseridname Thereadcommanddoestheworkofmovingontothenextlineoftextinthe.csvtextfile, so we don’t need another loop to do that. The while command exits when the read commandreturnsa FALSE value, which happens when it runs out of lines to read in the file.Tricky! Tofeedthedatafromthefileintothe whilecommand,youjustusearedirectionsymbol attheendofthewhilecommand. Puttingeverythingtogetherresultsinthisscript: $cattest26 #!/bin/bash #processnewuseraccounts input=“users.csv” whileIFS=’,’read-ruseridname do echo“adding$userid” useradd-c“$name”-m$userid done<“$input” $ The $inputvariablepointstothedatafileandisusedastheredirectdataforthe while command.Theusers.csvfilelookslikethis: $catusers.csv rich,RichardBlum christine,ChristineBresnahan barbara,BarbaraBlum tim,TimothyBresnahan $ To run the problem, you must be the root user account, because the useraddcommand requiresrootprivileges: #./test26 addingrich addingchristine addingbarbara addingtim # Thenbytakingaquicklookatthe /etc/passwdfile,youcanseethattheaccountshave beencreated: #tail/etc/passwd rich:x:1001:1001:RichardBlum:/home/rich:/bin/bash christine:x:1002:1002:ChristineBresnahan:/home/christine:/bin/bash barbara:x:1003:1003:BarbaraBlum:/home/barbara:/bin/bash tim:x:1004:1004:TimothyBresnahan:/home/tim:/bin/bash # Congratulations,you’vesavedyourselflotsoftimeinaddinguseraccounts! Summary Looping is an integral part of programming. The bash shell provides three looping commandsthatyoucanuseinyourscripts. Theforcommandallowsyoutoiteratethroughalistofvalues,eithersuppliedwithinthe commandline,containedinavariable,orobtainedbyusingfileglobbing,toextractfile anddirectorynamesfromawildcardcharacter. The while command provides a method to loop based on the condition of a command, usingeitherordinarycommandsorthetestcommand,whichallowsyoutotestconditions ofvariables.Aslongasthecommand(orcondition)producesazeroexitstatus,thewhile loopcontinuestoiteratethroughthespecifiedsetofcommands. Theuntilcommandalsoprovidesamethodtoiteratethroughcommands,butitbasesits iterations on a command (or condition) producing a non-zero exit status. This feature allowsyoutosetaconditionthatmustbemetbeforetheiterationstops. Youcancombineloopsinshellscripts,producingmultiplelayersofloops.Thebashshell provides the continue and break commands, which allow you to alter the flow of the normalloopprocessbasedondifferentvalueswithintheloop. Thebashshellalsoallowsyoutousestandardcommandredirectionandpipingtoalterthe outputofaloop.Youcanuseredirectiontoredirecttheoutputofalooptoafileorpiping to redirect the output of a loop to another command. This provides a wealth of features withwhichyoucancontrolyourshellscriptexecution. Thenextchapterdiscusseshowtointeractwithyourshellscriptuser.Often,shellscripts aren’t completely self-contained. They require some sort of external data that must be supplied at the time you run them. The next chapter discusses different methods with whichyoucanprovidereal-timedatatoyourshellscriptsforprocessing. Chapter14 HandlingUserInput InThisChapter 1. Passingparameters 2. Trackingparameters 3. Beingshifty 4. Workingwithoptions 5. Standardizingoptions 6. Gettinguserinput Sofaryou’veseenhowtowritescriptsthatinteractwithdata,variables,andfileson theLinuxsystem.Sometimes,youneedtowriteascriptthathastointeractwiththe personrunningthescript.Thebashshellprovidesafewdifferentmethodsfor retrievingdatafrompeople,includingcommandlineparameters(datavaluesadded afterthecommand),commandlineoptions(single-lettervaluesthatmodifythe behaviorofthecommand),andthecapabilitytoreadinputdirectlyfromthe keyboard.Thischapterdiscusseshowtoincorporatethesedifferentmethodsinto yourbashshellscriptstoobtaindatafromthepersonrunningyourscript. PassingParameters The most basic method of passing data to your shell script is to use command line parameters.Commandlineparametersallowyoutoadddatavaluestothecommandline whenyouexecutethescript: $./addem1030 Thisexamplepassestwocommandlineparameters(10and 30)tothescript addem.The script handles the command line parameters using special variables. The following sectionsdescribehowtousecommandlineparametersinyourbashshellscripts. Readingparameters The bash shell assigns special variables, called positional parameters, to all of the command line parameters entered. This includes the name of the script the shell is executing. The positional parameter variables are standard numbers, with $0 being the script’sname,$1beingthefirstparameter,$2beingthesecondparameter,andsoon,upto $9fortheninthparameter. Here’sasimpleexampleofusingonecommandlineparameterinashellscript: $cattest1.sh #!/bin/bash #usingonecommandlineparameter # factorial=1 for((number=1;number<=$1;number++)) do factorial=$[$factorial*$number] done echoThefactorialof$1is$factorial $ $./test1.sh5 Thefactorialof5is120 $ Youcanusethe $1variablejustlikeanyothervariableintheshellscript.Theshellscript automatically assigns the value from the command line parameter to the variable; you don’tneedtodoanythingwithit. Ifyouneedtoentermorecommandlineparameters,eachparametermustbeseparatedby aspaceonthecommandline: $cattest2.sh #!/bin/bash #testingtwocommandlineparameters # total=$[$1*$2] echoThefirstparameteris$1. echoThesecondparameteris$2. echoThetotalvalueis$total. $ $./test2.sh25 Thefirstparameteris2. Thesecondparameteris5. Thetotalvalueis10. $ Theshellassignseachparametertotheappropriatevariable. Intheprecedingexample,thecommandlineparametersusedwerebothnumericalvalues. Youcanalsousetextstringsinthecommandline: $cattest3.sh #!/bin/bash #testingstringparameters # echoHello$1,gladtomeetyou. $ $./test3.shRich HelloRich,gladtomeetyou. $ The shell passes the string value entered into the command line to the script. However, you’llhaveaproblemifyoutrytodothiswithatextstringthatcontainsspaces: $./test3.shRichBlum HelloRich,gladtomeetyou. $ Rememberthateachoftheparametersisseparatedbyaspace,sotheshellinterpretedthe spaceasjustseparatingthetwovalues.Toincludeaspaceasaparametervalue,youmust usequotationmarks(eithersingleordoublequotationmarks): $./test3.sh‘RichBlum’ HelloRichBlum,gladtomeetyou. $ $./test3.sh“RichBlum” HelloRichBlum,gladtomeetyou. $ Note Thequotationmarksusedwhenyoupasstextstringsasparametersarenotpartofthe data.Theyjustdelineatethebeginningandtheendofthedata. Ifyourscriptneedsmorethanninecommandlineparameters,youcancontinue,butthe variablenameschangeslightly.Aftertheninthvariable,youmustusebracesaroundthe variablenumber,suchas${10}.Here’sanexampleofdoingthat: $cattest4.sh #!/bin/bash #handlinglotsofparameters # total=$[${10}*${11}] echoThetenthparameteris${10} echoTheeleventhparameteris${11} echoThetotalis$total $ $./test4.sh123456789101112 Thetenthparameteris10 Theeleventhparameteris11 Thetotalis110 $ Thistechniqueallowsyoutoaddasmanycommandlineparameterstoyourscriptsasyou couldpossiblyneed. Readingthescriptname You can use the $0 parameter to determine the script name the shell started from the commandline.Thiscancomeinhandyifyou’rewritingautilitythatcanhavemultiple functions. $cattest5.sh #!/bin/bash #Testingthe$0parameter # echoThezeroparameterissetto:$0 # $ $bashtest5.sh Thezeroparameterissetto:test5.sh $ However,thereisapotentialproblem.Whenusingadifferentcommandtoruntheshell script,thecommandbecomesentangledwiththescriptnameinthe$0parameter: $./test5.sh Thezeroparameterissetto:./test5.sh $ Thereisanotherpotentialproblem.Whentheactualstringpassedisthefullscriptpath, andnotjustthescript’sname,the$0variablegetssettothefullscriptpathandname: $bash/home/Christine/test5.sh Thezeroparameterissetto:/home/Christine/test5.sh $ If you want to write a script that performs different functions based on just the script’s name,you’llhavetodoalittlework.Youneedtobeabletostripoffwhateverpathisused torunthescript.Also,youneedtobeabletoremoveanyentangledcommandsfromthe script. Fortunately, there’s a handy little command available that does just that. The basename commandreturnsjustthescript’snamewithoutthepath: $cattest5b.sh #!/bin/bash #Usingbasenamewiththe$0parameter # name=$(basename$0) echo echoThescriptnameis:$name # $bash/home/Christine/test5b.sh Thescriptnameis:test5b.sh $ $./test5b.sh Thescriptnameis:test5b.sh $ Nowthat’smuchbetter.Youcanusethistechniquetowritescriptsthatperformdifferent functionsbasedonthescriptnameused.Here’sasimpleexample: $cattest6.sh #!/bin/bash #TestingaMulti-functionscript # name=$(basename$0) # if[$name=“addem”] then total=$[$1+$2] # elif[$name=“multem”] then total=$[$1*$2] fi # echo echoThecalculatedvalueis$total # $ $cptest6.shaddem $chmodu+xaddem $ $ln-stest6.shmultem $ $ls-l*em -rwxrw-r—.1ChristineChristine224Jun3023:50addem lrwxrwxrwx.1ChristineChristine8Jun3023:50multem->test6.sh $ $./addem25 Thecalculatedvalueis7 $ $./multem25 Thecalculatedvalueis10 $ Theexamplecreatestwoseparatefilenamesfromthetest6.shscript,onebyjustcopying thefiletoanewscript(addem)andtheotherbyusingasymboliclink(seeChapter3)to createthenewscript(multem).Inbothcases,thescriptdeterminesthescript’sbasename andperformstheappropriatefunctionbasedonthatvalue. Testingparameters Becarefulwhenusingcommandlineparametersinyourshellscripts.Ifthescriptisrun withouttheparameters,badthingscanhappen: $./addem2 ./addem:line8:2+:syntaxerror:operandexpected(error tokenis”“) Thecalculatedvalueis $ Whenthescriptassumesthereisdatainaparametervariable,andnodataispresent,most likely you’ll get an error message from your script. This is a poor way to write scripts. Alwayscheckyourparameterstomakesurethedataistherebeforeusingit: $cattest7.sh #!/bin/bash #testingparametersbeforeuse # if[-n“$1”] then echoHello$1,gladtomeetyou. else echo“Sorry,youdidnotidentifyyourself.“ fi $ $./test7.shRich HelloRich,gladtomeetyou. $ $./test7.sh Sorry,youdidnotidentifyyourself. $ Inthisexample,the -ntestevaluationwasusedtocheckfordatainthe $1commandline parameter. In the next section, you’ll learn another way to check command line parameters. UsingSpecialParameterVariables Afewspecialbashshellvariablestrackcommandlineparameters.Thissectiondescribes whattheyareandhowtousethem. Countingparameters Asyousawinthelastsection,youshouldverifycommandlineparametersbeforeusing theminyourscript.Forscriptsthatusemultiplecommandlineparameters,thischecking cangettedious. Instead of testing each parameter, you can count how many parameters were entered on thecommandline.Thebashshellprovidesaspecialvariableforthispurpose. Thespecial $#variablecontainsthenumberofcommandlineparametersincludedwhen the script was run. You can use this special variable anywhere in the script, just like a normalvariable: $cattest8.sh #!/bin/bash #gettingthenumberofparameters # echoTherewere$#parameterssupplied. $ $./test8.sh Therewere0parameterssupplied. $ $./test8.sh12345 Therewere5parameterssupplied. $ $./test8.sh12345678910 Therewere10parameterssupplied. $ $./test8.sh“RichBlum” Therewere1parameterssupplied. $ Now you have the ability to test the number of parameters present before trying to use them: $cattest9.sh #!/bin/bash #Testingparameters # if[$#-ne2] then echo echoUsage:test9.shab echo else total=$[$1+$2] echo echoThetotalis$total echo fi # $ $bashtest9.sh Usage:test9.shab $bashtest9.sh10 Usage:test9.shab $bashtest9.sh1015 Thetotalis25 $ Theif-thenstatementusesthe-neevaluationtoperformanumerictestofthecommand line parameters supplied. If the correct number of parameters isn’t present, an error messagedisplaysshowingthecorrectusageofthescript. Thisvariablealsoprovidesacoolwayofgrabbingthelastparameteronthecommandline without having to know how many parameters were used. However, you need to use a littletricktogetthere. Ifyouthinkthisthrough,youmightthinkthatbecausethe $#variablecontainsthevalue ofthenumberofparameters,usingthevariable ${$#}wouldrepresentthelastcommand lineparametervariable.Trythatandseewhathappens: $catbadtest1.sh #!/bin/bash #testinggrabbinglastparameter # echoThelastparameterwas${$#} $ $./badtest1.sh10 Thelastparameterwas15354 $ Wow,whathappened?Obviously,somethingwentwrong.Itturnsoutthatyoucan’tuse the dollar sign within the braces. Instead, you must replace the dollar sign with an exclamationmark.Odd,butitworks: $cattest10.sh #!/bin/bash #Grabbingthelastparameter # params=$# echo echoThelastparameteris$params echoThelastparameteris${!#} echo # $ $bashtest10.sh12345 Thelastparameteris5 Thelastparameteris5 $ $bashtest10.sh Thelastparameteris0 Thelastparameteristest10.sh $ Perfect. This script also assigned the $# variable value to the variable params and then used that variable within the special command line parameter variable format as well. Both versions worked. It’s also important to notice that, when there weren’t any parameters on the command line, the $# value was zero, which is what appears in the paramsvariable,butthe${!#}variablereturnsthescriptnameusedonthecommandline. Grabbingallthedata In some situations you want to grab all the parameters provided on the command line. Insteadofhavingtomesswithusingthe $#variabletodeterminehowmanyparameters areonthecommandlineandhavingtoloopthroughallofthem,youcanuseacoupleof otherspecialvariables. The$*and$@variablesprovideeasyaccesstoallyourparameters.Bothofthesevariables includeallthecommandlineparameterswithinasinglevariable. The $*variabletakesalltheparameterssuppliedonthecommandlineasasingleword. The word contains each of the values as they appear on the command line. Basically, insteadoftreatingtheparametersasmultipleobjects,the$*variabletreatsthemallasone parameter. The$@variable,ontheotherhand,takesalltheparameterssuppliedonthecommandline asseparatewordsinthesamestring.Itallowsyoutoiteratethroughthevalues,separating outeachparametersupplied.Thisismostoftenaccomplishedusingtheforcommand. Itcaneasilygetconfusingtofigureouthowthesetwovariablesoperate.Let’slookatthe differencebetweenthetwo: $cattest11.sh #!/bin/bash #testing$*and$@ # echo echo“Usingthe\$*method:$*” echo echo“Usingthe\$@method:$@” $ $./test11.shrichbarbarakatiejessica Usingthe$*method:richbarbarakatiejessica Usingthe$@method:richbarbarakatiejessica $ Notice that on the surface, both variables produce the same output, showing all the commandlineparametersprovidedatonce. Thefollowingexampledemonstrateswherethedifferencesare: $cattest12.sh #!/bin/bash #testing$*and$@ # echo count=1 # forparamin“$*” do echo“\$*Parameter#$count=$param” count=$[$count+1] done # echo count=1 # forparamin“$@” do echo“\$@Parameter#$count=$param” count=$[$count+1] done $ $./test12.shrichbarbarakatiejessica $*Parameter#1=richbarbarakatiejessica $@Parameter#1=rich $@Parameter#2=barbara $@Parameter#3=katie $@Parameter#4=jessica $ Nowwe’regettingsomewhere.Byusingthe forcommandtoiteratethroughthespecial variables,youcanseehowtheyeachtreatthecommandlineparametersdifferently.The $*variabletreatedalltheparametersasasingleparameter,whilethe $@variabletreated eachparameterseparately.Thisisagreatwaytoiteratethroughcommandlineparameters. BeingShifty Anothertoolyouhaveinyourbashshelltoolbeltisthe shiftcommand.Thebashshell provides the shift command to help you manipulate command line parameters. The shiftcommandliterallyshiftsthecommandlineparametersintheirrelativepositions. Whenyouusethe shiftcommand,itmoveseachparametervariableonepositiontothe leftbydefault.Thus,thevalueforvariable $3ismovedto $2,thevalueforvariable $2is movedto$1,andthevalueforvariable$1isdiscarded(notethatthevalueforvariable$0, theprogramname,remainsunchanged). This is another great way to iterate through command line parameters, especially if you don’t know how many parameters are available. You can just operate on the first parameter,shifttheparametersover,andthenoperateonthefirstparameteragain. Here’sashortdemonstrationofhowthisworks: $cattest13.sh #!/bin/bash #demonstratingtheshiftcommand echo count=1 while[-n“$1”] do echo“Parameter#$count=$1” count=$[$count+1] shift done $ $./test13.shrichbarbarakatiejessica Parameter#1=rich Parameter#2=barbara Parameter#3=katie Parameter#4=jessica $ Thescriptperformsa whileloop,testingthelengthofthefirstparameter’svalue.When the first parameter’s length is zero, the loop ends. After testing the first parameter, the shiftcommandisusedtoshiftalltheparametersoneposition. Tip Becarefulwhenworkingwiththeshiftcommand.Whenaparameterisshiftedout, itsvalueislostandcan’tberecovered. Alternatively, you can perform a multiple location shift by providing a parameter to the shiftcommand.Justprovidethenumberofplacesyouwanttoshift: $cattest14.sh #!/bin/bash #demonstratingamulti-positionshift # echo echo“Theoriginalparameters:$*” shift2 echo“Here’sthenewfirstparameter:$1” $ $./test14.sh12345 Theoriginalparameters:12345 Here’sthenewfirstparameter:3 $ By using values in the shift command, you can easily skip over parameters you don’t need. WorkingwithOptions If you’ve been following along in the book, you’ve seen several bash commands that provide both parameters and options. Options are single letters preceded by a dash that alter the behavior of a command. This section shows three methods for working with optionsinyourshellscripts. Findingyouroptions Onthesurface,there’snothingallthatspecialaboutcommandlineoptions.Theyappear on the command line immediately after the script name, just the same as command line parameters.Infact,ifyouwant,youcanprocesscommandlineoptionsthesamewayyou processcommandlineparameters. Processingsimpleoptions In the test13.sh script earlier, you saw how to use the shift command to work your way down the command line parameters provided with the script program. You can use thissametechniquetoprocesscommandlineoptions. As you extract each individual parameter, use the case statement (see Chapter 12) to determinewhenaparameterisformattedasanoption: $cattest15.sh #!/bin/bash #extractingcommandlineoptionsasparameters # echo while[-n“$1”] do case“$1”in -a)echo“Foundthe-aoption”;; -b)echo“Foundthe-boption”;; -c)echo“Foundthe-coption”;; *)echo“$1isnotanoption”;; esac shift done $ $./test15.sh-a-b-c-d Foundthe-aoption Foundthe-boption Foundthe-coption -disnotanoption $ The case statement checks each parameter for valid options. When one is found, the appropriatecommandsareruninthecasestatement. This method works, no matter in what order the options are presented on the command line: $./test15.sh-d-c-a -disnotanoption Foundthe-coption Foundthe-aoption $ Thecasestatementprocesseseachoptionasitfindsitinthecommandlineparameters.If anyotherparametersareincludedonthecommandline,youcanincludecommandsinthe catch-allpartofthecasestatementtoprocessthem. Separatingoptionsfromparameters Oftenyou’llrunintosituationswhereyou’llwanttousebothoptionsandparametersfora shell script. The standard way to do this in Linux is to separate the two with a special character code that tells the script when the options are finished and when the normal parametersstart. ForLinux,thisspecialcharacteristhedoubledash(—).Theshellusesthedoubledashto indicate the end of the option list. After seeing the double dash, your script can safely processtheremainingcommandlineparametersasparametersandnotoptions. Tocheckforthedoubledash,simplyaddanotherentryinthecasestatement: $cattest16.sh #!/bin/bash #extractingoptionsandparameters echo while[-n“$1”] do case“$1”in -a)echo“Foundthe-aoption”;; -b)echo“Foundthe-boption”;; -c)echo“Foundthe-coption”;; —)shift break;; *)echo“$1isnotanoption”;; esac shift done # count=1 forparamin$@ do echo“Parameter#$count:$param” count=$[$count+1] done $ Thisscriptusesthebreakcommandtobreakoutofthewhileloopwhenitencountersthe doubledash.Becausewe’rebreakingoutprematurely,weneedtoensurethatwestickin anothershiftcommandtogetthedoubledashoutoftheparametervariables. Forthefirsttest,tryrunningthescriptusinganormalsetofoptionsandparameters: $./test16.sh-c-a-btest1test2test3 Foundthe-coption Foundthe-aoption Foundthe-boption test1isnotanoption test2isnotanoption test3isnotanoption $ The results show that the script assumed that all the command line parameters were optionswhenitprocessedthem.Next,trythesamething,onlythistimeusingthedouble dashtoseparatetheoptionsfromtheparametersonthecommandline: $./test16.sh-c-a-b—test1test2test3 Foundthe-coption Foundthe-aoption Foundthe-boption Parameter#1:test1 Parameter#2:test2 Parameter#3:test3 $ Whenthescriptreachesthedoubledash,itstopsprocessingoptionsandassumesthatany remainingparametersarecommandlineparameters. Processingoptionswithvalues Someoptionsrequireanadditionalparametervalue.Inthesesituations,thecommandline lookssomethinglikethis: $./testing.sh-atest1-b-c-dtest2 Yourscriptmustbeabletodetectwhenyourcommandlineoptionrequiresanadditional parameterandbeabletoprocessitappropriately.Here’sanexampleofhowtodothat: $cattest17.sh #!/bin/bash #extractingcommandlineoptionsandvalues echo while[-n“$1”] do case“$1”in -a)echo“Foundthe-aoption”;; -b)param=”$2” echo“Foundthe-boption,withparametervalue$param” shift;; -c)echo“Foundthe-coption”;; —)shift break;; *)echo“$1isnotanoption”;; esac shift done # count=1 forparamin“$@” do echo“Parameter#$count:$param” count=$[$count+1] done $ $./test17.sh-a-btest1-d Foundthe-aoption Foundthe-boption,withparametervaluetest1 -disnotanoption $ Inthisexample,the casestatementdefinesthreeoptionsthatitprocesses.The -boption alsorequiresanadditionalparametervalue.Becausetheparameterbeingprocessedis $1, youknowthattheadditionalparametervalueislocatedin $2(becausealltheparameters areshiftedaftertheyareprocessed).Justextracttheparametervaluefromthe$2variable. Ofcourse,becauseweusedtwoparameterspotsforthisoption,youalsoneedtosetthe shiftcommandtoshiftoneadditionalposition. Just as with the basic feature, this process works no matter what order you place the options in (just remember to include the appropriate option parameter with the each option): $./test17.sh-btest1-a-d Foundthe-boption,withparametervaluetest1 Foundthe-aoption -disnotanoption $ Nowyouhavethebasicabilitytoprocesscommandlineoptionsinyourshellscripts,but there are limitations. For example, this doesn’t work if you try to combine multiple optionsinoneparameter: $./test17.sh-ac -acisnotanoption $ ItisacommonpracticeinLinuxtocombineoptions,andifyourscriptisgoingtobeuserfriendly, you’ll want to offer this feature for your users as well. Fortunately, there’s anothermethodforprocessingoptionsthatcanhelpyou. Usingthegetoptcommand The getopt command is a great tool to have handy when processing command line optionsandparameters.Itreorganizesthecommandlineparameterstomakeparsingthem inyourscripteasier. Lookingatthecommandformat The getopt command can take a list of command line options and parameters, in any form,andautomaticallyturnthemintotheproperformat.Itusesthefollowingcommand format: getoptoptstringparameters Theoptstringisthekeytotheprocess.Itdefinesthevalidoptionlettersthatcanbeused inthecommandline.Italsodefineswhichoptionlettersrequireaparametervalue. First, list each command line option letter you’re going to use in your script in the optstring.Thenplaceacolonaftereachoptionletterthatrequiresaparametervalue.The getoptcommandparsesthesuppliedparametersbasedontheoptstringyoudefine. Tip Amoreadvancedversionofthegetoptcommand,calledgetopts(noticeitis plural),isavailable.Thegetoptscommandiscoveredlaterinthischapter.Because oftheirnearlyidenticalspelling,it’seasytogetthesetwocommandsconfused.Be careful! Here’sasimpleexampleofhowgetoptworks: $getoptab:cd-a-btest1-cdtest2test3 -a-btest1-c-d—test2test3 $ Theoptstringdefinesfourvalidoptionletters,a,b,c,andd.Acolon(:)isplacedbehind the letter b in order to require option b to have a parameter value. When the getopt commandruns,itexaminestheprovidedparameterlist(-a-btest1-cdtest2test3) andparsesitbasedonthesuppliedoptstring.Noticethatitautomaticallyseparatedthecdoptionsintotwoseparateoptionsandinsertedthedoubledashtoseparatetheadditional parametersontheline. Ifyouspecifyaparameteroptionnotinthe optstring,bydefaultthe getoptcommand producesanerrormessage: $getoptab:cd-a-btest1-cdetest2test3 getopt:invalidoption—e -a-btest1-c-d—test2test3 $ Ifyouprefertojustignoretheerrormessages,usegetoptwiththe-qoption: $getopt-qab:cd-a-btest1-cdetest2test3 -a-b‘test1’-c-d—‘test2’‘test3’ $ Note that the getopt command options must be listed before the optstring. Now you shouldbereadytousethiscommandinyourscriptstoprocesscommandlineoptions. Usinggetoptinyourscripts Youcanusethe getoptcommandinyourscriptstoformatanycommandlineoptionsor parametersenteredforyourscript.It’salittletricky,however,touse. The trick is to replace the existing command line options and parameters with the formattedversionproducedbythe getoptcommand.Thewaytodothatistousethe set command. YousawthesetcommandbackinChapter6.Thesetcommandworkswiththedifferent variablesintheshell. Oneofthe setcommandoptionsisthedoubledash(—).Thedoubledashinstructs setto replace the command line parameter variables with the values on the set command’s commandline. The trick then is to feed the original script command line parameters to the getopt commandandthenfeedtheoutputofthegetoptcommandtothesetcommandtoreplace theoriginalcommandlineparameterswiththenicelyformattedonesfrom getopt.This lookssomethinglikethis: set—$(getopt-qab:cd“$@”) Now the values of the original command line parameter variables are replaced with the outputfromthegetoptcommand,whichformatsthecommandlineparametersforus. Usingthistechnique,wecannowwritescriptsthathandleourcommandlineparameters forus: $cattest18.sh #!/bin/bash #Extractcommandlineoptions&valueswithgetopt # set—$(getopt-qab:cd“$@”) # echo while[-n“$1”] do case“$1”in -a)echo“Foundthe-aoption”;; -b)param=”$2” echo“Foundthe-boption,withparametervalue$param” shift;; -c)echo“Foundthe-coption”;; —)shift break;; *)echo“$1isnotanoption”;; esac shift done # count=1 forparamin“$@” do echo“Parameter#$count:$param” count=$[$count+1] done # $ You’llnoticethisisbasicallythesamescriptasintest17.sh.Theonlythingthatchanged istheadditionofthegetoptcommandtohelpformatourcommandlineparameters. Nowwhenyourunthescriptwithcomplexoptions,thingsworkmuchbetter: $./test18.sh-ac Foundthe-aoption Foundthe-coption $ Andofcourse,alltheoriginalfeaturesworkjustfineaswell: $./test18.sh-a-btest1-cdtest2test3test4 Foundthe-aoption Foundthe-boption,withparametervalue‘test1’ Foundthe-coption Parameter#1:‘test2’ Parameter#2:‘test3’ Parameter#3:‘test4’ $ Nowthingsarelookingprettyfancy.However,there’sstillonesmallbugthatlurksinthe getoptcommand.Checkoutthisexample: $./test18.sh-a-btest1-cd“test2test3”test4 Foundthe-aoption Foundthe-boption,withparametervalue‘test1’ Foundthe-coption Parameter#1:‘test2 Parameter#2:test3’ Parameter#3:‘test4’ $ The getopt command isn’t good at dealing with parameter values with spaces and quotationmarks.Itinterpretedthespaceastheparameterseparator,insteadoffollowing thedoublequotationmarksandcombiningthetwovaluesintooneparameter.Fortunately, thisproblemhasanothersolution. Advancingtogetopts The getoptscommand(noticethatitisplural)isbuiltintothebashshell.Itlooksmuch likeitsgetoptcousin,buthassomeexpandedfeatures. Unlike getopt, which produces one output for all the processed options and parameters foundinthecommandline,the getoptscommandworksontheexistingshellparameter variablessequentially. It processes the parameters it detects in the command line one at a time each time it’s called.Whenitrunsoutofparameters,itexitswithanexitstatusgreaterthanzero.This makesitgreatforusinginloopstoparsealltheparametersonthecommandline. Here’stheformatofthegetoptscommand: getoptsoptstringvariable The optstring value is similar to the one used in the getopt command. Valid option letters are listed in the optstring, along with a colon if the option letter requires a parameter value. To suppress error messages, start the optstring with a colon. The getoptscommandplacesthecurrentparameterinthe variabledefinedinthecommand line. Thegetoptscommandusestwoenvironmentvariables.TheOPTARGenvironmentvariable contains the value to be used if an option requires a parameter value. The OPTIND environment variable contains the value of the current location within the parameter list where getopts left off. This allows you to continue processing other command line parametersafterfinishingtheoptions. Let’slookatasimpleexamplethatusesthegetoptscommand: $cattest19.sh #!/bin/bash #simpledemonstrationofthegetoptscommand # echo whilegetopts:ab:copt do case“$opt”in a)echo“Foundthe-aoption”;; b)echo“Foundthe-boption,withvalue$OPTARG”;; c)echo“Foundthe-coption”;; *)echo“Unknownoption:$opt”;; esac done $ $./test19.sh-abtest1-c Foundthe-aoption Foundthe-boption,withvaluetest1 Foundthe-coption $ The while statement defines the getopts command, specifying what command line optionstolookfor,alongwiththevariablename(opt)tostoretheminforeachiteration. You’ll notice something different about the case statement in this example. When the getoptscommandparsesthecommandlineoptions,itstripsofftheleadingdash,soyou don’tneedleadingdashesinthecasedefinitions. Thegetoptscommandoffersseveralnicefeatures.Forstarters,youcanincludespacesin yourparametervalues: $./test19.sh-b“test1test2”-a Foundthe-boption,withvaluetest1test2 Foundthe-aoption $ Anothernicefeatureisthatyoucanruntheoptionletterandtheparametervaluetogether withoutaspace: $./test19.sh-abtest1 Foundthe-aoption Foundthe-boption,withvaluetest1 $ The getoptscommandcorrectlyparsedthe test1valuefromthe -boption.Inaddition, the getoptscommandbundlesanyundefinedoptionitfindsinthecommandlineintoa singleoutput,thequestionmark: $./test19.sh-d Unknownoption:? $ $./test19.sh-acde Foundthe-aoption Foundthe-coption Unknownoption:? Unknownoption:? $ Any option letter not defined in the optstring value is sent to your code as a question mark. The getoptscommandknowswhentostopprocessingoptionsandleavetheparameters for you to process. As getopts processes each option, it increments the OPTIND environment variable by one. When you’ve reached the end of the getopts processing, youcanusetheOPTINDvaluewiththeshiftcommandtomovetotheparameters: $cattest20.sh #!/bin/bash #Processingoptions¶meterswithgetopts # echo whilegetopts:ab:cdopt do case“$opt”in a)echo“Foundthe-aoption”;; b)echo“Foundthe-boption,withvalue$OPTARG”;; c)echo“Foundthe-coption”;; d)echo“Foundthe-doption”;; *)echo“Unknownoption:$opt”;; esac done # shift$[$OPTIND-1] # echo count=1 forparamin“$@” do echo“Parameter$count:$param” count=$[$count+1] done # $ $./test20.sh-a-btest1-dtest2test3test4 Foundthe-aoption Foundthe-boption,withvaluetest1 Foundthe-doption Parameter1:test2 Parameter2:test3 Parameter3:test4 $ Nowyouhaveafull-featuredcommandlineoptionandparameterprocessingutilityyou canuseinallyourshellscripts! StandardizingOptions When you create your shell script, obviously you’re in control of what happens. It’s completelyuptoyouastowhichletteroptionsyouselecttouseandhowyouselecttouse them. However,afewletteroptionshaveachievedasomewhatstandardmeaningintheLinux world.Ifyouleveragetheseoptionsinyourshellscript,yourscriptswillbemoreuserfriendly. Table14.1showssomeofthecommonmeaningsforcommandlineoptionsusedinLinux. Table14.1CommonLinuxCommandLineOptions Option -a -c -d -e -f -h -i -l -n -o -q -r -s -v -x -y Description Showsallobjects Producesacount Specifiesadirectory Expandsanobject Specifiesafiletoreaddatafrom Displaysahelpmessageforthecommand Ignorestextcase Producesalongformatversionoftheoutput Usesanon-interactive(batch)mode Specifiesanoutputfiletoredirectalloutputto Runsinquietmode Processesdirectoriesandfilesrecursively Runsinsilentmode Producesverboseoutput Excludesanobject Answersyestoallquestions You’ll probably recognize most of these option meanings just from working with the various bash commands throughout the book. Using the same meaning for your options helpsusersinteractwithyourscriptwithouthavingtoworryaboutmanuals. GettingUserInput Althoughprovidingcommandlineoptionsandparametersisagreatwaytogetdatafrom yourscriptusers,sometimesyourscriptneedstobemoreinteractive.Sometimesyouneed to ask a question while the script is running and wait for a response from the person runningyourscript.Thebashshellprovidesthereadcommandjustforthispurpose. Readingbasics The readcommandacceptsinputeitherfromstandardinput(suchasfromthekeyboard) or from another file descriptor. After receiving the input, the read command places the dataintoavariable.Here’sthereadcommandatitssimplest: $cattest21.sh #!/bin/bash #testingthereadcommand # echo-n“Enteryourname:“ readname echo“Hello$name,welcometomyprogram.“ # $ $./test21.sh Enteryourname:RichBlum HelloRichBlum,welcometomyprogram. $ That’sprettysimple.Noticethatthe echocommandthatproducedthepromptusesthe -n option.Thissuppressesthenewlinecharacterattheendofthestring,allowingthescript usertoenterdataimmediatelyafterthestring,insteadofonthenextline.Thisgivesyour scriptsamoreform-likeappearance. Infact,the readcommandincludesthe -poption,whichallowsyoutospecifyaprompt directlyinthereadcommandline: $cattest22.sh #!/bin/bash #testingtheread-poption # read-p“Pleaseenteryourage:”age days=$[$age*365] echo“Thatmakesyouover$daysdaysold!“ # $ $./test22.sh Pleaseenteryourage:10 Thatmakesyouover3650daysold! $ You’ll notice in the first example that when a name was entered, the read command assigned both the first name and last name to the same variable. The read command assigns all data entered at the prompt to a single variable, or you can specify multiple variables.Eachdatavalueenteredisassignedtothenextvariableinthelist.Ifthelistof variablesrunsoutbeforethedatadoes,theremainingdataisassignedtothelastvariable: $cattest23.sh #!/bin/bash #enteringmultiplevariables # read-p“Enteryourname:”firstlast echo“Checkingdatafor$last,$first…” $ $./test23.sh Enteryourname:RichBlum CheckingdataforBlum,Rich… $ You can also specify no variables on the read command line. If you do that, the read commandplacesanydataitreceivesinthespecialenvironmentvariableREPLY: $cattest24.sh #!/bin/bash #TestingtheREPLYEnvironmentvariable # read-p“Enteryourname:“ echo echoHello$REPLY,welcometomyprogram. # $ $./test24.sh Enteryourname:Christine HelloChristine,welcometomyprogram. $ The REPLY environment variable contains all the data entered in the input, and it can be usedintheshellscriptasanyothervariable. Timingout Becarefulwhenusingthereadcommand.Yourscriptmaygetstuckwaitingforthescript usertoenterdata.Ifthescriptmustgoonregardlessofwhetheranydatawasentered,you canusethe-toptiontospecifyatimer.The-toptionspecifiesthenumberofsecondsfor thereadcommandtowaitforinput.Whenthetimerexpires,thereadcommandreturnsa non-zeroexitstatus: $cattest25.sh #!/bin/bash #timingthedataentry # ifread-t5-p“Pleaseenteryourname:”name then echo“Hello$name,welcometomyscript” else echo echo“Sorry,tooslow!“ fi $ $./test25.sh Pleaseenteryourname:Rich HelloRich,welcometomyscript $ $./test25.sh Pleaseenteryourname: Sorry,tooslow! $ Becausethereadcommandexitswithanon-zeroexitstatusifthetimerexpires,it’seasy tousethestandardstructuredstatements,suchasanif-thenstatementorawhileloopto trackwhathappened.Inthisexample,whenthetimerexpires,the ifstatementfails,and theshellexecutesthecommandsintheelsesection. Instead of timing the input, you can also set the read command to count the input characters. When a preset number of characters has been entered, it automatically exits, assigningtheentereddatatothevariable: $cattest26.sh #!/bin/bash #gettingjustonecharacterofinput # read-n1-p“Doyouwanttocontinue[Y/N]?”answer case$answerin Y|y)echo echo“fine,continueon…”;; N|n)echo echoOK,goodbye exit;; esac echo“Thisistheendofthescript” $ $./test26.sh Doyouwanttocontinue[Y/N]?Y fine,continueon… Thisistheendofthescript $ $./test26.sh Doyouwanttocontinue[Y/N]?n OK,goodbye $ This example uses the -n option with the value of 1, instructing the read command to acceptonlyasinglecharacterbeforeexiting.Assoonasyoupressthesinglecharacterto answer,thereadcommandacceptstheinputandpassesittothevariable.Youdon’tneed topresstheEnterkey. Readingwithnodisplay Sometimesyouneedinputfromthescriptuser,butyoudon’twantthatinputtodisplayon themonitor.Theclassicexampleiswhenenteringpasswords,butthereareplentyofother typesofdatathatyouneedtohide. The-soptionpreventsthedataenteredinthereadcommandfrombeingdisplayedonthe monitor;actually,thedataisdisplayed,butthe readcommandsetsthetextcolortothe sameasthebackgroundcolor.Here’sanexampleofusingthe-soptioninascript: $cattest27.sh #!/bin/bash #hidinginputdatafromthemonitor # read-s-p“Enteryourpassword:”pass echo echo“Isyourpasswordreally$pass?“ $ $./test27.sh Enteryourpassword: IsyourpasswordreallyT3st1ng? $ The data typed at the input prompt doesn’t appear on the monitor but is assigned to the variableforuseinthescript. Readingfromafile Finally, you can also use the read command to read data stored in a file on the Linux system.Eachcalltothe readcommandreadsasinglelineoftextfromthefile.Whenno morelinesareleftinthefile,thereadcommandexitswithanon-zeroexitstatus. Thetrickypartisgettingthedatafromthefiletothe readcommand.Themostcommon methodisto pipetheresultofthe catcommandofthefiledirectlytoa whilecommand thatcontainsthereadcommand.Here’sanexample: $cattest28.sh #!/bin/bash #readingdatafromafile # count=1 cattest|whilereadline do echo“Line$count:$line” count=$[$count+1] done echo“Finishedprocessingthefile” $ $cattest Thequickbrowndogjumpsoverthelazyfox. Thisisatest,thisisonlyatest. ORomeo,Romeo!WhereforeartthouRomeo? $ $./test28.sh Line1:Thequickbrowndogjumpsoverthelazyfox. Line2:Thisisatest,thisisonlyatest. Line3:ORomeo,Romeo!WhereforeartthouRomeo? Finishedprocessingthefile $ The whilecommandloopcontinuesprocessinglinesofthefilewiththe readcommand, untilthereadcommandexitswithanon-zeroexitstatus. Summary Thischaptershowedthreemethodsforretrievingdatafromthescriptuser.Commandline parameters allow users to enter data directly on the command line when they run the script.Thescriptusespositionalparameterstoretrievethecommandlineparametersand assignthemtovariables. The shiftcommandallowsyoutomanipulatethecommandlineparametersbyrotating themwithinthepositionalparameters.Thiscommandallowsyoutoeasilyiteratethrough theparameterswithoutknowinghowmanyparametersareavailable. You can use three special variables when working with command line parameters. The shellsetsthe $#variabletothenumberofparametersenteredonthecommandline.The $*variablecontainsalltheparametersasasinglestring,andthe $@variablecontainsall the parameters as separate words. These variables come in handy when you’re trying to processlongparameterlists. Besidesparameters,yourscriptuserscanusecommandlineoptionstopassinformationto yourscript.Commandlineoptionsaresinglelettersprecededbyadash.Differentoptions canbeassignedtoalterthebehaviorofyourscript. Thebashshellprovidesthreewaystohandlecommandlineoptions. Thefirstwayistohandlethemjustlikecommandlineparameters.Youcaniteratethrough theoptionsusingthepositionalparametervariables,processingeachoptionasitappears onthecommandline. Another way to handle command line options is with the getopt command. This commandconvertscommandlineoptionsandparametersintoastandardformatthatyou can process in your script. The getopt command allows you to specify which letters it recognizes as options and which options require an additional parameter value. The getopt command processes the standard command line parameters and outputs the optionsandparametersintheproperorder. Thefinalmethodforhandlingcommandlineoptionsisviathe getoptscommand(note that it’s plural). The getopts command provides more advanced processing of the command line parameters. It allows for multi-value parameters, along with identifying optionsnotdefinedbythescript. An interactive method to obtain data from your script users is the read command. The read command allows your scripts to query users for information and wait. The read commandplacesanydataenteredbythescriptuserintooneormorevariables,whichyou canusewithinthescript. Severaloptionsareavailableforthe readcommandthatallowyoutocustomizethedata input into your script, such as using hidden data entry, applying timed data entry, and requestingaspecificnumberofinputcharacters. Inthenextchapter,welookfurtherintohowbashshellscriptsoutputdata.Sofar,you’ve seenhowtodisplaydataonthemonitorandredirectittoafile.Next,weexploreafew otheroptionsthatyouhaveavailablenotonlytodirectdatatospecificlocationsbutalso todirectspecifictypesofdatatospecificlocations.Thiswillhelpmakeyourshellscripts lookprofessional! Chapter15 PresentingData InThisChapter 1. Revisitingredirection 2. Standardinputandoutput 3. Reportingerrors 4. Throwingawaydata 5. Creatinglogfiles Sofarthescriptsshowninthisbookdisplayinformationeitherbyechoingdatatothe monitororbyredirectingdatatoafile.Chapter11demonstratedhowtoredirectthe outputofacommandtoafile.Thischapterexpandsonthattopicbyshowingyou howyoucanredirecttheoutputofyourscripttodifferentlocationsonyourLinux system. UnderstandingInputandOutput Sofar,you’veseentwomethodsfordisplayingtheoutputfromyourscripts: Displayingoutputonthemonitorscreen Redirectingoutputtoafile Both methods produced an all-or-nothing approach to data output. There are times, however,whenitwouldbenicetodisplaysomedataonthemonitorandotherdataina file.Fortheseinstances,itcomesinhandytoknowhowLinuxhandlesinputandoutput soyoucangetyourscriptoutputtotherightplace. ThefollowingsectionsdescribehowtousethestandardLinuxinputandoutputsystemto youradvantage,tohelpdirectscriptoutputtospecificlocations. Standardfiledescriptors The Linux system handles every object as a file. This includes the input and output process. Linux identifies each file object using a filedescriptor. The file descriptor is a non-negative integer that uniquely identifies open files in a session. Each process is allowedtohaveuptonineopenfiledescriptorsatatime.Thebashshellreservesthefirst threefiledescriptors(0,1,and2)forspecialpurposes.TheseareshowninTable15.1. Table15.1LinuxStandardFileDescriptors FileDescriptor Abbreviation Description STDIN 0 Standardinput STDOUT 1 Standardoutput STDERR 2 Standarderror Thesethreespecialfiledescriptorshandletheinputandoutputfromyourscript.Theshell uses them to direct the default input and output in the shell to the appropriate location, which by default is usually your monitor. The following sections describe each of these standardfiledescriptorsingreaterdetail. STDIN The STDIN file descriptor references the standard input to the shell. For a terminal interface,thestandardinputisthekeyboard.Theshellreceivesinputfromthekeyboard ontheSTDINfiledescriptorandprocesseseachcharacterasyoutypeit. When you use the input redirect symbol (<), Linux replaces the standard input file descriptor with the file referenced by the redirection. It reads the file and retrieves data justasifitweretypedonthekeyboard. Manybashcommandsacceptinputfrom STDIN,especiallyifnofilesarespecifiedonthe command line. Here’s an example of using the cat command with data entered from STDIN: $cat thisisatest thisisatest thisisasecondtest. thisisasecondtest. When you enter the cat command on the command line by itself, it accepts input from STDIN.Asyouentereachline,thecatcommandechoesthelinetothedisplay. However,youcanalsousethe STDINredirectsymboltoforcethe catcommandtoaccept inputfromanotherfileotherthanSTDIN: $cat<testfile Thisisthefirstline. Thisisthesecondline. Thisisthethirdline. $ Nowthe catcommandusesthelinesthatarecontainedinthe testfilefileastheinput. You can use this technique to input data to any shell command that accepts data from STDIN. STDOUT The STDOUT file descriptor references the standard output for the shell. On a terminal interface,thestandardoutputistheterminalmonitor.Alloutputfromtheshell(including programsandscriptsyourunintheshell)isdirectedtothestandardoutput,whichisthe monitor. MostbashcommandsdirecttheiroutputtotheSTDOUTfiledescriptorbydefault.Asshown inChapter11,youcanchangethatusingoutputredirection: $ls-l>test2 $cattest2 total20 -rw-rw-r—1richrich532014-10-1611:30test -rw-rw-r—1richrich02014-10-1611:32test2 -rw-rw-r—1richrich732014-10-1611:23testfile $ Withtheoutputredirectionsymbol,alltheoutputthatnormallywouldgotothemonitoris insteadredirectedtothedesignatedredirectionfilebytheshell. Youcanalsoappenddatatoafile.Youdothisusingthe>>symbol: $who>>test2 $cattest2 total20 -rw-rw-r—1richrich532014-10-1611:30test -rw-rw-r—1richrich02014-10-1611:32test2 -rw-rw-r—1richrich732014-10-1611:23testfile richpts/02014-10-1715:34(192.168.1.2) $ Theoutputgeneratedbythe whocommandisappendedtothedataalreadyinthe test2 file. However, if you use the standard output redirection for your scripts, you can run into a problem.Here’sanexampleofwhatcanhappeninyourscript: $ls-albadfile>test3 ls:cannotaccessbadfile:Nosuchfileordirectory $cattest3 $ Whenacommandproducesanerrormessage,theshelldoesn’tredirecttheerrormessage to the output redirection file. The shell created the output redirection file, but the error message appeared on the monitor screen. Notice that there isn’t an error when trying to displaythecontentsofthetest3file.Thetest3filewascreatedjustfine,butit’sempty. Theshellhandleserrormessagesseparatelyfromthenormaloutput.Ifyou’recreatinga shell script that runs in background mode, often you must rely on the output messages being sent to a log file. Using this technique, if any error messages occur, they don’t appearinthelogfile.Youneedtodosomethingdifferent. STDERR Theshellhandleserrormessagesusingthespecial STDERRfiledescriptor.The STDERRfile descriptorreferencesthestandarderroroutputfortheshell.Thisisthelocationwherethe shellsendserrormessagesgeneratedbytheshellorprogramsandscriptsrunninginthe shell. Bydefault,theSTDERRfiledescriptorpointstothesameplaceastheSTDOUTfiledescriptor (even though they are assigned different file descriptor values). This means that, by default,allerrormessagesgotothemonitordisplay. However,asyousawintheexample,whenyouredirectSTDOUT,thisdoesn’tautomatically redirect STDERR. When working with scripts, you’ll often want to change that behavior, especiallyifyou’reinterestedinloggingerrormessagestoalogfile. Redirectingerrors You’ve already seen how to redirect the STDOUT data by using the redirection symbol. Redirectingthe STDERRdataisn’tmuchdifferent;youjustneedtodefinethe STDERRfile descriptorwhenyouusetheredirectionsymbol.Youcandothisinacoupleofways. Redirectingerrorsonly AsyousawinTable15.1,theSTDERRfiledescriptorissettothevalue2.Youcanselectto redirectonlyerrormessagesbyplacingthisfiledescriptorvalueimmediatelybeforethe redirectionsymbol.Thevaluemustappearimmediatelybeforetheredirectionsymbolorit doesn’twork: $ls-albadfile2>test4 $cattest4 ls:cannotaccessbadfile:Nosuchfileordirectory $ Now when you run the command, the error message doesn’t appear on the monitor. Instead,theoutputfilecontainsanyerrormessagesthataregeneratedbythecommand. Usingthismethod,theshellredirectstheerrormessagesonly,notthenormaldata.Here’s anotherexampleofmixingSTDOUTandSTDERRmessagesinthesameoutput: $ls-altestbadtesttest22>test5 -rw-rw-r—1richrich1582014-10-1611:32test2 $cattest5 ls:cannotaccesstest:Nosuchfileordirectory ls:cannotaccessbadtest:Nosuchfileordirectory $ The normal STDOUT output from the ls command still goes to the default STDOUT file descriptor,whichisthemonitor.Becausethecommandredirectsfiledescriptor2output (STDERR) to an output file, the shell sends any error messages generated directly to the specifiedredirectionfile. Redirectingerrorsanddata Ifyouwanttoredirectbotherrorsandthenormaloutput,youneedtousetworedirection symbols. You need to precede each with the appropriate file descriptor for the data you want to redirect and then have them point to the appropriate output file for holding the data: $ls-altesttest2test3badtest2>test61>test7 $cattest6 ls:cannotaccesstest:Nosuchfileordirectory ls:cannotaccessbadtest:Nosuchfileordirectory $cattest7 -rw-rw-r—1richrich1582014-10-1611:32test2 -rw-rw-r—1richrich02014-10-1611:33test3 $ Theshellredirectsthenormaloutputofthe lscommandthatwouldhavegoneto STDOUT tothetest7fileusingthe1>symbol.AnyerrormessagesthatwouldhavegonetoSTDERR wereredirectedtothetest6fileusingthe2>symbol. Youcanusethistechniquetoseparatenormalscriptoutputfromanyerrormessagesthat occur in the script. This allows you to easily identify errors without having to wade throughthousandsoflinesofnormaloutputdata. Alternatively, if you want, you can redirect both STDERRand STDOUT output to the same outputfile.Thebashshellprovidesaspecialredirectionsymboljustforthispurpose,the &>symbol: $ls-altesttest2test3badtest&>test7 $cattest7 ls:cannotaccesstest:Nosuchfileordirectory ls:cannotaccessbadtest:Nosuchfileordirectory -rw-rw-r—1richrich1582014-10-1611:32test2 -rw-rw-r—1richrich02014-10-1611:33test3 $ Whenyouusethe&>symbol,alltheoutputgeneratedbythecommandissenttothesame location,bothdataanderrors.Noticethatoneoftheerrormessagesisoutoforderfrom what you’d expect. The error message for the badtest file (the last file to be listed) appears second in the output file. The bash shell automatically gives error messages a higher priority than the standard output. This allows you to view the error messages together,ratherthanscatteredthroughouttheoutputfile. RedirectingOutputinScripts Youcanusethe STDOUTand STDERR file descriptors in your scripts to produce output in multiple locations simply by redirecting the appropriate file descriptors. There are two methodsforredirectingoutputinthescript: Temporarilyredirectingeachline Permanentlyredirectingallcommandsinthescript Thefollowingsectionsdescribehoweachofthesemethodsworks. Temporaryredirections If you want to purposely generate error messages in your script, you can redirect an individual output line to STDERR. You just need to use the output redirection symbol to redirecttheoutputtotheSTDERRfiledescriptor.Whenyouredirecttoafiledescriptor,you mustprecedethefiledescriptornumberwithanampersand(&): echo“Thisisanerrormessage”>&2 This line displays the text wherever the STDERR file descriptor for the script is pointing, instead of the normal STDOUT. The following is an example of a script that uses this feature: $cattest8 #!/bin/bash #testingSTDERRmessages echo“Thisisanerror”>&2 echo“Thisisnormaloutput” $ Ifyourunthescriptasnormal,youdon’tnoticeanydifference: $./test8 Thisisanerror Thisisnormaloutput $ Rememberthat,bydefault,Linuxdirectsthe STDERRoutputto STDOUT.However,ifyou redirect STDERR when running the script, any text directed to STDERR in the script is redirected: $./test82>test9 Thisisnormaloutput $cattest9 Thisisanerror $ Perfect!ThetextdisplayedusingSTDOUTappearsonthemonitor,whiletheechostatement textsenttoSTDERRisredirectedtotheoutputfile. Thismethodisgreatforgeneratingerrormessagesinyourscripts.Ifsomeoneusesyour scripts, they can easily redirect the error messages using the STDERR file descriptor, as shown. Permanentredirections Ifyouhavelotsofdatathatyou’reredirectinginyourscript,itcangettedioushavingto redirect every echo statement. Instead, you can tell the shell to redirect a specific file descriptorforthedurationofthescriptbyusingtheexeccommand: $cattest10 #!/bin/bash #redirectingalloutputtoafile exec1>testout echo“Thisisatestofredirectingalloutput” echo“fromascripttoanotherfile.” echo“withouthavingtoredirecteveryindividualline” $./test10 $cattestout Thisisatestofredirectingalloutput fromascripttoanotherfile. withouthavingtoredirecteveryindividualline $ TheexeccommandstartsanewshellandredirectstheSTDOUTfiledescriptortoafile.All outputinthescriptthatgoestoSTDOUTisinsteadredirectedtothefile. YoucanalsoredirecttheSTDOUTinthemiddleofascript: $cattest11 #!/bin/bash #redirectingoutputtodifferentlocations exec2>testerror echo“Thisisthestartofthescript” echo“nowredirectingalloutputtoanotherlocation” exec1>testout echo“Thisoutputshouldgotothetestoutfile” echo“butthisshouldgotothetesterrorfile”>&2 $ $./test11 Thisisthestartofthescript nowredirectingalloutputtoanotherlocation $cattestout Thisoutputshouldgotothetestoutfile $cattesterror butthisshouldgotothetesterrorfile $ The script uses the exec command to redirect any output going to STDERR to the file testerror.Next,thescriptusestheechostatementtodisplayafewlinestoSTDOUT.After that,the execcommandisusedagaintoredirect STDOUTtothe testoutfile.Noticethat evenwhenSTDOUTisredirected,youcanstillspecifytheoutputfromanechostatementto gotoSTDERR,whichinthiscaseisstillredirectedtothetesterrorfile. This feature can come in handy when you want to redirect the output of just parts of a scripttoanalternativelocation,suchasanerrorlog.There’sjustoneproblemyouruninto whenusingthis. Afteryouredirect STDOUTor STDERR,youcan’teasilyredirectthembacktotheiroriginal location.Ifyouneedtoswitchbackandforthwithyourredirection,youneedtolearna trick. The “Creating Your Own Redirection” section later in this chapter discusses this trickandhowtouseitinyourshellscripts. RedirectingInputinScripts You can use the same technique used to redirect STDOUT and STDERR in your scripts to redirectSTDINfromthekeyboard.TheexeccommandallowsyoutoredirectSTDINfroma fileontheLinuxsystem: exec0<testfile This command informs the shell that it should retrieve input from the file testfile instead of STDIN. This redirection applies anytime the script requests input. Here’s an exampleofthisinaction: $cattest12 #!/bin/bash #redirectingfileinput exec0<testfile count=1 whilereadline do echo“Line#$count:$line” count=$[$count+1] done $./test12 Line#1:Thisisthefirstline. Line#2:Thisisthesecondline. Line#3:Thisisthethirdline. $ Chapter 14 showed you how to use the read command to read data entered from the keyboardbyauser.ByredirectingSTDINfromafile,whenthereadcommandattemptsto readfromSTDIN,itretrievesdatafromthefileinsteadofthekeyboard. This is an excellent technique to read data in files for processing in your scripts. A commontaskforLinuxsystemadministratorsistoreaddatafromlogfilesforprocessing. Thisistheeasiestwaytoaccomplishthattask. CreatingYourOwnRedirection Whenyouredirectinputandoutputinyourscript,you’renotlimitedtothethreedefault file descriptors. I mentioned that you could have up to nine open file descriptors in the shell.Theothersixfiledescriptorsarenumberedfrom3through8andareavailablefor youtouseaseitherinputoroutputredirection.Youcanassignanyofthesefiledescriptors toafileandthenusetheminyourscriptsaswell.Thissectionshowsyouhowtousethe otherfiledescriptorsinyourscripts. Creatingoutputfiledescriptors Youassignafiledescriptorforoutputbyusingtheexeccommand.Aswiththestandard file descriptors, after you assign an alternative file descriptor to a file location, that redirection stays permanent until you reassign it. Here’s a simple example of using an alternativefiledescriptorinascript: $cattest13 #!/bin/bash #usinganalternativefiledescriptor exec3>test13out echo“Thisshoulddisplayonthemonitor” echo“andthisshouldbestoredinthefile”>&3 echo“Thenthisshouldbebackonthemonitor” $./test13 Thisshoulddisplayonthemonitor Thenthisshouldbebackonthemonitor $cattest13out andthisshouldbestoredinthefile $ The script uses the exec command to redirect file descriptor 3 to an alternative file location. When the script executes the echo statements, they display on STDOUT as you wouldexpect.However,theechostatementsthatyouredirecttofiledescriptor3gotothe alternativefile.Thisallowsyoutokeepnormaloutputforthemonitorandredirectspecial informationtofiles,suchaslogfiles. Youcanalsousetheexeccommandtoappenddatatoanexistingfileinsteadofcreatinga newfile: exec3>>test13out Nowtheoutputisappendedtothetest13outfileinsteadofcreatinganewfile. Redirectingfiledescriptors Here’s the trick to help you bring back a redirected file descriptor. You can assign an alternativefiledescriptortoastandardfiledescriptor,andviceversa.Thismeansthatyou can redirect the original location of STDOUT to an alternative file descriptor and then redirectthatfiledescriptorbackto STDOUT.Thismightsoundsomewhatcomplicated,but inpracticeit’sfairlystraightforward.Thisexamplewillclearthingsupforyou: $cattest14 #!/bin/bash #storingSTDOUT,thencomingbacktoit exec3>&1 exec1>test14out echo“Thisshouldstoreintheoutputfile” echo“alongwiththisline.” exec1>&3 echo“Nowthingsshouldbebacktonormal” $ $./test14 Nowthingsshouldbebacktonormal $cattest14out Thisshouldstoreintheoutputfile alongwiththisline. $ This example is a little crazy so let’s walk through it piece by piece. First, the script redirectsfiledescriptor3tothecurrentlocationoffiledescriptor1,whichisSTDOUT.This meansthatanyoutputsenttofiledescriptor3goestothemonitor. Thesecond execcommandredirects STDOUTtoafile.Theshellnowredirectsanyoutput sent to STDOUT directly to the output file. However, file descriptor 3 still points to the originallocationofSTDOUT,whichisthemonitor.Ifyousendoutputdatatofiledescriptor 3atthispoint,itstillgoestothemonitor,eventhoughSTDOUTisredirected. After sending some output to STDOUT, which points to a file, the script then redirects STDOUT to the current location of file descriptor 3, which is still set to the monitor. This meansthatnowSTDOUTpointstoitsoriginallocation,themonitor. Thismethodcangetconfusing,butit’sacommonwaytotemporarilyredirectoutputin scriptfilesandthensettheoutputbacktothenormalsettings. Creatinginputfiledescriptors You can redirect input file descriptors exactly the same way as output file descriptors. Savethe STDINfiledescriptorlocationtoanotherfiledescriptorbeforeredirectingittoa file;whenyou’refinishedreadingthefile,youcanrestoreSTDINtoitsoriginallocation: $cattest15 #!/bin/bash #redirectinginputfiledescriptors exec6<&0 exec0<testfile count=1 whilereadline do echo“Line#$count:$line” count=$[$count+1] done exec0<&6 read-p“Areyoudonenow?”answer case$answerin Y|y)echo“Goodbye”;; N|n)echo“Sorry,thisistheend.”;; esac $./test15 Line#1:Thisisthefirstline. Line#2:Thisisthesecondline. Line#3:Thisisthethirdline. Areyoudonenow?y Goodbye $ In this example, file descriptor 6 is used to hold the location for STDIN. The script then redirects STDINtoafile.Alltheinputforthe readcommandcomesfromtheredirected STDIN,whichisnowtheinputfile. When all the lines have been read, the script returns STDIN to its original location by redirectingittofiledescriptor6.ThescriptteststomakesurethatSTDINisbacktonormal byusinganotherreadcommand,whichthistimewaitsforinputfromthekeyboard. Creatingaread/writefiledescriptor As odd as it may seem, you can also open a single file descriptor for both input and output.Youcanthenusethesamefiledescriptortobothreaddatafromafileandwrite datatothesamefile. Youneedtobeespeciallycarefulwiththismethod,however.Asyoureadandwritedata toandfromafile,theshellmaintainsaninternalpointer,indicatingwhereitisinthefile. Anyreadingorwritingoccurswherethefilepointerlastleftoff.Thiscanproducesome interestingresultsifyou’renotcareful.Lookatthisexample: $cattest16 #!/bin/bash #testinginput/outputfiledescriptor exec3<>testfile readline<&3 echo“Read:$line” echo“Thisisatestline”>&3 $cattestfile Thisisthefirstline. Thisisthesecondline. Thisisthethirdline. $./test16 Read:Thisisthefirstline. $cattestfile Thisisthefirstline. Thisisatestline ine. Thisisthethirdline. $ Thisexampleusestheexeccommandtoassignfiledescriptor3forbothinputandoutput senttoandfromthefiletestfile.Next,itusesthereadcommandtoreadthefirstlinein the file, using the assigned file descriptor, and then it displays the read line of data in STDOUT.Afterthat,itusestheechostatementtowritealineofdatatothefileopenedwith thesamefiledescriptor. Whenyourunthescript,atfirstthingslookjustfine.Theoutputshowsthatthescriptread thefirstlineinthe testfilefile.However,ifyoudisplaythecontentsofthe testfile fileafterrunningthescript,youseethatthedatawrittentothefileoverwrotetheexisting data. Whenthescriptwritesdatatothefile,itstartswherethefilepointerislocated.The read commandreadsthefirstlineofdata,soitleftthefilepointerpointingtothefirstcharacter inthesecondlineofdata.Whenthe echostatementoutputsdatatothefile,itplacesthe dataatthecurrentlocationofthefilepointer,overwritingwhateverdatawasthere. Closingfiledescriptors Ifyoucreatenewinputoroutputfiledescriptors,theshellautomaticallyclosesthemwhen the script exits. There are situations, however, when you need to manually close a file descriptorbeforetheendofthescript. Tocloseafiledescriptor,redirectittothespecialsymbol&-.Thisishowthislooksinthe script: exec3>&- This statement closes file descriptor 3, preventing it from being used any more in the script.Here’sanexampleofwhathappenswhenyoutrytouseaclosedfiledescriptor: $catbadtest #!/bin/bash #testingclosingfiledescriptors exec3>test17file echo“Thisisatestlineofdata”>&3 exec3>&echo“Thiswon’twork”>&3 $./badtest ./badtest:3:Badfiledescriptor $ Afteryouclosethefiledescriptor,youcan’twriteanydatatoitinyourscriptortheshell producesanerrormessage. There’s yet another thing to be careful of when closing file descriptors. If you open the sameoutputfilelateroninyourscript,theshellreplacestheexistingfilewithanewfile. This means that if you output any data, it overwrites the existing file. Consider the followingexampleofthisproblem: $cattest17 #!/bin/bash #testingclosingfiledescriptors exec3>test17file echo“Thisisatestlineofdata”>&3 exec3>&cattest17file exec3>test17file echo“This’llbebad”>&3 $./test17 Thisisatestlineofdata $cattest17file This’llbebad $ Aftersendingadatastringtothetest17filefileandclosingthefiledescriptor,thescript usesthecatcommandtodisplaythecontentsofthefile.Sofar,sogood.Next,thescript reopenstheoutputfileandsendsanotherdatastringtoit.Whenyoudisplaythecontents of the output file, all you see is the second data string. The shell overwrote the original outputfile. ListingOpenFileDescriptors With only nine file descriptors available to you, you’d think that it wouldn’t be hard to keepthingsstraight.Sometimes,however,it’seasytogetlostwhentryingtokeeptrackof which file descriptor is redirected where. To help you keep your sanity, the bash shell providesthelsofcommand. The lsofcommandlistsalltheopenfiledescriptorsontheentireLinuxsystem.Thisis somewhatofacontroversialfeature,becauseitcanprovideinformationabouttheLinux systemtonon-system-administrators.That’swhymanyLinuxsystemshidethiscommand sousersdon’taccidentallystumbleacrossit. OnmanyLinuxsystems(suchasFedora)the lsofcommandislocatedinthe /usr/sbin directory.Torunitwithanormaluseraccount,Ihavetoreferenceitbyitsfullpathname: $/usr/sbin/lsof This produces an amazing amount of output. It displays information about every file currently open on the Linux system. This includes all the processes running on background,aswellasanyuseraccountsloggedintothesystem. Plenty of command line parameters and options are available to help filter out the lsof output.Themostcommonlyusedare -p,whichallowsyoutospecifyaprocessID(PID), and-d,whichallowsyoutospecifythefiledescriptornumberstodisplay. ToeasilydeterminethecurrentPIDoftheprocess,youcanusethespecialenvironment variable $$, which the shell sets to the current PID. The -a option is used to perform a BooleanANDoftheresultsoftheothertwooptions,toproducethefollowing: $/usr/sbin/lsof-a-p$$-d0,1,2 COMMANDPIDUSERFDTYPEDEVICESIZENODENAME bash3344rich0uCHR136,02/dev/pts/0 bash3344rich1uCHR136,02/dev/pts/0 bash3344rich2uCHR136,02/dev/pts/0 $ Thisshowsthedefaultfiledescriptors(0,1,and2)forthecurrentprocess(thebashshell). The default output of lsof contains several columns of information, described in Table 15.2. Table15.2DefaultlsofOutput Column COMMAND PID USER FD TYPE Description Thefirstninecharactersofthenameofthecommandintheprocess TheprocessIDoftheprocess Theloginnameoftheuserwhoownstheprocess Thefiledescriptornumberandaccesstype[r—(read),w—(write),u— (read/write)] Thetypeoffile[CHR—(character),BLK—(block),DIR—(directory), REG—(regularfile)] DEVICE SIZE NODE NAME Thedevicenumbers(majorandminor)ofthedevice Ifavailable,thesizeofthefile Thenodenumberofthelocalfile Thenameofthefile Thefiletypeassociatedwith STDIN, STDOUT,and STDERRischaractermode.Becausethe STDIN,STDOUT,andSTDERRfiledescriptorsallpointtotheterminal,thenameoftheoutput file is the device name of the terminal. All three standard files are available for both readingandwriting(althoughitdoesseemoddtobeabletowritetoSTDINandreadfrom STDOUT). Now, let’s look at the results of the lsof command from inside a script that’s opened a coupleofalternativefiledescriptors: $cattest18 #!/bin/bash #testinglsofwithfiledescriptors exec3>test18file1 exec6>test18file2 exec7<testfile /usr/sbin/lsof-a-p$$-d0,1,2,3,6,7 $./test18 COMMANDPIDUSERFDTYPEDEVICESIZENODENAME test183594rich0uCHR136,02/dev/pts/0 test183594rich1uCHR136,02/dev/pts/0 test183594rich2uCHR136,02/dev/pts/0 183594rich3wREG253,00360712/home/rich/test18file1 183594rich6wREG253,00360715/home/rich/test18file2 183594rich7rREG253,073360717/home/rich/testfile $ Thescriptcreatesthreealternativefiledescriptors,twoforoutput(3and6)andonefor input(7).Whenthescriptrunsthelsofcommand,youcanseethenewfiledescriptorsin the output. We truncated the first part of the output so you could see the results of the filename. The filename shows the complete pathname for the files used in the file descriptors. It shows each of the files as type REG, which indicates that they are regular filesonthefilesystem. SuppressingCommandOutput Sometimes,youmaynotwanttodisplayanyoutputfromyourscript.Thisoftenoccursif you’rerunningascriptasabackgroundprocess(seeChapter16).Ifanyerrormessages occurfromthescriptwhileit’srunninginthebackground,theshelle-mailsthemtothe owner of the process. This can get tedious, especially if you run scripts that generate minornuisanceerrors. To solve that problem, you can redirect STDERR to a special file called the nullfile.The nullfileisprettymuchwhatitsaysitis—afilethatcontainsnothing.Anydatathatthe shelloutputstothenullfileisnotsaved,thusthedataarelost. The standard location for the null file on Linux systems is /dev/null. Any data you redirecttothatlocationisthrownawayanddoesn’tappear: $ls-al>/dev/null $cat/dev/null $ Thisisacommonwaytosuppressanyerrormessageswithoutactuallysavingthem: $ls-albadfiletest162>/dev/null -rwxr—r—1richrich135Oct2919:57test16* $ You can also use the /dev/null file for input redirection as an input file. Because the /dev/nullfilecontainsnothing,itisoftenusedbyprogrammerstoquicklyremovedata fromanexistingfilewithouthavingtoremovethefileandre-createit: $cattestfile Thisisthefirstline. Thisisthesecondline. Thisisthethirdline. $cat/dev/null>testfile $cattestfile $ Thefiletestfilestillexistsonthesystem,butnowitisempty.Thisisacommonmethod usedtoclearoutlogfilesthatmustremaininplaceforapplicationstooperate. UsingTemporaryFiles TheLinuxsystemcontainsaspecialdirectorylocationreservedfortemporaryfiles.Linux uses the /tmp directory for files that don’t need to be kept indefinitely. Most Linux distributionsconfigurethesystemtoautomaticallyremoveanyfilesinthe /tmpdirectory atbootup. Anyuseraccountonthesystemhasprivilegestoreadandwritefilesinthe/tmpdirectory. This feature provides an easy way for you to create temporary files that you don’t necessarilyhavetoworryaboutcleaningup. There’s even a specific command to use for creating a temporary file. The mktemp commandallowsyoutoeasilycreateauniquetemporaryfileinthe /tmpfolder.Theshell createsthefilebutdoesn’tuseyourdefaultumaskvalue(seeChapter7).Instead,itonly assignsreadandwritepermissionstothefile’sownerandmakesyoutheownerofthefile. Afteryoucreatethefile,youhavefullaccesstoreadandwritetoandfromitfromyour script,butnooneelsecanaccessit(otherthantherootuser,ofcourse). Creatingalocaltemporaryfile Bydefault,mktempcreatesafileinthelocaldirectory.Tocreateatemporaryfileinalocal directory with the mktemp command, you just need to specify a filename template. The templateconsistsofanytextfilename,plussixX’sappendedtotheendofthefilename: $mktemptesting.XXXXXX $ls-altesting* -rw––-1richrich0Oct1721:30testing.UfIi13 $ ThemktempcommandreplacesthesixX’swithasix-charactercodetoensurethefilename isuniqueinthedirectory.Youcancreatemultipletemporaryfilesandbeassuredthateach oneisunique: $mktemptesting.XXXXXX testing.1DRLuV $mktemptesting.XXXXXX testing.lVBtkW $mktemptesting.XXXXXX testing.PgqNKG $ls-ltesting* -rw––-1richrich0Oct1721:57testing.1DRLuV -rw––-1richrich0Oct1721:57testing.PgqNKG -rw––-1richrich0Oct1721:30testing.UfIi13 -rw––-1richrich0Oct1721:57testing.lVBtkW $ Asyoucansee,theoutputofthe mktempcommandisthenameofthefilethatitcreates. When you use the mktemp command in a script, you’ll want to save that filename in a variable,soyoucanrefertoitlateroninthescript: $cattest19 #!/bin/bash #creatingandusingatempfile tempfile=$(mktemptest19.XXXXXX) exec3>$tempfile echo“Thisscriptwritestotempfile$tempfile” echo“Thisisthefirstline”>&3 echo“Thisisthesecondline.”>&3 echo“Thisisthelastline.”>&3 exec3>&echo“Donecreatingtempfile.Thecontentsare:” cat$tempfile rm-f$tempfile2>/dev/null $./test19 Thisscriptwritestotempfiletest19.vCHoya Donecreatingtempfile.Thecontentsare: Thisisthefirstline Thisisthesecondline. Thisisthelastline. $ls-altest19* -rwxr—r—1richrich356Oct2922:03test19* $ Thescriptusesthemktempcommandtocreateatemporaryfileandassignsthefilenameto the$tempfilevariable.Itthenusesthetemporaryfileastheoutputredirectionfileforfile descriptor3.Afterdisplayingthetemporaryfilenameon STDOUT,itwritesafewlinesto thetemporaryfile,andthenitclosesthefiledescriptor.Finally,itdisplaysthecontentsof thetemporaryfileandthenusesthermcommandtoremoveit. Creatingatemporaryfilein/tmp The -toptionforces mktemp to create the file in the temporary directory of the system. Whenyouusethisfeature,the mktempcommandreturnsthefullpathnameusedtocreate thetemporaryfile,notjustthefilename: $mktemp-ttest.XXXXXX /tmp/test.xG3374 $ls-al/tmp/test* -rw––-1richrich02014-10-2918:41/tmp/test.xG3374 $ Because the mktemp command returns the full pathname, you can then reference the temporary file from any directory on the Linux system, no matter where it places the temporarydirectory: $cattest20 #!/bin/bash #creatingatempfilein/tmp tempfile=$(mktemp-ttmp.XXXXXX) echo“Thisisatestfile.”>$tempfile echo“Thisisthesecondlineofthetest.”>>$tempfile echo“Thetempfileislocatedat:$tempfile” cat$tempfile rm-f$tempfile $./test20 Thetempfileislocatedat:/tmp/tmp.Ma3390 Thisisatestfile. Thisisthesecondlineofthetest. $ When mktempcreatesthetemporaryfile,itreturnsthefullpathnametotheenvironment variable.Youcanthenusethatvalueinanycommandtoreferencethetemporaryfile. Creatingatemporarydirectory The -doptiontellsthe mktempcommandtocreateatemporarydirectoryinsteadofafile. You can then use that directory for whatever purposes you need, such as creating additionaltemporaryfiles: $cattest21 #!/bin/bash #usingatemporarydirectory tempdir=$(mktemp-ddir.XXXXXX) cd$tempdir tempfile1=$(mktemptemp.XXXXXX) tempfile2=$(mktemptemp.XXXXXX) exec7>$tempfile1 exec8>$tempfile2 echo“Sendingdatatodirectory$tempdir” echo“Thisisatestlineofdatafor$tempfile1”>&7 echo“Thisisatestlineofdatafor$tempfile2”>&8 $./test21 Sendingdatatodirectorydir.ouT8S8 $ls-al total72 drwxr-xr-x3richrich4096Oct1722:20./ drwxr-xr-x9richrich4096Oct1709:44../ drwx––2richrich4096Oct1722:20dir.ouT8S8/ -rwxr—r—1richrich338Oct1722:20test21* $cddir.ouT8S8 [dir.ouT8S8]$ls-al total16 drwx––2richrich4096Oct1722:20./ drwxr-xr-x3richrich4096Oct1722:20../ -rw––-1richrich44Oct1722:20temp.N5F3O6 -rw––-1richrich44Oct1722:20temp.SQslb7 [dir.ouT8S8]$cattemp.N5F3O6 Thisisatestlineofdatafortemp.N5F3O6 [dir.ouT8S8]$cattemp.SQslb7 Thisisatestlineofdatafortemp.SQslb7 [dir.ouT8S8]$ Thescriptcreatesadirectoryinthecurrentdirectoryandusesthe cdcommandtochange to that directory before creating two temporary files. The two temporary files are then assignedtofiledescriptorsandusedtostoreoutputfromthescript. LoggingMessages Sometimes, it’s beneficial to send output both to the monitor and to a file for logging. Insteadofhavingtoredirectoutputtwice,youcanusethespecialteecommand. The tee command is like a T-connector for pipes. It sends data from STDIN to two destinations at the same time. One destination is STDOUT. The other destination is a filenamespecifiedontheteecommandline: teefilename Because teeredirectsdatafrom STDIN,youcanuseitwiththepipecommandtoredirect outputfromanycommand: $date|teetestfile SunOct1918:56:21EDT2014 $cattestfile SunOct1918:56:21EDT2014 $ Theoutputappearsin STDOUTandiswrittentothefilespecified.Becareful:Bydefault, theteecommandoverwritestheoutputfileoneachuse: $who|teetestfile richpts/02014-10-1718:41(192.168.1.2) $cattestfile richpts/02014-10-1718:41(192.168.1.2) $ Ifyouwanttoappenddatatothefile,youmustusethe-aoption: $date|tee-atestfile SunOct1918:58:05EDT2014 $cattestfile richpts/02014-10-1718:41(192.168.1.2) SunOct1918:58:05EDT2014 $ Usingthistechnique,youcanbothsavedatainfilesanddisplaythedataonthemonitor foryourusers: $cattest22 #!/bin/bash #usingtheteecommandforlogging tempfile=test22file echo“Thisisthestartofthetest”|tee$tempfile echo“Thisisthesecondlineofthetest”|tee-a$tempfile echo“Thisistheendofthetest”|tee-a$tempfile $./test22 Thisisthestartofthetest Thisisthesecondlineofthetest Thisistheendofthetest $cattest22file Thisisthestartofthetest Thisisthesecondlineofthetest Thisistheendofthetest $ Nowyoucansaveapermanentcopyofyouroutputatthesametimeasyou’redisplaying ittoyourusers. PracticalExample Fileredirectionisverycommonbothwhenreadingfilesintoscriptsandwhenoutputting datafromascriptintoafile.Thisexamplescriptdoesbothofthosethings.Itreadsa.csvformatteddatafileandoutputs SQL INSERT statements to insert the data into a database (seeChapter25). Theshellscriptusesacommandlineparametertodefinethenameofthe .csvfilefrom whichtoreadthedata.The .csvformatisusedtoexportdatafromspreadsheets,soyou canplacethedatabasedataintoaspreadsheet,savethespreadsheetin .csvformat,read thefile,andcreateINSERTstatementstoinsertthedataintoaMySQLdatabase. Here’swhatthescriptlookslike: $cattest23 #!/bin/bash #readfileandcreateINSERTstatementsforMySQL outfile=‘members.sql’ IFS=’,’ whilereadlnamefnameaddresscitystatezip do cat>>$outfile<<EOF INSERTINTOmembers(lname,fname,address,city,state,zip)VALUES (‘$lname’,‘$fname’,‘$address’,‘$city’,‘$state’,‘$zip’); EOF done<${1} $ That’s a pretty short script, thanks to the file redirection that goes on! There are three redirection operations happening in the script. The while loop uses the read statement (discussedinChapter14)toreadtextfromthedatafile.Noticeinthe donestatementthe redirectionsymbol: done<${1} The $1 represents the first command line parameter when you run the test23program. Thatspecifiesthedatafilefromwhichtoreadthedata.Thereadstatementparsesthetext usingtheIFScharacter,whichwespecifyasacomma. Theothertworedirectionoperationsinthescriptbothappearinthesamestatement: cat>>$outfile<<EOF This one statement has one output append redirection (the double greater-than symbol) and one input append redirection (the double less-than symbol). The output redirection appendsthecatcommandoutputtothefilespecifiedbythe$outfilevariable.Theinput tothecatcommandisredirectedfromthestandardinputtousethedatastoredinsidethe script.TheEOFsymbolmarksthestartandenddelimiterofthedatathat’sappendedto thefile: INSERTINTOmembers(lname,fname,address,city,state,zip)VALUES (‘$lname’,‘$fname’,‘$address’,‘$city’,‘$state’,‘$zip’); ThetextcreatesastandardSQLINSERTstatement.Noticethatthedatavaluesarereplaced withthevariablesforthedatareadfromthereadstatement. Sobasicallythe whileloopreadsonthedataonelineatatime,plugsthosedatavalues intotheINSERTstatementtemplate,thenoutputstheresulttotheoutputfile. Forthisexperiment,Iusedthisastheinputdatafile: $catmembers.csv Blum,Richard,123MainSt.,Chicago,IL,60601 Blum,Barbara,123MainSt.,Chicago,IL,60601 Bresnahan,Christine,456OakAve.,Columbus,OH,43201 Bresnahan,Timothy,456OakAve.,Columbus,OH,43201 $ Whenyourunthescript,nothingappearsintheoutputonthemonitor: $./test23<members.csv $ Butwhenyoulookatthemembers.sqloutputfile,youshouldseetheoutputdata: $catmembers.sql INSERTINTOmembers(lname,fname,address,city,state,zip)VALUES(‘Blum’, ‘Richard’,‘123MainSt.’,‘Chicago’,‘IL’,‘60601’); INSERTINTOmembers(lname,fname,address,city,state,zip)VALUES(‘Blum’, ‘Barbara’,‘123MainSt.’,‘Chicago’,‘IL’,‘60601’); INSERTINTOmembers(lname,fname,address,city,state,zip)VALUES (‘Bresnahan’, ‘Christine’,‘456OakAve.’,‘Columbus’,‘OH’,‘43201’); INSERTINTOmembers(lname,fname,address,city,state,zip)VALUES (‘Bresnahan’, ‘Timothy’,‘456OakAve.’,‘Columbus’,‘OH’,‘43201’); $ Thescriptworkedexactlyasexpected!Nowyoucaneasilyimportthe members.sqlfile intoaMySQLdatabasetable(seeChapter25). Summary Understanding how the bash shell handles input and output can come in handy when creating your scripts. You can manipulate both how the script receives data and how it displaysdata,tocustomizeyourscriptforanyenvironment.Youcanredirecttheinputofa scriptfromthestandardinput(STDIN)toanyfileonthesystem.Youcanalsoredirectthe outputofthescriptfromthestandardoutput(STDOUT)toanyfileonthesystem. Besides the STDOUT, you can redirect any error messages your script generates by redirecting the STDERR output. This is accomplished by redirecting the file descriptor associated with the STDERR output, which is file descriptor 2. You can redirect STDERR outputtothesamefileasthe STDOUToutputortoacompletelyseparatefile.Thisenables youtoseparatenormalscriptmessagesfromanyerrormessagesgeneratedbythescript. Thebashshellallowsyoutocreateyourownfiledescriptorsforuseinyourscripts.You cancreatefiledescriptors3through8andassignthemtoanyoutputfileyoudesire.After youcreateafiledescriptor,youcanredirecttheoutputofanycommandtoit,usingthe standardredirectionsymbols. Thebashshellalsoallowsyoutoredirectinputtoafiledescriptor,providinganeasyway toreaddatacontainedinafileintoyourscript.Youcanusethe lsofcommandtodisplay theactivefiledescriptorsinyourshell. Linuxsystemsprovideaspecialfile,called/dev/null,toallowyoutoredirectoutputthat youdon’twant.TheLinuxsystemdiscardsanythingredirectedtothe/dev/nullfile.You canalsousethisfiletoproduceanemptyfilebyredirectingthecontentsofthe/dev/null filetothefile. Themktempcommandisahandyfeatureofthebashshellthatallowsyoutoeasilycreate temporaryfilesanddirectories.Simplyspecifyatemplateforthemktempcommand,andit createsauniquefileeachtimeyoucallit,basedonthefiletemplateformat.Youcanalso createtemporaryfilesanddirectoriesinthe/tmpdirectoryontheLinuxsystem,whichisa speciallocationthatisn’tpreservedbetweensystemboots. Theteecommandisahandywaytosendoutputbothtothestandardoutputandtoalog file.Thisenablesyoutodisplaymessagesfromyourscriptonthemonitorandstorethem inalogfileatthesametime. In Chapter 16, you’ll see how to control and run your scripts. Linux provides several differentmethodsforrunningscriptsotherthandirectlyfromthecommandlineinterface prompt.You’llseehowtoscheduleyourscriptstorunataspecifictime,aswellaslearn howtopausethemwhilethey’rerunning. Chapter16 ScriptControl InThisChapter 1. Handlingsignals 2. Runningscriptsinthebackground 3. Forbiddinghang-ups 4. ControllingaJob 5. Modifyingscriptpriority 6. Automatingscriptexecution Asyoustartbuildingadvancedscripts,you’llprobablywonderhowtorunand controlthemonyourLinuxsystem.Sofarinthisbook,theonlywaywe’verun scriptsisdirectlyfromthecommandlineinterfaceinreal-timemode.Thisisn’tthe onlywaytorunscriptsinLinux.Quiteafewoptionsareavailableforrunningyour shellscripts.Therearealsooptionsforcontrollingyourscripts.Variouscontrol methodsincludesendingsignalstoyourscript,modifyingascript’spriority,and switchingtherunmodewhileascriptisrunning.Thischapterexaminesthedifferent waysyoucancontrolyourshellscripts. HandlingSignals Linux uses signals to communicate with processes running on the system. Chapter 4 describedthedifferentLinuxsignalsandhowtheLinuxsystemusesthesesignalstostop, start, and kill processes. You can control the operation of your shell script by programmingthescripttoperformcertaincommandswhenitreceivesspecificsignals. Signalingthebashshell There are more than 30 Linux signals that can be generated by the system and applications.Table16.1liststhemostcommonLinuxsystemsignalsthatyou’llrunacross inyourshellscriptwriting. Table16.1LinuxSignals Signal Value 1 SIGHUP 2 SIGINT 3 SIGQUIT 9 SIGKILL 15 SIGTERM 17 SIGSTOP 18 SIGTSTP 19 SIGCONT Description Hangsuptheprocess Interruptstheprocess Stopstheprocess Unconditionallyterminatestheprocess Terminatestheprocessifpossible Unconditionallystops,butdoesn’tterminate,theprocess Stopsorpausestheprocess,butdoesn’tterminate Continuesastoppedprocess Bydefault,thebashshellignoresany SIGQUIT(3)and SIGTERM(15)signalsitreceives (soaninteractiveshellcannotbeaccidentallyterminated).However,thebashshelldoes notignoreanySIGHUP(1)andSIGINT(2)signalsitreceives. Ifthebashshellreceivesa SIGHUPsignal,suchaswhenyouleaveaninteractiveshell,it exits.Beforeitexits,however,itpassesthe SIGHUPsignaltoanyprocessesstartedbythe shell,includinganyrunningshellscripts. WithaSIGINTsignal,theshellisjustinterrupted.TheLinuxkernelstopsgivingtheshell processingtimeontheCPU.Whenthishappens,theshellpassestheSIGINTsignaltoany processesstartedbytheshelltonotifythemofthesituation. As you probably have noticed, the shell passes these signals on to your shell script programforprocessing.However,ashellscript’sdefaultbehaviordoesnotgovernthese signals, which may have an adverse effect on the script’s operation. To avoid this situation, you can program your script to recognize signals and perform commands to preparethescriptfortheconsequencesofthesignal. Generatingsignals ThebashshellallowsyoutogeneratetwobasicLinuxsignalsusingkeycombinationson thekeyboard.Thisfeaturecomesinhandyifyouneedtostoporpausearunawayscript. Interruptingaprocess The Ctrl+C key combination generates a SIGINT signal and sends it to any processes currentlyrunningintheshell.Youcantestthisbyrunningacommandthatnormallytakes alongtimetofinishandpressingtheCtrl+Ckeycombination: $sleep100 ^C $ The Ctrl+C key combination sends a SIGINT signal, which simply stops the current process running in the shell. The sleep command pauses the shell’s operation for the specified number of seconds and returns the shell prompt. By pressing the Ctrl+C key combinationbeforethetimepassed,thesleepcommandterminatedprematurely. Pausingaprocess Instead of terminating a process, you can pause it in the middle of whatever it’s doing. Sometimes,thiscanbeadangerousthing(forexample,ifascripthasafilelockopenona crucialsystemfile),butoftenitallowsyoutopeekinsidewhatascriptisdoingwithout actuallyterminatingtheprocess. TheCtrl+Zkeycombinationgeneratesa SIGTSTPsignal,stoppinganyprocessesrunning in the shell. Stopping a process is different than terminating the process. Stopping the processleavestheprograminmemoryandabletocontinuerunningfromwhereitleftoff. Inthe“ControllingtheJob”sectionlaterinthischapter,youlearnhowtorestartaprocess that’sbeenstopped. WhenyouusetheCtrl+Zkeycombination,theshellinformsyouthattheprocesshasbeen stopped: $sleep100 ^Z [1]+Stoppedsleep100 $ Thenumberinthesquarebracketsisthejobnumberassignedbytheshell.Theshellrefers to each process running in the shell as a job and assigns each job a unique job number withinthecurrentshell.Itassignsthefirststartedprocessjobnumber1,thesecondjob number2,andsoon. Ifyouhaveastoppedjobassignedtoyourshellsession,bashwarnsyouifyoutrytoexit theshell: $sleep100 ^Z [1]+Stoppedsleep100 $exit exit Therearestoppedjobs. $ Youcanviewthestoppedjobsusingthepscommand: $sleep100 ^Z [1]+Stoppedsleep100 $ $ps-l FSUIDPIDPPIDCPRINIADDRSZWCHANTTYTIMECMD 0S501243124300800-27118waitpts/000:00:00bash 0T501245624310800-25227signalpts/000:00:00sleep 0R501245824310800-27034-pts/000:00:00ps $ Inthe Scolumn(processstate),the pscommandshowsthestoppedjob’sstateasT.This indicatesthecommandiseitherbeingtracedorisstopped. If you really want to exit the shell with a stopped job still active, just type the exit command again. The shell exits, terminating the stopped job. Alternately, now that you knowthePIDofthestoppedjob,youcanusethekillcommandtosendaSIGKILLsignal toterminateit: $kill-92456 $ [1]+Killedsleep100 $ Whenyoukillthejob,initiallyyoudon’tgetanyresponse.However,thenexttimeyoudo something that produces a shell prompt (such as pressing the Enter key), you’ll see a messageindicatingthatthejobwaskilled.Eachtimetheshellproducesaprompt,italso displaysthestatusofanyjobsthathavechangedstatesintheshell.Afteryoukillajob, thenexttimeyouforcetheshelltoproduceaprompt,itdisplaysamessageshowingthat thejobwaskilledwhilerunning. Trappingsignals Insteadofallowingyourscripttoleavesignalsungoverned,youcantrapthemwhenthey appear and perform other commands. The trap command allows you to specify which Linux signals your shell script can watch for and intercept from the shell. If the script receivesasignallistedinthe trapcommand,itpreventsitfrombeingprocessedbythe shellandinsteadhandlesitlocally. Theformatofthetrapcommandis: trapcommandssignals On the trap command line, you just list the commands you want the shell to execute, alongwithaspace-separatedlistofsignalsyouwanttotrap.Youcanspecifythesignals eitherbytheirnumericvalueorbytheirLinuxsignalname. Here’s a simple example of using the trap command to capture the SIGINT signal and governthescript’sbehaviorwhenthesignalissent: $cattest1.sh #!/bin/bash #Testingsignaltrapping # trap“echo‘Sorry!IhavetrappedCtrl-C”’SIGINT # echoThisisatestscript # count=1 while[$count-le10] do echo“Loop#$count” sleep1 count=$[$count+1] done # echo“Thisistheendofthetestscript” # The trap command used in this example displays a simple text message each time it detects the SIGINT signal. Trapping this signal makes this script impervious to the user attemptingtostoptheprogrambyusingthebashshellkeyboardCtrl+Ccommand: $./test1.sh Thisisatestscript Loop#1 Loop#2 Loop#3 Loop#4 Loop#5 ^CSorry!IhavetrappedCtrl-C Loop#6 Loop#7 Loop#8 ^CSorry!IhavetrappedCtrl-C Loop#9 Loop#10 Thisistheendofthetestscript $ EachtimetheCtrl+Ckeycombinationwasused,thescriptexecutedthe echostatement specifiedinthe trapcommandinsteadofnotmanagingthesignalandallowingtheshell tostopthescript. Trappingascriptexit Besidestrappingsignalsinyourshellscript,youcantrapthemwhentheshellscriptexits. Thisisaconvenientwaytoperformcommandsjustastheshellfinishesitsjob. Totraptheshellscriptexiting,justaddtheEXITsignaltothetrapcommand: $cattest2.sh #!/bin/bash #Trappingthescriptexit # trap“echoGoodbye…”EXIT # count=1 while[$count-le5] do echo“Loop#$count” sleep1 count=$[$count+1] done # $ $./test2.sh Loop#1 Loop#2 Loop#3 Loop#4 Loop#5 Goodbye… $ Whenthescriptgetstothenormalexitpoint,thetrapistriggered,andtheshellexecutes the command you specify on the trap command line. The EXIT trap also works if you prematurelyexitthescript: $./test2.sh Loop#1 Loop#2 Loop#3 ^CGoodbye… $ Because the SIGINT signal isn’t listed in the trap command list, when the Ctrl+C key combinationisusedtosendthatsignal,thescriptexits.However,beforethescriptexits, becausetheEXITistrapped,theshellexecutesthetrapcommand. Modifyingorremovingatrap Tohandletrapsdifferentlyinvarioussectionsofyourshellscript,yousimplyreissuethe trapcommandwithnewoptions: $cattest3.sh #!/bin/bash #Modifyingasettrap # trap“echo‘Sorry…Ctrl-Cistrapped.”’SIGINT # count=1 while[$count-le5] do echo“Loop#$count” sleep1 count=$[$count+1] done # trap“echo‘Imodifiedthetrap!”’SIGINT # count=1 while[$count-le5] do echo“SecondLoop#$count” sleep1 count=$[$count+1] done # $ After the signal trap is modified, the script manages the signal or signals differently. However,ifasignalisreceivedbeforethetrapismodified,thescriptprocessesitperthe originaltrapcommand: $./test3.sh Loop#1 Loop#2 Loop#3 ^CSorry…Ctrl-Cistrapped. Loop#4 Loop#5 SecondLoop#1 SecondLoop#2 ^CImodifiedthetrap! SecondLoop#3 SecondLoop#4 SecondLoop#5 $ Youcanalsoremoveasettrap.Simplyaddtwodashesafterthe trapcommandandalist ofthesignalsyouwanttoreturntodefaultbehavior: $cattest3b.sh #!/bin/bash #Removingasettrap # trap“echo‘Sorry…Ctrl-Cistrapped.”’SIGINT # count=1 while[$count-le5] do echo“Loop#$count” sleep1 count=$[$count+1] done # #Removethetrap trap—SIGINT echo“Ijustremovedthetrap” # count=1 while[$count-le5] do echo“SecondLoop#$count” sleep1 count=$[$count+1] done # $./test3b.sh Loop#1 Loop#2 Loop#3 Loop#4 Loop#5 Ijustremovedthetrap SecondLoop#1 SecondLoop#2 SecondLoop#3 ^C $ Tip Youcanuseasingledashinsteadofadoubledashafterthetrapcommandtoreturn signalstotheirdefaultbehavior.Boththesingleanddoubledashworkproperly. Afterthesignaltrapisremoved,thescripthandlestheSIGINTsignalinitsdefaultmanner, terminating the script. However, if a signal is received before the trap is removed, the scriptprocessesitpertheoriginaltrapcommand: $./test3b.sh Loop#1 Loop#2 Loop#3 ^CSorry…Ctrl-Cistrapped. Loop#4 Loop#5 Ijustremovedthetrap SecondLoop#1 SecondLoop#2 ^C $ In this example, the first Ctrl+C key combination was used to attempt to terminate the script prematurely. Because the signal was received before the trap was removed, the script executed the command specified in the trap. After the script executed the trap removal,thenCtrl+Ccouldprematurelyterminatethescript. RunningScriptsinBackgroundMode Sometimes, running a shell script directly from the command line interface is inconvenient.Somescriptscantakealongtimetoprocess,andyoumaynotwanttotieup thecommandlineinterfacewaiting.Whilethescriptisrunning,youcan’tdoanythingelse inyourterminalsession.Fortunately,there’sasimplesolutiontothatproblem. Whenyouusethe pscommand,youseeawholebunchofdifferentprocessesrunningon theLinuxsystem.Obviously,alltheseprocessesarenotrunningonyourterminalmonitor. Thisiscalledrunningprocessesinthebackground.Inbackgroundmode,aprocessruns without being associated with a STDIN, STDOUT, and STDERR on a terminal session (see Chapter15). Youcanexploitthisfeaturewithyourshellscriptsaswell,allowingthemtorunbehind thescenesandnotlockupyourterminalsession.Thefollowingsectionsdescribehowto runyourscriptsinbackgroundmodeonyourLinuxsystem. Runninginthebackground Runningashellscriptinbackgroundmodeisafairlyeasythingtodo.Torunashellscript in background mode from the command line interface, just place an ampersand symbol (&)afterthecommand: $cattest4.sh #!/bin/bash #Testrunninginthebackground # count=1 while[$count-le10] do sleep1 count=$[$count+1] done # $ $./test4.sh& [1]3231 $ Whenyouplacetheampersandsymbolafteracommand,itseparatesthecommandfrom thebashshellandrunsitasaseparatebackgroundprocessonthesystem.Thefirstthing thatdisplaysistheline: [1]3231 The number in the square brackets is the job number assigned by the shell to the backgroundprocess.ThenextnumberistheProcessID(PID)theLinuxsystemassignsto theprocess.EveryprocessrunningontheLinuxsystemmusthaveauniquePID. Assoonasthesystemdisplaystheseitems,anewcommandlineinterfacepromptappears. Youarereturnedtotheshell,andthecommandyouexecutedrunssafelyinbackground mode.Atthispoint,youcanenternewcommandsattheprompt. Whenthebackgroundprocessfinishes,itdisplaysamessageontheterminal: [1]Done./test4.sh Thisshowsthejobnumberandthestatusofthejob(Done),alongwiththecommandused tostartthejob. Beawarethatwhilethebackgroundprocessisrunning,itstillusesyourterminalmonitor forSTDOUTandSTDERRmessages: $cattest5.sh #!/bin/bash #Testrunninginthebackgroundwithoutput # echo“Startthetestscript” count=1 while[$count-le5] do echo“Loop#$count” sleep5 count=$[$count+1] done # echo“Testscriptiscomplete” # $ $./test5.sh& [1]3275 $Startthetestscript Loop#1 Loop#2 Loop#3 Loop#4 Loop#5 Testscriptiscomplete [1]Done./test5.sh $ You’ll notice from the example that the output from the test5.sh script displays. The outputintermixeswiththeshellprompt,whichiswhy Start the test scriptappears nexttothe$prompt. Youcanstillissuecommandswhilethisoutputisoccurring: $./test5.sh& [1]3319 $Startthetestscript Loop#1 Loop#2 Loop#3 lsmyprog* myprogmyprog.c $Loop#4 Loop#5 Testscriptiscomplete [1]+Done./test5.sh $$ Whilethe test5.sh script is running in the background, the command ls myprog*was entered.Thescript’soutput,thetypedcommand,andthecommand’soutputallintermixed with each other’s output display. This can be confusing! It is a good idea to redirect STDOUT and STDERR for scripts you will be running in the background (Chapter 15) to avoidthismessyoutput. Runningmultiplebackgroundjobs You can start any number of background jobs at the same time from the command line prompt: $./test6.sh& [1]3568 $ThisisTestScript#1 $./test7.sh& [2]3570 $ThisisTestScript#2 $./test8.sh& [3]3573 $And…anotherTestscript $./test9.sh& [4]3576 $Then…therewasonemoretestscript $ Eachtimeyoustartanewjob,theLinuxsystemassignsitanewjobnumberandPID.You canseethatallthescriptsarerunningusingthepscommand: $ps PIDTTYTIMECMD 2431pts/000:00:00bash 3568pts/000:00:00test6.sh 3570pts/000:00:00test7.sh 3573pts/000:00:00test8.sh 3574pts/000:00:00sleep 3575pts/000:00:00sleep 3576pts/000:00:00test9.sh 3577pts/000:00:00sleep 3578pts/000:00:00sleep 3579pts/000:00:00ps $ Youmustbecarefulwhenusingbackgroundprocessesfromaterminalsession.Noticein the output from the ps command that each of the background processes is tied to the terminalsession(pts/0)terminal.Iftheterminalsessionexits,thebackgroundprocessalso exits. Note Earlierinthischapterwementionedthatwhenyouattempttoexitaterminalsession, awarningisissuediftherearestoppedprocesses.However,withbackground processes,onlysometerminalemulatorsremindyouthatabackgroundjobis running,beforeyouattempttoexittheterminalsession. Ifyouwantyourscripttocontinuerunninginbackgroundmodeafteryouhaveloggedoff the console, there’s something else you need to do. The next section discusses that process. RunningScriptswithoutaHang-Up Sometimes,youmaywanttostartashellscriptfromaterminalsessionandletthescript runinbackgroundmodeuntilitfinishes,evenifyouexittheterminalsession.Youcando thisbyusingthenohupcommand. The nohupcommandrunsanothercommandblockingany SIGHUPsignalsthataresentto theprocess.Thispreventstheprocessfromexitingwhenyouexityourterminalsession. Theformatusedforthenohupcommandisasfollows: $nohup./test1.sh& [1]3856 $nohup:ignoringinputandappendingoutputto‘nohup.out’ $ Aswithanormalbackgroundprocess,theshellassignsthecommandajobnumber,and the Linux system assigns a PID number. The difference is that when you use the nohup command,thescriptignoresany SIGHUPsignalssentbytheterminalsessionifyouclose thesession. Becausethenohupcommanddisassociatestheprocessfromtheterminal,theprocessloses the STDOUT and STDERR output links. To accommodate any output generated by the command,the nohupcommandautomaticallyredirects STDOUTand STDERRmessagestoa file,callednohup.out. Note Ifyourunanothercommandusingnohup,theoutputisappendedtotheexisting nohup.outfile.Becarefulwhenrunningmultiplecommandsfromthesame directory,becausealltheoutputissenttothesamenohup.outfile,whichcanget confusing. The nohup.out file contains all the output that would normally be sent to the terminal monitor.Aftertheprocessfinishesrunning,youcanviewthenohup.outfilefortheoutput results: $catnohup.out Thisisatestscript Loop1 Loop2 Loop3 Loop4 Loop5 Loop6 Loop7 Loop8 Loop9 Loop10 Thisistheendofthetestscript $ Theoutputappearsinthenohup.outfilejustasiftheprocessranonthecommandline. ControllingtheJob Earlier in this chapter, you saw how to use the Ctrl+C key combination to stop a job runningintheshell.Afteryoustopajob,theLinuxsystemletsyoueitherkillorrestartit. You can kill the process by using the kill command. Restarting a stopped process requiresthatyousenditaSIGCONTsignal. The function of starting, stopping, killing, and resuming jobs is called jobcontrol.With jobcontrol,youhavefullcontroloverhowprocessesruninyourshellenvironment.This sectiondescribesthecommandsusedtoviewandcontroljobsrunninginyourshell. Viewingjobs Thekeycommandforjobcontrolisthejobscommand.Thejobscommandallowsyouto viewthecurrentjobsbeinghandledbytheshell: $cattest10.sh #!/bin/bash #Testjobcontrol # echo“ScriptProcessID:$$” # count=1 while[$count-le10] do echo“Loop#$count” sleep10 count=$[$count+1] done # echo“Endofscript…” # $ The script uses the $$ variable to display the PID that the Linux system assigns to the script;thenitgoesintoaloop,sleepingfor10secondsatatimeforeachiteration. YoucanstartthescriptfromthecommandlineinterfaceandthenstopitusingtheCtrl+Z keycombination: $./test10.sh ScriptProcessID:1897 Loop#1 Loop#2 ^Z [1]+Stopped./test10.sh $ Usingthesamescript,anotherjobisstartedasabackgroundprocess,usingtheampersand symbol. To make life a little easier, the output of that script is redirected to a file so it doesn’tappearonthescreen: $./test10.sh>test10.out& [2]1917 $ Thejobscommandenablesyoutoviewthejobsassignedtotheshell.Thejobscommand shows both the stopped and the running jobs, along with their job numbers and the commandsusedinthejobs: $jobs [1]+Stopped./test10.sh [2]-Running./test10.sh>test10.out& $ Youcanviewthevariousjobs’PIDsbyaddingthe-lparameter(lowercaseL)onthejobs command: $jobs-l [1]+1897Stopped./test10.sh [2]-1917Running./test10.sh>test10.out& $ The jobs command uses a few different command line parameters, as shown in Table 16.2. Table16.2ThejobsCommandParameters Parameter -l -n -p -r -s Description ListsthePIDoftheprocessalongwiththejobnumber Listsonlyjobsthathavechangedtheirstatussincethelastnotificationfrom theshell ListsonlythePIDsofthejobs Listsonlytherunningjobs Listsonlystoppedjobs Youprobablynoticedtheplusandminussignsinthejobscommandoutput.Thejobwith the plus sign is considered the default job. It would be the job referenced by any job controlcommandsifajobnumberwasn’tspecifiedinthecommandline. Thejobwiththeminussignisthejobthatwouldbecomethedefaultjobwhenthecurrent defaultjobfinishesprocessing.Therewillbeonlyonejobwiththeplussignandonejob withtheminussignatanytime,nomatterhowmanyjobsarerunningintheshell. The following is an example showing how the next job in line takes over the default status, when the default job is removed. Three separate processes are started in the background. The jobs command listing shows the three processes, their PID, and their status.Notethatthedefaultprocess(theonelistedwiththeplussign)isthelastprocess started,job#3. $./test10.sh>test10a.out& [1]1950 $./test10.sh>test10b.out& [2]1952 $./test10.sh>test10c.out& [3]1955 $ $jobs-l [1]1950Running./test10.sh>test10a.out& [2]-1952Running./test10.sh>test10b.out& [3]+1955Running./test10.sh>test10c.out& $ Usingthe killcommandtosenda SIGHUPsignaltothedefaultprocesscausesthejobto terminate.Inthenext jobslisting,thejobthatpreviouslyhadtheminussignnowhasthe plussignandisthedefaultjob: $kill1955 $ [3]+Terminated./test10.sh>test10c.out $ $jobs-l [1]-1950Running./test10.sh>test10a.out& [2]+1952Running./test10.sh>test10b.out& $ $kill1952 $ [2]+Terminated./test10.sh>test10b.out $ $jobs-l [1]+1950Running./test10.sh>test10a.out& $ Althoughchangingabackgroundjobtothedefaultprocessisinteresting,itdoesn’tseem very useful. In the next section, you learn how to use commands to interact with the defaultprocessusingnoPIDorjobnumber. Restartingstoppedjobs Underbashjobcontrol,youcanrestartanystoppedjobaseitherabackgroundprocessor a foreground process. A foreground process takes over control of the terminal you’re workingon,sobecarefulaboutusingthatfeature. Torestartajobinbackgroundmode,usethebgcommand: $./test11.sh ^Z [1]+Stopped./test11.sh $ $bg [1]+./test11.sh& $ $jobs [1]+Running./test11.sh& $ Becausethejobwasthedefaultjob,indicatedbytheplussign,onlythe bgcommandwas needed to restart it in background mode. Notice that no PID is listed when the job is movedintobackgroundmode. Ifyouhaveadditionaljobs,youneedtousethejobnumberalongwiththebgcommand: $./test11.sh ^Z [1]+Stopped./test11.sh $ $./test12.sh ^Z [2]+Stopped./test12.sh $ $bg2 [2]+./test12.sh& $ $jobs [1]+Stopped./test11.sh [2]-Running./test12.sh& $ Thecommand bg2wasusedtosendthesecondjobintobackgroundmode.Noticethat whenthe jobscommandwasused,itlistedbothjobswiththeirstatus,eventhoughthe defaultjobisnotcurrentlyinbackgroundmode. Torestartajobinforegroundmode,usethefgcommand,alongwiththejobnumber: $fg2 ./test12.sh Thisisthescript’send… $ Becausethejobisrunninginforegroundmode,thecommandlineinterfacepromptdoes notappearuntilthejobfinishes. BeingNice In a multitasking operating system (which Linux is), the kernel is responsible for assigningCPUtimeforeachprocessrunningonthesystem.Theschedulingpriorityisthe amountofCPUtimethekernelassignstotheprocessrelativetotheotherprocesses.By default,allprocessesstartedfromtheshellhavethesameschedulingpriorityontheLinux system. The scheduling priority is an integer value, from -20 (the highest priority) to +19 (the lowestpriority).Bydefault,thebashshellstartsallprocesseswithaschedulingpriorityof 0. Tip It’sconfusingtorememberthat-20,thelowestvalue,isthehighestpriorityand19, thehighestvalue,isthelowestpriority.Justrememberthephrase,“Niceguysfinish last.”The“nicer”orhigheryouareinvalue,theloweryourchanceofgettingthe CPU. Sometimes,youwanttochangethepriorityofashellscript,eitherloweringitspriorityso itdoesn’ttakeasmuchprocessingpowerawayfromotherprocessesorgivingitahigher prioritysoitgetsmoreprocessingtime.Youcandothisbyusingthenicecommand. Usingthenicecommand Thenicecommandallowsyoutosettheschedulingpriorityofacommandasyoustartit. Tomakeacommandrunwithlesspriority,justusethe -ncommandlineoptionfor nice tospecifyanewprioritylevel: $nice-n10./test4.sh>test4.out& [1]4973 $ $ps-p4973-opid,ppid,ni,cmd PIDPPIDNICMD 4973472110/bin/bash./test4.sh $ Notice that you must use the nice command on the same line as the command you are starting.Theoutputfromthe pscommandconfirmsthatthenicevalue(column NI)has beensetto10. The nice command causes the script to run at a lower priority. However, if you try to increasethepriorityofoneofyourcommands,youmightbeinforasurprise: $nice-n-10./test4.sh>test4.out& [1]4985 $nice:cannotsetniceness:Permissiondenied [1]+Donenice-n-10./test4.sh>test4.out $ The nice command prevents normal system users from increasing the priority of their commands.Noticethatthejobdoesrun,eventhoughtheattempttoraiseitsprioritywith thenicecommandfailed. You don’t have to use the -n option with the nice command. You can simply type the priorityprecededbyadash: $nice-10./test4.sh>test4.out& [1]4993 $ $ps-p4993-opid,ppid,ni,cmd PIDPPIDNICMD 4993472110/bin/bash./test4.sh $ However,thiscangetconfusingwhenthepriorityisanegativenumber,becauseyoumust haveadouble-dash.It’sbestjusttousethe-noptiontoavoidconfusion. Usingtherenicecommand Sometimes,you’dliketochangethepriorityofacommandthat’salreadyrunningonthe system. That’s what the renice command is for. It allows you to specify the PID of a runningprocesstochangeitspriority: $./test11.sh& [1]5055 $ $ps-p5055-opid,ppid,ni,cmd PIDPPIDNICMD 505547210/bin/bash./test11.sh $ $renice-n10-p5055 5055:oldpriority0,newpriority10 $ $ps-p5055-opid,ppid,ni,cmd PIDPPIDNICMD 5055472110/bin/bash./test11.sh $ The renice command automatically updates the scheduling priority of the running process.Aswiththenicecommand,therenicecommandhassomelimitations: Youcanonlyreniceprocessesthatyouown. Youcanonlyreniceyourprocessestoalowerpriority. Therootusercanreniceanyprocesstoanypriority. Ifyouwanttofullycontrolrunningprocesses,youmustbeloggedinastherootaccount orusethesudocommand. RunningLikeClockwork Whenyoustartworkingwithscripts,youmaywanttorunascriptatapresettime,usually at a time when you’re not there. The Linux system provides a couple of ways to run a script at a preselected time: the at command and the cron table. Each method uses a different technique for scheduling when and how often to run scripts. The following sectionsdescribeeachofthesemethods. Schedulingajobusingtheatcommand The atcommandallowsyoutospecifyatimewhentheLinuxsystemwillrunascript. The atcommandsubmitsajobtoaqueuewithdirectionsonwhentheshellshouldrun thejob.The atdaemon,atd,runsinthebackgroundandchecksthejobqueueforjobsto run.MostLinuxdistributionsstartthisdaemonautomaticallyatboottime. The atd daemon checks a special directory on the system (usually /var/spool/at) for jobssubmittedusingthe atcommand.Bydefault,the atddaemonchecksthisdirectory every60seconds.Whenajobispresent,the atddaemonchecksthetimethejobissetto berun.Ifthetimematchesthecurrenttime,theatddaemonrunsthejob. The following sections describe how to use the at command to submit jobs to run and howtomanagethesejobs. Understandingtheatcommandformat Thebasicatcommandformatisprettysimple: at[-ffilename]time By default, the at command submits input from STDIN to the queue. You can specify a filenameusedtoreadcommands(yourscriptfile)usingthe-fparameter. The time parameter specifies when you want the Linux system to run the job. If you specifyatimethathasalreadypassed,the atcommandrunsthejobatthattimeonthe nextday. Youcangetprettycreativewithhowyouspecifythetime.The atcommandrecognizes lotsofdifferenttimeformats: Astandardhourandminute,suchas10:15 AnAM/PMindicator,suchas10:15PM Aspecificnamedtime,suchasnow,noon,midnight,orteatime(4PM) Inadditiontospecifyingthetimetorunthejob,youcanalsoincludeaspecificdate,using afewdifferentdateformats: Astandarddateformat,suchasMMDDYY,MM/DD/YY,orDD.MM.YY Atextdate,suchasJul4orDec25,withorwithouttheyear Atimeincrement: Now+25minutes 10:15PMtomorrow 10:15+7days Whenyouusetheatcommand,thejobissubmittedintoajobqueue.Thejobqueueholds thejobssubmittedbythe atcommandforprocessing.Thereare26differentjobqueues availablefordifferentprioritylevels.Jobqueuesarereferencedusinglowercaseletters,a throughz,anduppercaselettersAthroughZ. Note Afewyearsago,thebatchcommandwasanothermethodthatallowedascripttobe runatalatertime.Thebatchcommandwasuniquebecauseyoucouldschedulea scripttorunwhenthesystemwasatalowerusagelevel.However,nowadays,the batchcommandisjustsimplyascript,/usr/bin/batch,thatcallstheatcommand andsubmitsyourjobtothebqueue. Thehigheralphabeticallythejobqueue,thelowerthepriority(higher nicevalue)thejob willrununder.Bydefault, atjobsaresubmittedtothe atjobaqueue.Ifyouwanttorun ajobatalowerpriority,youcanspecifyadifferentqueueletterusingthe-qparameter. Retrievingjoboutput When the job runs on the Linux system, there’s no monitor associated with the job. Instead, the Linux system uses the e-mail address of the user who submitted the job as STDOUTandSTDERR.AnyoutputdestinedtoSTDOUTor STDERRismailedtotheuserviathe mailsystem. Here’s a simple example using the at command to schedule a job to run on a CentOS distribution: $cattest13.sh #!/bin/bash #Testusingatcommand # echo“Thisscriptranat$(date+%B%d,%T)” echo sleep5 echo“Thisisthescript’send…” # $at-ftest13.shnow job7at2015-07-1412:38 $ The atcommanddisplaysthejobnumberassignedtothejobalongwiththetimethejob isscheduledtorun.The-foptiontellswhatscriptfiletouseandthenowtimedesignation directsattorunthescriptimmediately. Usinge-mailfortheatcommand’soutputisinconvenientatbest.Theatcommandsends e-mailviathesendmailapplication.Ifyoursystemdoesnotusesendmail,youwon’tget anyoutput!Therefore,it’sbesttoredirectSTDOUTandSTDERRinyourscripts(seeChapter 15)whenusingtheatcommand,asthefollowingexampleshows: $cattest13b.sh #!/bin/bash #Testusingatcommand # echo“Thisscriptranat$(date+%B%d,%T)”>test13b.out echo>>test13b.out sleep5 echo“Thisisthescript’send…”>>test13b.out # $ $at-M-ftest13b.shnow job8at2015-07-1412:48 $ $cattest13b.out ThisscriptranatJuly14,12:48:18 Thisisthescript’send… $ If you don’t want to use e-mail or redirection with at, it is best to add the -Moptionto suppressanyoutputgeneratedbyjobsusingtheatcommand. Listingpendingjobs Theatqcommandallowsyoutoviewwhatjobsarependingonthesystem: $at-M-ftest13b.shteatime job17at2015-07-1416:00 $ $at-M-ftest13b.shtomorrow job18at2015-07-1513:03 $ $at-M-ftest13b.sh13:30 job19at2015-07-1413:30 $ $at-M-ftest13b.shnow job20at2015-07-1413:03 $ $atq 202015-07-1413:03=Christine 182015-07-1513:03aChristine 172015-07-1416:00aChristine 192015-07-1413:30aChristine $ Thejoblistingshowsthejobnumber,thedateandtimethesystemwillrunthejob,and thejobqueuethejobisstoredin. Removingjobs Afteryouknowtheinformationaboutwhatjobsarependinginthejobqueues,youcan usetheatrmcommandtoremoveapendingjob: $atq 182015-07-1513:03aChristine 172015-07-1416:00aChristine 192015-07-1413:30aChristine $ $atrm18 $ $atq 172015-07-1416:00aChristine 192015-07-1413:30aChristine $ Just specify the job number you want to remove. You can only remove jobs that you submitforexecution.Youcan’tremovejobssubmittedbyothers. Schedulingregularscripts Usingtheatcommandtoscheduleascripttorunatapresettimeisgreat,butwhatifyou needthatscripttorunatthesametimeeverydayoronceaweekoronceamonth?Instead ofhavingtocontinuallysubmitatjobs,youcanuseanotherfeatureoftheLinuxsystem. TheLinuxsystemusesthecronprogramtoallowyoutoschedulejobsthatneedtorunon aregularbasis.Thecronprogramrunsinthebackgroundandchecksspecialtables,called crontables,forjobsthatarescheduledtorun. Lookingatthecrontable Thecrontableusesaspecialformatforallowingyoutospecifywhenajobshouldberun. Theformatforthecrontableis: minhourdayofmonthmonthdayofweekcommand The crontableallowsyoutospecifyentriesasspecificvalues,rangesofvalues(suchas 1–5),orasawildcardcharacter(theasterisk).Forexample,ifyouwanttorunacommand at10:15oneveryday,youwouldusethiscrontableentry: 1510***command Thewildcardcharacterusedinthedayofmonth,month,anddayofweekfieldsindicatesthat cronwillexecutethecommandeverydayofeverymonthat10:15.Tospecifyacommand torunat4:15PMeveryMonday,youwouldusethefollowing: 1516**1command Youcanspecifythedayofweekentryaseitherathree-charactertextvalue(mon,tue,wed, thu,fri,sat,sun)orasanumericvalue,with0beingSundayand6beingSaturday. Here’sanotherexample:toexecuteacommandat12noononthefirstdayofeverymonth, youwouldusethefollowingformat: 00121**command Thedayofmonthentryspecifiesadatevalue(1–31)forthemonth. Note Theastutereadermightbewonderingjusthowyouwouldbeabletosetacommand toexecuteonthelastdayofeverymonthbecauseyoucan’tsetthedayofmonthvalue tocovereverymonth.ThisproblemhasplaguedLinuxandUnixprogrammers,and hasspawnedquiteafewdifferentsolutions.Acommonmethodistoaddanif-then statementthatusesthedatecommandtocheckiftomorrow’sdateis01: 1. 0012***if[”date+%d-dtomorrow”=01];then;command Thischeckseverydayat12noontoseeifit’sthelastdayofthemonth,andifso, cronrunsthecommand. Thecommandlistmustspecifythefullcommandpathnameorshellscripttorun.Youcan addanycommandlineparametersorredirectionsymbolsyoulike,asaregularcommand line: 1510***/home/rich/test4.sh>test4out Thecronprogramrunsthescriptusingtheuseraccountthatsubmittedthejob.Thus,you musthavetheproperpermissionstoaccessthecommandandoutputfilesspecifiedinthe commandlisting. Buildingthecrontable Each system user can have their own cron table (including the root user) for running scheduledjobs.Linuxprovidesthe crontabcommandforhandlingthe crontable.Tolist anexistingcrontable,usethe-lparameter: $crontab-l nocrontabforrich $ Bydefault,eachuser’scrontablefiledoesn’texist.Toaddentriestoyour crontable,use the -e parameter. When you do that, the crontab command starts a text editor (see Chapter10)withtheexistingcrontable(oranemptyfileifitdoesn’tyetexist). Viewingcrondirectories Whenyoucreateascriptthathaslesspreciseexecutiontimeneeds,itiseasiertouseone ofthepre-configuredcronscriptdirectories.Therearefourbasicdirectories:hourly,daily, monthly,andweekly. $ls/etc/cron.*ly /etc/cron.daily: cupsmakewhatis.cronprelinktmpwatch logrotatemlocate.cronreadahead.cron /etc/cron.hourly: 0anacron /etc/cron.monthly: readahead-monthly.cron /etc/cron.weekly: $ Thus,ifyouhaveascriptthatneedstoberunonetimeperday,justcopythescripttothe dailydirectoryandcronexecutesiteachday. Lookingattheanacronprogram The only problem with the cron program is that it assumes that your Linux system is operational 24 hours a day, 7 days a week. Unless you’re running Linux in a server environment,thismaynotnecessarilybetrue. IftheLinuxsystemisturnedoffatthetimeajobisscheduledtoruninthecrontable,the jobdoesn’trun.The cronprogramdoesn’tretroactivelyrunmissedjobswhenthesystem isturnedbackon.Toresolvethisissue,manyLinuxdistributionsalsoincludetheanacron program. Ifanacrondeterminesthatajobhasmissedascheduledrunning,itrunsthejobassoonas possible.ThismeansthatifyourLinuxsystemisturnedoffforafewdays,whenitstarts backup,anyjobsscheduledtorunduringthetimeitwasoffareautomaticallyrun. Thisfeatureisoftenusedforscriptsthatperformroutinelogmaintenance.Ifthesystemis always off when the script should run, the log files would never get trimmed and could growtoundesirablesizes.With anacron,you’reguaranteedthatthelogfilesaretrimmed atleasteachtimethesystemisstarted. The anacron program deals only with programs located in the cron directories, such as /etc/cron.monthly. It uses timestamps to determine if the jobs have been run at the properscheduledinterval.Atimestampfileexistsforeachcrondirectoryandislocatedin /var/spool/anacron: $sudocat/var/spool/anacron/cron.monthly 20150626 $ Theanacronprogramhasitsowntable(usuallylocatedat/etc/anacrontab)tocheckthe jobdirectories: $sudocat/etc/anacrontab #/etc/anacrontab:configurationfileforanacron #Seeanacron(8)andanacrontab(5)fordetails. SHELL=/bin/sh PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root #themaximalrandomdelayaddedtothebasedelayofthejobs RANDOM_DELAY=45 #thejobswillbestartedduringthefollowinghoursonly START_HOURS_RANGE=3-22 #periodindaysdelayinminutesjob-identifiercommand 15cron.dailynicerun-parts/etc/cron.daily 725cron.weeklynicerun-parts/etc/cron.weekly @monthly45cron.monthlynicerun-parts/etc/cron.monthly $ Thebasicformatoftheanacrontableisslightlydifferentfromthatofthecrontable: perioddelayidentifiercommand Theperiodentrydefineshowoftenthejobsshouldberun,specifiedindays.Theanacron programusesthisentrytocheckagainstthejobs’timestampfile.Thedelayentryspecifies howmanyminutesafterthesystemstartstheanacronprogramshouldrunmissedscripts. The command entry contains the run-parts program and a cron script directory name. Therun-partsprogramisresponsibleforrunninganyscriptinthedirectorypassedtoit. Noticethatanacrondoesnotrunthescriptslocatedin/etc/cron.hourly.Thisisbecause theanacronprogramdoesnotdealwithscriptsthathaveexecutiontimeneedsoflessthan daily. Theidentifierentryisauniquenon-blankcharacterstring—forexample,cron-weekly.It isusedtouniquelyidentifythejobinlogmessagesanderrore-mails. Startingscriptswithanewshell The ability to run a script every time a user starts a new bash shell (even just when a specific user starts a bash shell) can come in handy. Sometimes, you want to set shell featuresforashellsessionorjustensurethataspecificfilehasbeenset. Recall the startup files run when a user logs into the bash shell (covered in detail in Chapter6).Also,rememberthatnoteverydistributionhasallthestartupfiles.Essentially, thefirstfilefoundinthefollowingorderedlistisrunandtherestareignored: $HOME/.bash_profile $HOME/.bash_login $HOME/.profile Therefore,youshouldplaceanyscriptsyouwantrunatlogintimeinthefirstfilelisted. Thebashshellrunsthe .bashrcfileanytimeanewshellisstarted.Youcantestthisby addingasimpleechostatementtothe .bashrcfileinyourhomedirectoryandstartinga newshell: $cat.bashrc #.bashrc #Sourceglobaldefinitions if[-f/etc/bashrc];then ./etc/bashrc fi #Userspecificaliasesandfunctions echo“I’minanewshell!” $ $bash I’minanewshell! $ $exit exit $ The .bashrc file is also typically run from one of the bash startup files. Because the .bashrcfilerunsbothwhenyoulogintothebashshellandwhenyoustartabashshell,if youneedascripttoruninbothinstances,placeyourshellscriptinsidethisfile. Summary TheLinuxsystemallowsyoutocontrolyourshellscriptsbyusingsignals.Thebashshell acceptssignalsandpassesthemontoanyprocessrunningundertheshellprocess.Linux signals allow you to easily kill a runaway process or temporarily pause a long-running process. Youcanusethe trapstatementinyourscriptstocatchsignalsandperformcommands. This feature provides a simple way to control whether a user can interrupt your script whileit’srunning. By default, when you run a script in a terminal session shell, the interactive shell is suspended until the script completes. You can cause a script or command to run in backgroundmodebyaddinganampersandsign(&)afterthecommandname.Whenyou runascriptorcommandinbackgroundmode,theinteractiveshellreturns,allowingyou to continue entering more commands. Any background processes run using this method are still tied to the terminal session. If you exit the terminal session, the background processesalsoexit. To prevent this from happening, use the nohup command. This command intercepts any signals intended for the command that would stop it — for example, when you exit the terminalsession.Thisallowsscriptstocontinuerunninginbackgroundmodeevenifyou exittheterminalsession. Whenyoumoveaprocesstobackgroundmode,youcanstillcontrolwhathappenstoit. Thejobscommandallowsyoutoviewprocessesstartedfromtheshellsession.Afteryou knowthejobIDofabackgroundprocess,youcanusethe killcommandtosendLinux signalstotheprocessorusethe fgcommandtobringtheprocessbacktotheforeground in the shell session. You can suspend a running foreground process by using the Ctrl+Z keycombinationandplaceitbackinbackgroundmode,usingthebgcommand. The niceand renicecommandsallowyoutochangetheprioritylevelofaprocess.By givingaprocessalowerpriority,youallowtheCPUtoallocatelesstimetoit.Thiscomes inhandywhenrunninglongprocessesthatcantakelotsofCPUtime. Inadditiontocontrollingprocesseswhilethey’rerunning,youcanalsodeterminewhena processstartsonthesystem.Insteadofrunningascriptdirectlyfromthecommandline interface prompt, you can schedule the process to run at an alternative time. You can accomplish this in several different ways. The at command enables you to run a script once at a preset time. The cron program provides an interface that can run scripts at a regularlyscheduledinterval. Finally,theLinuxsystemprovidesscriptfilesforyoutouseforschedulingyourscriptsto runwheneverauserstartsanewbashshell.Similarly,thestartupfiles,suchas .bashrc, are located in every user’s home directory to provide a location to place scripts and commandsthatrunwithanewshell. Inthenextchapter,welookathowtowritescriptfunctions.Scriptfunctionsallowyouto writecodeblocksonceandthenusetheminmultiplelocationsthroughoutyourscript. PartIII AdvancedShellScripting InThisPart 1. Chapter17CreatingFunctions 1. Chapter18WritingScriptsforGraphicalDesktops 1. Chapter19Introducingsedandgawk 1. Chapter20RegularExpressions 1. Chapter21Advancedsed 1. Chapter22Advancedgawk 1. Chapter23WorkingwithAlternativeShells Chapter17 CreatingFunctions InThisChapter 1. Basicscriptfunctions 2. Returningavalue 3. Usingvariablesinfunctions 4. Arrayandvariablefunctions 5. Functionrecursion 6. Creatingalibrary 7. Usingfunctionsonthecommandline Oftenwhilewritingshellscripts,you’llfindyourselfusingthesamecodeinmultiple locations.Ifit’sjustasmallcodesnippet,it’susuallynotthatbigofadeal.However, rewritinglargechunksofcodemultipletimesinyourshellscriptcangettiring.The bashshellprovidesawaytohelpyououtbysupportinguser-definedfunctions.You canencapsulateyourshellscriptcodeintoafunctionanduseitasmanytimesasyou wantanywhereinyourscript.Thischapterwalksyouthroughtheprocessofcreating yourownshellscriptfunctionsanddemonstrateshowtousetheminothershell scriptapplications. BasicScriptFunctions Asyoustartwritingmorecomplexshellscripts,you’llfindyourselfreusingpartsofcode that perform specific tasks. Sometimes, it’s something simple, such as displaying a text message and retrieving an answer from the script users. Other times, it’s a complicated calculationthat’susedmultipletimesinyourscriptaspartofalargerprocess. Ineachofthesesituations,itcangettiresomewritingthesameblocksofcodeoverand overinyourscript.Itwouldbenicetojustwritetheblockofcodeonceandbeableto refertothatblockofcodeanywhereinyourscriptwithouthavingtorewriteit. The bash shell provides a feature allowing you to do just that. Functions are blocks of scriptcodethatyouassignanametoandreuseanywhereinyourcode.Anytimeyouneed tousethatblockofcodeinyourscript,yousimplyusethefunctionnameyouassignedit (referredtoascallingthefunction).Thissectiondescribeshowtocreateandusefunctions inyourshellscripts. Creatingafunction There are two formats you can use to create functions in bash shell scripts. The first formatusesthekeyword function,alongwiththefunctionnameyouassigntotheblock ofcode: functionname{ commands } The name attribute defines a unique name assigned to the function. Each function you defineinyourscriptmustbeassignedauniquename. The commandsareoneormorebashshellcommandsthatmakeupyourfunction.When you call the function, the bash shell executes each of the commands in the order they appearinthefunction,justasinanormalscript. Thesecondformatfordefiningafunctioninabashshellscriptmorecloselyfollowshow functionsaredefinedinotherprogramminglanguages: name(){ commands } The empty parentheses after the function name indicate that you’re defining a function. Thesamenamingrulesapplyinthisformatasintheoriginalshellscriptfunctionformat. Usingfunctions Touseafunctioninyourscript,specifythefunctionnameonaline,justasyouwouldany othershellcommand: $cattest1 #!/bin/bash #usingafunctioninascript functionfunc1{ echo“Thisisanexampleofafunction” } count=1 while[$count-le5] do func1 count=$[$count+1] done echo“Thisistheendoftheloop” func1 echo“Nowthisistheendofthescript” $ $./test1 Thisisanexampleofafunction Thisisanexampleofafunction Thisisanexampleofafunction Thisisanexampleofafunction Thisisanexampleofafunction Thisistheendoftheloop Thisisanexampleofafunction Nowthisistheendofthescript $ Each time you reference the func1 function name, the bash shell returns to the func1 functiondefinitionandexecutesanycommandsyoudefinedthere. The function definition doesn’t have to be the first thing in your shell script, but be careful.Ifyouattempttouseafunctionbeforeit’sdefined,you’llgetanerrormessage: $cattest2 #!/bin/bash #usingafunctionlocatedinthemiddleofascript count=1 echo“Thislinecomesbeforethefunctiondefinition” functionfunc1{ echo“Thisisanexampleofafunction” } while[$count-le5] do func1 count=$[$count+1] done echo“Thisistheendoftheloop” func2 echo“Nowthisistheendofthescript” functionfunc2{ echo“Thisisanexampleofafunction” } $ $./test2 Thislinecomesbeforethefunctiondefinition Thisisanexampleofafunction Thisisanexampleofafunction Thisisanexampleofafunction Thisisanexampleofafunction Thisisanexampleofafunction Thisistheendoftheloop ./test2:func2:commandnotfound Nowthisistheendofthescript $ Thefirstfunction, func1,wasdefinedafteracoupleofstatementsinthescript,whichis perfectlyfine.Whenthe func1 function was used in the script, the shell knew where to findit. However,thescriptattemptedtousethefunc2functionbeforeitwasdefined.Becausethe func2 function wasn’t defined, when the script reached the place where we used it, it producedanerrormessage. You also need to be careful about your function names. Remember, each function name mustbeunique,oryou’llhaveaproblem.Ifyouredefineafunction,thenewdefinition overridestheoriginalfunctiondefinition,withoutproducinganyerrormessages: $cattest3 #!/bin/bash #testingusingaduplicatefunctionname functionfunc1{ echo“Thisisthefirstdefinitionofthefunctionname” } func1 functionfunc1{ echo“Thisisarepeatofthesamefunctionname” } func1 echo“Thisistheendofthescript” $ $./test3 Thisisthefirstdefinitionofthefunctionname Thisisarepeatofthesamefunctionname Thisistheendofthescript $ Theoriginaldefinitionofthe func1functionworksfine,butaftertheseconddefinitionof thefunc1function,anysubsequentusesofthefunctionusetheseconddefinition. ReturningaValue The bash shell treats functions like mini-scripts, complete with an exit status (see Chapter11). There are three different ways you can generate an exit status for your functions. Thedefaultexitstatus Bydefault,theexitstatusofafunctionistheexitstatusreturnedbythelastcommandin thefunction.Afterthefunctionexecutes,youusethestandard$?variabletodeterminethe exitstatusofthefunction: $cattest4 #!/bin/bash #testingtheexitstatusofafunction func1(){ echo“tryingtodisplayanon-existentfile” ls-lbadfile } echo“testingthefunction:“ func1 echo“Theexitstatusis:$?” $ $./test4 testingthefunction: tryingtodisplayanon-existentfile ls:badfile:Nosuchfileordirectory Theexitstatusis:1 $ The exit status of the function is 1 because the last command in the function failed. However, you have no way of knowing if any of the other commands in the function completedsuccessfullyornot.Lookatthisexample: $cattest4b #!/bin/bash #testingtheexitstatusofafunction func1(){ ls-lbadfile echo“Thiswasatestofabadcommand” } echo“testingthefunction:” func1 echo“Theexitstatusis:$?” $ $./test4b testingthefunction: ls:badfile:Nosuchfileordirectory Thiswasatestofabadcommand Theexitstatusis:0 $ Thistime,becausethefunctionendedwithanechostatementthatcompletedsuccessfully, the exit status of the function is 0, even though one of the commands in the function failed.Usingthedefaultexitstatusofafunctioncanbeadangerouspractice.Fortunately, wehaveacoupleofothersolutions. Usingthereturncommand Thebashshellusesthereturncommandtoexitafunctionwithaspecificexitstatus.The returncommandallowsyoutospecifyasingleintegervaluetodefinethefunctionexit status, providing an easy way for you to programmatically set the exit status of your function: $cattest5 #!/bin/bash #usingthereturncommandinafunction functiondbl{ read-p“Enteravalue:”value echo“doublingthevalue” return$[$value*2] } dbl echo“Thenewvalueis$?” $ The dblfunctiondoublestheintegervaluecontainedinthe $valuevariableprovidedby the user input. It then returns the result using the return command, which the script displaysusingthe$?variable. Youmustbecareful,however,whenusingthistechniquetoreturnavaluefromafunction. Keepthefollowingtwotipsinmindtoavoidproblems: Remembertoretrievethereturnvalueassoonasthefunctioncompletes. Rememberthatanexitstatusmustbeintherangeof0to255. Ifyouexecuteanyothercommandsbeforeretrievingthevalueofthefunction,usingthe $? variable, the return value from the function is lost. Remember that the $? variable returnstheexitstatusofthelastexecutedcommand. Thesecondproblemdefinesalimitationforusingthisreturnvaluetechnique.Becausean exitstatusmustbelessthan256,theresultofyourfunctionmustproduceanintegervalue lessthan256.Anyvalueoverthatreturnsanerrorvalue: $./test5 Enteravalue:200 doublingthevalue Thenewvalueis1 $ Youcannotusethisreturnvaluetechniqueifyouneedtoreturneitherlargerintegervalues or a string value. Instead, you need to use another method, demonstrated in the next section. Usingfunctionoutput Justasyoucancapturetheoutputofacommandtoashellvariable,youcanalsocapture theoutputofafunctiontoashellvariable.Youcanusethistechniquetoretrieveanytype ofoutputfromafunctiontoassigntoavariable: result=‘dbl’ Thiscommandassignstheoutputofthedblfunctiontothe$resultshellvariable.Here’s anexampleofusingthismethodinascript: $cattest5b #!/bin/bash #usingtheechotoreturnavalue functiondbl{ read-p“Enteravalue:”value echo$[$value*2] } result=$(dbl) echo“Thenewvalueis$result” $ $./test5b Enteravalue:200 Thenewvalueis400 $ $./test5b Enteravalue:1000 Thenewvalueis2000 $ Thenewfunctionnowusesan echostatementtodisplaytheresultofthecalculation.The scriptjustcapturestheoutputofthe dblfunctioninsteadoflookingattheexitstatusfor theanswer. There’sasubtletrickthatthisexampledemonstrates.You’llnoticethatthe db1function really outputs two messages. The read command outputs a short message querying the userforthevalue.Thebashshellscriptissmartenoughtonotconsiderthisaspartofthe STDOUT output and ignores it. If you had used an echo statement to produce this query messagetotheuser,itwouldhavebeencapturedbytheshellvariableaswellastheoutput value. Note Usingthistechnique,youcanalsoreturnfloatingpointandstringvalues,makingthis anextremelyversatilemethodforreturningvaluesfromfunctions. UsingVariablesinFunctions You might have noticed in the test5 example in the previous section that we used a variablecalled $valuewithinthefunctiontoholdthevaluethatitprocessed.Whenyou use variables in your functions, you need to be somewhat careful about how you define andhandlethem.Thisisacommoncauseofproblemsinshellscripts.Thissectiongoes over a few techniques for handling variables both inside and outside your shell script functions. Passingparameterstoafunction Asmentionedearlierinthe“ReturningaValue”section,thebashshelltreatsfunctionsjust likemini-scripts.Thismeansthatyoucanpassparameterstoafunctionjustlikearegular script(seeChapter14). Functions can use the standard parameter environment variables to represent any parameters passed to the function on the command line. For example, the name of the functionisdefinedinthe $0variable,andanyparametersonthefunctioncommandline aredefinedusingthevariables $1,$2,andsoon.Youcanalsousethespecialvariable $# todeterminethenumberofparameterspassedtothefunction. Whenspecifyingthefunctioninyourscript,youmustprovidetheparametersonthesame commandlineasthefunction,likethis: func1$value110 The function can then retrieve the parameter values using the parameter environment variables.Here’sanexampleofusingthismethodtopassvaluestoafunction: $cattest6 #!/bin/bash #passingparameterstoafunction functionaddem{ if[$#-eq0]||[$#-gt2] then echo-1 elif[$#-eq1] then echo$[$1+$1] else echo$[$1+$2] fi } echo-n“Adding10and15:“ value=$(addem1015) echo$value echo-n“Let’stryaddingjustonenumber:“ value=$(addem10) echo$value echo-n“Nowtryingaddingnonumbers:“ value=$(addem) echo$value echo-n“Finally,tryaddingthreenumbers:“ value=$(addem101520) echo$value $ $./test6 Adding10and15:25 Let’stryaddingjustonenumber:20 Nowtryingaddingnonumbers:-1 Finally,tryaddingthreenumbers:-1 $ The addemfunctioninthe text6scriptfirstchecksthenumberofparameterspassedtoit by the script. If there aren’t any parameters, or if there are more than two parameters, addem returns a value of -1. If there’s just one parameter, addem adds the parameter to itselffortheresult.Iftherearetwoparameters,addemaddsthemtogetherfortheresult. Because the function uses the special parameter environment variables for its own parameter values, it can’t directly access the script parameter values from the command lineofthescript.Thefollowingexamplefails: $catbadtest1 #!/bin/bash #tryingtoaccessscriptparametersinsideafunction functionbadfunc1{ echo$[$1*$2] } if[$#-eq2] then value=$(badfunc1) echo“Theresultis$value” else echo“Usage:badtest1ab” fi $ $./badtest1 Usage:badtest1ab $./badtest11015 ./badtest1:*:syntaxerror:operandexpected(errortokenis“* “) Theresultis $ Even though the function uses the $1 and $2 variables, they aren’t the same $1 and $2 variablesavailableinthemainpartofthescript.Instead,ifyouwanttousethosevaluesin yourfunction,youhavetomanuallypassthemwhenyoucallthefunction: $cattest7 #!/bin/bash #tryingtoaccessscriptparametersinsideafunction functionfunc7{ echo$[$1*$2] } if[$#-eq2] then value=$(func7$1$2) echo“Theresultis$value” else echo“Usage:badtest1ab” fi $ $./test7 Usage:badtest1ab $./test71015 Theresultis150 $ Bypassingthe $1and$2variablestothefunction,theybecomeavailableforthefunction touse,justlikeanyotherparameter. Handlingvariablesinafunction One thing that causes problems for shell script programmers is the scope of a variable. The scope is where the variable is visible. Variables defined in functions can have a different scope than regular variables. That is, they can be hidden from the rest of the script. Functionsusetwotypesofvariables: Global Local Thefollowingsectionsdescribehowtousebothtypesofvariablesinyourfunctions. Globalvariables Globalvariablesarevariablesthatarevalidanywherewithintheshellscript.Ifyoudefine a global variable in the main section of a script, you can retrieve its value inside a function.Likewise,ifyoudefineaglobalvariableinsideafunction,youcanretrieveits valueinthemainsectionofthescript. Bydefault,anyvariablesyoudefineinthescriptareglobalvariables.Variablesdefined outsideofafunctioncanbeaccessedwithinthefunctionjustfine: $cattest8 #!/bin/bash #usingaglobalvariabletopassavalue functiondbl{ value=$[$value*2] } read-p“Enteravalue:”value dbl echo“Thenewvalueis:$value” $ $./test8 Enteravalue:450 Thenewvalueis:900 $ The$valuevariableisdefinedoutsideofthefunctionandassignedavalueoutsideofthe function.Whenthe dblfunctioniscalled,thevariableanditsvaluearestillvalidinside thefunction.Whenthevariableisassignedanewvalueinsidethefunction,thatnewvalue isstillvalidwhenthescriptreferencesthevariable. Thiscanbeadangerouspractice,however,especiallyifyouintendtouseyourfunctions indifferentshellscripts.Itrequiresthatyouknowexactlywhatvariablesareusedinthe function,includinganyvariablesusedtocalculatevaluesnotreturnedtothescript.Here’s anexampleofhowthingscangobad: $catbadtest2 #!/bin/bash #demonstratingabaduseofvariables functionfunc1{ temp=$[$value+5] result=$[$temp*2] } temp=4 value=6 func1 echo“Theresultis$result” if[$temp-gt$value] then echo“tempislarger” else echo“tempissmaller” fi $ $./badtest2 Theresultis22 tempislarger $ Because the $temp variable was used in the function, its value is compromised in the script, producing a result that you may not have intended. There’s an easy way to solve thisprobleminyourfunctions,asshowninthenextsection. Localvariables Instead of using global variables in functions, any variables that the function uses internally can be declared as local variables. To do that, just use the local keyword in frontofthevariabledeclaration: localtemp Youcanalsousethelocalkeywordinanassignmentstatementwhileassigningavalueto thevariable: localtemp=$[$value+5] The local keyword ensures that the variable is limited to only within the function. If a variablewiththesamenameappearsoutsidethefunctioninthescript,theshellkeepsthe two variable values separate. Now you can easily keep your function variables separate fromyourscriptvariablesandshareonlytheonesyouwanttoshare: $cattest9 #!/bin/bash #demonstratingthelocalkeyword functionfunc1{ localtemp=$[$value+5] result=$[$temp*2] } temp=4 value=6 func1 echo“Theresultis$result” if[$temp-gt$value] then echo“tempislarger” else echo“tempissmaller” fi $ $./test9 Theresultis22 tempissmaller $ Now when you use the $temp variable within the func1 function, it doesn’t affect the valueassignedtothe$tempvariableinthemainscript. ArrayVariablesandFunctions Chapter6discussedanadvancedwayofallowingasinglevariabletoholdmultiplevalues byusingarrays.Usingarrayvariablevalueswithfunctionsisalittletricky,andthereare somespecialconsiderations.Thissectiondescribesatechniquethatallowsyoutodothat. Passingarraystofunctions Theartofpassinganarrayvariabletoascriptfunctioncanbeconfusing.Ifyoutrytopass thearrayvariableasasingleparameter,itdoesn’twork: $catbadtest3 #!/bin/bash #tryingtopassanarrayvariable functiontestit{ echo“Theparametersare:$@” thisarray=$1 echo“Thereceivedarrayis${thisarray[*]}” } myarray=(12345) echo“Theoriginalarrayis:${myarray[*]}” testit$myarray $ $./badtest3 Theoriginalarrayis:12345 Theparametersare:1 Thereceivedarrayis1 $ Ifyoutryusingthearrayvariableasafunctionparameter,thefunctiononlypicksupthe firstvalueofthearrayvariable. Tosolvethisproblem,youmustdisassemblethearrayvariableintoitsindividualvalues andusethevaluesasfunctionparameters.Insidethefunction,youcanreassembleallthe parametersintoanewarrayvariable.Here’sanexampleofdoingthis: $cattest10 #!/bin/bash #arrayvariabletofunctiontest functiontestit{ localnewarray newarray=(;‘echo“$@”’) echo“Thenewarrayvalueis:${newarray[*]}” } myarray=(12345) echo“Theoriginalarrayis${myarray[*]}” testit${myarray[*]} $ $./test10 Theoriginalarrayis12345 Thenewarrayvalueis:12345 $ Thescriptusesthe$myarrayvariabletoholdalltheindividualarrayvaluestoplacethem all on the command line for the function. The function then rebuilds the array variable fromthecommandlineparameters.Onceinsidethefunction,thearraycanbeusedjust likeanyotherarray: $cattest11 #!/bin/bash #addingvaluesinanarray functionaddarray{ localsum=0 localnewarray newarray=($(echo“$@”)) forvaluein${newarray[*]} do sum=$[$sum+$value] done echo$sum } myarray=(12345) echo“Theoriginalarrayis:${myarray[*]}” arg1=$(echo${myarray[*]}) result=$(addarray$arg1) echo“Theresultis$result” $ $./test11 Theoriginalarrayis:12345 Theresultis15 $ The addarray function iterates through the array values, adding them together. You can putanynumberofvaluesinthe myarrayarrayvariable,andthe addarrayfunctionadds them. Returningarraysfromfunctions Passinganarrayvariablefromafunctionbacktotheshellscriptusesasimilartechnique. The function uses an echo statement to output the individual array values in the proper order,andthescriptmustreassemblethemintoanewarrayvariable: $cattest12 #!/bin/bash #returninganarrayvalue functionarraydblr{ localorigarray localnewarray localelements locali origarray=($(echo“$@”)) newarray=($(echo“$@”)) elements=$[$#-1] for((i=0;i<=$elements;i++)) { newarray[$i]=$[${origarray[$i]}*2] } echo${newarray[*]} } myarray=(12345) echo“Theoriginalarrayis:${myarray[*]}” arg1=$(echo${myarray[*]}) result=($(arraydblr$arg1)) echo“Thenewarrayis:${result[*]}” $ $./test12 Theoriginalarrayis:12345 Thenewarrayis:246810 Thescriptpassesthearrayvalue,usingthe$arg1variabletothearraydblrfunction.The arraydblrfunctionreassemblesthearrayintoanewarrayvariable,anditmakesacopy fortheoutputarrayvariable.Ittheniteratesthroughtheindividualarrayvariablevalues, doubleseachvalue,andplacesitintothecopyofthearrayvariableinthefunction. Thearraydblrfunctionthenusestheechostatementtooutputtheindividualvaluesofthe arrayvariablevalues.Thescriptusestheoutputofthearraydblrfunctiontoreassemblea newarrayvariablewiththevalues. FunctionRecursion One feature that local function variables provide is self-containment. A self-contained functiondoesn’tuseanyresourcesoutsideofthefunction,otherthanwhatevervariables thescriptpassestoitinthecommandline. Thisfeatureenablesthefunctiontobecalledrecursively,whichmeansthatthefunction calls itself to reach an answer. Usually, a recursive function has a base value that it eventually iterates down to. Many advanced mathematical algorithms use recursion to reduceacomplexequationdownonelevelrepeatedly,untiltheygettotheleveldefined bythebasevalue. The classic example of a recursive algorithm is calculating factorials. A factorial of a numberisthevalueoftheprecedingnumbersmultipliedwiththenumber.Thus,tofind thefactorialof5,you’dperformthefollowingequation: 5!=1*2*3*4*5=120 Usingrecursion,theequationisreduceddowntothefollowingformat: x!=x*(x-1)! or in English, the factorial of x is equal to x times the factorial of x-1. This can be expressedinasimplerecursivescript: functionfactorial{ if[$1-eq1] then echo1 else localtemp=$[$1-1] localresult=‘factorial$temp’ echo$[$result*$1] fi } Thefactorialfunctionusesitselftocalculatethevalueforthefactorial: $cattest13 #!/bin/bash #usingrecursion functionfactorial{ if[$1-eq1] then echo1 else localtemp=$[$1-1] localresult=$(factorial$temp) echo$[$result*$1] fi } read-p“Entervalue:”value result=$(factorial$value) echo“Thefactorialof$valueis:$result” $ $./test13 Entervalue:5 Thefactorialof5is:120 $ Usingthefactorialfunctioniseasy.Havingcreatedafunctionlikethis,youmaywantto useitinotherscripts.Next,welookathowtodothatefficiently. CreatingaLibrary It’seasytoseehowfunctionscanhelpsavetypinginasinglescript,butwhatifyoujust happen to use the same single code block between scripts? It’s obviously challenging if youhavetodefinethesamefunctionineachscript,onlytouseitonetimeineachscript. There’sasolutionforthatproblem!Thebashshellallowsyoutocreatealibraryfilefor yourfunctionsandthenreferencethatsinglelibraryfileinasmanyscriptsasyouneedto. Thefirststepintheprocessistocreateacommonlibraryfilethatcontainsthefunctions you need in your scripts. Here’s a simple library file called myfuncs that defines three simplefunctions: $catmyfuncs #myscriptfunctions functionaddem{ echo$[$1+$2] } functionmultem{ echo$[$1*$2] } functiondivem{ if[$2-ne0] then echo$[$1/$2] else echo-1 fi } $ Thenextstepistoincludethemyfuncslibraryfileinyourscriptfilesthatwanttouseany ofthefunctions.Thisiswherethingsgettricky. The problem is with the scope of shell functions. As with environment variables, shell functions are valid only for the shell session in which you define them. If you run the myfuncs shell script from your shell command line interface prompt, the shell creates a new shell and runs the script in that new shell. This defines the three functions for that shell, but when you try to run another script that uses those functions, they aren’t available. Thisappliestoscriptsaswell.Ifyoutrytojustrunthelibraryfileasaregularscriptfile, thefunctionsdon’tappearinyourscript: $catbadtest4 #!/bin/bash #usingalibraryfilethewrongway ./myfuncs result=$(addem1015) echo“Theresultis$result” $ $./badtest4 ./badtest4:addem:commandnotfound Theresultis $ The key to using function libraries is the source command. The source command executes commands within the current shell context instead of creating a new shell to execute them. You use the source command to run the library file script inside of your shellscript.Thismakesthefunctionsavailabletothescript. Thesourcecommandhasashortcutalias,calledthedotoperator.Tosourcethe myfuncs libraryfileinashellscript,youjustneedtoaddthefollowingline: ../myfuncs Thisexampleassumesthatthe myfuncslibraryfileislocatedinthesamedirectoryasthe shell script. If not, you need to use the appropriate path to access the file. Here’s an exampleofcreatingascriptthatusesthemyfuncslibraryfile: $cattest14 #!/bin/bash #usingfunctionsdefinedinalibraryfile ../myfuncs value1=10 value2=5 result1=$(addem$value1$value2) result2=$(multem$value1$value2) result3=$(divem$value1$value2) echo“Theresultofaddingthemis:$result1” echo“Theresultofmultiplyingthemis:$result2” echo“Theresultofdividingthemis:$result3” $ $./test14 Theresultofaddingthemis:15 Theresultofmultiplyingthemis:50 Theresultofdividingthemis:2 $ Thescriptsuccessfullyusesthefunctionsdefinedinthemyfuncslibraryfile. UsingFunctionsontheCommandLine You can use script functions to create some pretty complex operations. Sometimes, it would be nice to be able to use these functions directly on the command line interface prompt. Justasyoucanuseascriptfunctionasacommandinashellscript,youcanalsousea scriptfunctionasacommandinthecommandlineinterface.Thisisanicefeaturebecause afteryoudefinethefunctionintheshell,youcanuseitfromanydirectoryonthesystem; youdon’thavetoworryaboutascriptbeinginyourPATHenvironmentvariable.Thetrick istogettheshelltorecognizethefunction.Youcandothatinacoupleofways. Creatingfunctionsonthecommandline Because the shell interprets commands as you type them, you can define a function directlyonthecommandline.Youcandothatintwoways. Thefirstmethoddefinesthefunctionallononeline: $functiondivem{echo$[$1/$2];} $divem1005 20 $ When you define the function on the command line, you must remember to include a semicolonattheendofeachcommand,sotheshellknowswheretoseparatecommands: $functiondoubleit{read-p“Entervalue:”value;echo$[ $value*2];} $ $doubleit Entervalue:20 40 $ The other method is to use multiple lines to define the function. When you do that, the bash shell uses the secondary prompt to prompt you for more commands. Using this method,youdon’tneedtoplaceasemicolonattheendofeachcommand;justpressthe Enterkey: $functionmultem{ >echo$[$1*$2] >} $multem25 10 $ When you use the brace at the end of the function, the shell knows that you’re finished definingthefunction. Caution Beextremelycarefulwhencreatingfunctionsonthecommandline.Ifyouusea functionwiththesamenameasabuilt-incommandoranothercommand,the functionoverridestheoriginalcommand. Definingfunctionsinthe.bashrcfile The obvious downside to defining shell functions directly on the command line is that whenyouexittheshell,yourfunctiondisappears.Forcomplexfunctions,thiscanbecome aproblem. A much simpler method is to define the function in a place where it is reloaded by the shelleachtimeyoustartanewshell. Thebestplacetodothatisthe.bashrcfile.Thebashshelllooksforthisfileinyourhome directoryeachtimeitstarts,whetherinteractivelyorastheresultofstartinganewshell fromwithinanexistingshell. Directlydefiningfunctions You can define the functions directly in the .bashrc file in your home directory. Most Linux distributions already define some things in the .bashrc file, so be careful not to removethoseitems.Justaddyourfunctionstothebottomoftheexistingfile.Here’san exampleofdoingthat: $cat.bashrc #.bashrc #Sourceglobaldefinitions if[-r/etc/bashrc];then ./etc/bashrc fi functionaddem{ echo$[$1+$2] } $ Thefunctiondoesn’ttakeeffectuntilthenexttimeyoustartanewbashshell.Afteryou dothat,youcanusethefunctionanywhereonthesystem. Sourcingfunctionfiles Justasinashellscript,youcanusethe sourcecommand(oritsaliasthedotoperator)to addfunctionsfromanexistinglibraryfiletoyour.bashrcscript: $cat.bashrc #.bashrc #Sourceglobaldefinitions if[-r/etc/bashrc];then ./etc/bashrc fi ./home/rich/libraries/myfuncs $ Makesurethatyouincludetheproperpathnametoreferencethelibraryfileforthebash shelltofind.Thenexttimeyoustartashell,allthefunctionsinyourlibraryareavailable atthecommandlineinterface: $addem105 15 $multem105 50 $divem105 2 $ Even better, the shell also passes any defined functions to child shell processes so your functionsareautomaticallyavailableforanyshellscriptsyourunfromyourshellsession. Youcantestthisbywritingascriptthatusesthefunctionswithoutdefiningorsourcing them: $cattest15 #!/bin/bash #usingafunctiondefinedinthe.bashrcfile value1=10 value2=5 result1=$(addem$value1$value2) result2=$(multem$value1$value2) result3=$(divem$value1$value2) echo“Theresultofaddingthemis:$result1” echo“Theresultofmultiplyingthemis:$result2” echo“Theresultofdividingthemis:$result3” $ $./test15 Theresultofaddingthemis:15 Theresultofmultiplyingthemis:50 Theresultofdividingthemis:2 $ Evenwithoutsourcingthelibraryfile,thefunctionsworkedperfectlyintheshellscript. FollowingaPracticalExample There’smuchmoretousingfunctionsthanjustcreatingyourownfunctionstoworkwith. In the open source world, code sharing is key, and that also applies to shell script functions. Quite a few different shell script functions are available for you to download anduseinyourownapplications. Thissectionwalksthroughdownloading,installing,andusingtheGNUshtoolshellscript function library. The shtool library provides some simple shell script functions for performingeverydayshellfunctions,suchasworkingwithtemporaryfilesandfoldersor formattingoutputtodisplay. Downloadingandinstalling The first step in the process is to download and install the GNU shtool library to your systemsoyoucanusethelibraryfunctionsinyourownshellscripts.Todothat,youneed to use an FTP client program or a browser in a graphical desktop. Use this URL to downloadtheshtoolpackage: ftp://ftp.gnu.org/gnu/shtool/shtool-2.0.8.tar.gz This downloads the file shtool-2.0.8.tar.gz to the download folder. From there, you can use the cp command line tool or the graphical file manager tool in your Linux distribution(suchasNautilusinUbuntu)tocopythefiletoyourHomefolder. AfteryoucopythefiletoyourHomefolder,youcanextractitusingthetarcommand: tar-zxvfshtool-2.0.8.tar.gz This extracts the package files into a folder named shtool-2.0.8.Nowyou’rereadyto buildtheshellscriptlibraryfile. Buildingthelibrary TheshtooldistributionfilemustbeconfiguredforyourspecificLinuxenvironment.Todo that, it uses standard configure and make commands, commonly used in the C programmingenvironment.Tobuildthelibraryfile,youjustneedtoruntwocommands: $./confifgure $make Theconfigurecommandchecksthesoftwarenecessarytobuildtheshtoollibraryfile.As it finds the tools it needs, it modifies the configuration file with the proper paths to the tools. Themakecommandrunsthroughthestepstobuildtheshtoollibraryfile.Theresultingfile (shtool) is the full library package file. You can test the library file using the make commandaswell: $maketest Runningtestsuite: echo………..ok mdate……….ok table……….ok prop………..ok move………..ok install……..ok mkdir……….ok mkln………..ok mkshadow…….ok fixperm……..ok rotate………ok tarball……..ok subst……….ok platform…….ok arx…………ok slo…………ok scpp………..ok version……..ok path………..ok OK:passed:19/19 $ Thetestmodetestsallthefunctionsavailableintheshtoollibrary.Ifallpass,thenyou’re ready to install the library into a common location on your Linux system so all your scripts can use it. To do that, you can use the install option of the make command. However,youneedtobeloggedinastherootuseraccounttorunit: $su Password: #makeinstall ./shtoolmkdir-f-p-m755/usr/local ./shtoolmkdir-f-p-m755/usr/local/bin ./shtoolmkdir-f-p-m755/usr/local/share/man/man1 ./shtoolmkdir-f-p-m755/usr/local/share/aclocal ./shtoolmkdir-f-p-m755/usr/local/share/shtool … ./shtoolinstall-c-m644sh.version/usr/local/share/shtool/sh.version ./shtoolinstall-c-m644sh.path/usr/local/share/shtool/sh.path # Nowyou’rereadytostartusingthefunctionsinyourownshellscripts! Theshtoollibraryfunctions Theshtoollibraryprovidesquiteafewfunctionsthatcancomeinhandywhenworking withshellscripts.Table17.1showsthefunctionsavailableinthelibrary. Table17.1TheshtoolLibraryFunctions Function Description Arx Createsanarchivewithextendedfeatures Echo Displaysthestringvaluewithconstructexpansion fixperm Changesfilepermissionsinsideafoldertree install Installsascriptorfile mdate Displaysmodificationtimeofafileordirectory mkdir Createsoneormoredirectories Mkln Createsalinkusingrelativepaths mkshadow move Path platform Prop rotate Scpp Slo Subst Table tarball version Createsashadowtree Movesfileswithsubstitution Workswithprogrampaths Displaystheplatformidentity Displaysananimatedprogresspropeller Rotateslogfiles ThesharingCpre-processor Separateslinkeroptionsbylibraryclass Usessedsubstitutionoperations Displaysfield-separateddatainatableformat Createstarfilesfromfilesandfolders Createsaversioninformationfile Eachoftheshtoolfunctionshaslotsofoptionsandargumentsthatyoucanusetomodify howitworks.Here’stheformattouseashtoolfunction: shtool[options][function[options][args]] Usingthelibrary You can use the shtool functions directly from the command line or from within your shellscripts.Here’sanexampleofusingtheplatformfunctioninsideashellscript: $cattest16 #!/bin/bash shtoolplatform $./test16 Ubuntu14.04(iX86) $ The platformfunctionreturnstheLinuxdistributionandtheCPUhardwarethatthehost systemisusing.Oneofmyfavoritesisthe propfunction.Itcreatesaspinningpropeller fromalternatingthe∖,|,/,and–characterswhilesomethingisprocessing.That’sagreat tool to help show your shell script users that something is happening in the background whilethescriptisrunning. Tousethe propfunction,youjustpipetheoutputofthefunctionyouwanttomonitorto theshtoolscript: $ls–al/usr/bin|shtoolprop–p“waiting…” waiting… $ Thepropfunctionalternatesbetweenthepropellercharacterstoindicatethatsomethingis happening.Inthiscase,it’stheoutputfromthe lscommand.Howmuchofthatyousee dependsonhowfastyourCPUcanlistoutallthefilesinthe /usr/binfolder!The –p optionallowsyoutocustomizetheoutputtextthatappearsbeforethepropellercharacters. Nowthat’sgettingfancy! Summary Shellscriptfunctionsallowyoutoplacescriptcodethat’srepeatedthroughoutthescript in a single place. Instead of having to rewrite blocks of code, you can create a function containing the code block and then just reference the function name in your script. The bashshelljumpstothefunctioncodeblockwheneveritseesthefunctionnameusedinthe script. Youcanevencreatescriptfunctionsthatreturnvalues.Thisallowsyoutocreatefunctions that interact with the script, returning both numeric and character data. Script functions can return numeric data by using the exit status of the last command in the function or usingthereturncommand.Thereturncommandallowsyoutoprogrammaticallysetthe exitstatusofyourfunctiontoaspecificvaluebasedontheresultsofthefunction. Functionscanalsoreturnvaluesusingthestandard echostatement.Youcancapturethe output data using the backtick character as you would any other shell command. This enablesyoutoreturnanytypeofdatafromafunction,includingstringsandfloating-point numbers. You can use shell variables within your functions, assigning values to variables and retrieving values from existing variables. This allows you to pass any type of data both intoandoutofascriptfunctionfromthemainscriptprogram.Functionsalsoallowyouto define local variables, which are accessible only from within the function code block. Local variables allow you to create self-contained functions, which don’t interfere with anyvariablesorprocessesusedinthemainshellscript. Functionscanalsocallotherfunctions,includingthemselves.Whenafunctioncallsitself, itiscalledrecursion.Arecursivefunctionoftenhasabasevaluethatistheterminalvalue of the function. The function continues to call itself with a decreasing parameter value untilthebasevalueisreached. If you use lots of functions in your shell scripts, you can create library files of script functions. The library files can be included in any shell script file by using the source command,oritsalias,thedotoperator.Thisiscalledsourcingthelibraryfile.Theshell doesn’trunthelibraryfilebutmakesthefunctionsavailablewithintheshellthatrunsthe script.Youcanusethissametechniquetocreatefunctionsthatyoucanuseonthenormal shellcommandline.Youcaneitherdefinefunctionsdirectlyonthecommandlineoryou can add them to your .bashrc file so they are available for each new shell session you start. This is a handy way to create utilities that can be used no matter what your PATH environmentvariableissetto. Thenextchapterdiscussestheuseoftextgraphicsinyourscripts.Inthisdayofmodern graphical interfaces, sometimes a plain text interface just doesn’t cut it. The bash shell providessomeeasywaysforyoutoincorporatesimplegraphicsfeaturesinyourscriptsto helpspicethingsup. Chapter18 WritingScriptsforGraphicalDesktops InThisChapter 1. Creatingtextmenus 2. Buildingtextwindowwidgets 3. AddingXWindowgraphics Overtheyears,shellscriptshaveacquiredareputationforbeingdullandboring. Thisdoesn’thavetobethecase,however,ifyouplanonrunningyourscriptsina graphicalenvironment.Thereareplentyofwaystointeractwithyourscriptuserthat don’trelyonthereadandechostatements.Thischapterdivesintoafewdifferent methodsyoucanusetohelpaddlifetoyourinteractivescriptssotheydon’tlookso old-fashioned. CreatingTextMenus Themostcommonwaytocreateaninteractiveshellscriptistoutilizeamenu.Offering your customers a choice of various options helps guide them through exactly what the scriptcanandcan’tdo. Menuscriptsusuallyclearthedisplayareaandthenshowalistofoptionsavailable.The customercanselectanoptionbypressinganassociatedletterornumberassignedtoeach option.Figure18.1showsthelayoutofasamplemenu. Figure18.1Displayingamenufromashellscript The core of a shell script menu is the case command (see Chapter 12). The case command performs specific commands, depending on what character your customer selectsfromthemenu. The following sections walk you through the steps you should follow to create a menubasedshellscript. Createthemenulayout The first step in creating a menu is, obviously, to determine what elements you want to appearinthemenuandlaythemoutthewaythatyouwantthemtoappear. Before creating the menu, it’s usually a good idea to clear the monitor display. This enablesyoutodisplayyourmenuinacleanenvironmentwithoutdistractingtext. The clear command uses the terminfo data of your terminal session (see Chapter 2) to clearanytextthatappearsonthemonitor.Aftertheclearcommand,youcanusetheecho commandtodisplayyourmenuelements. Bydefault,the echocommandcanonlydisplayprintabletextcharacters.Whencreating menu items, it’s often helpful to use nonprintable items, such as the tab and newline characters. To include these characters in your echo command, you must use the -e option.Thus,thecommand: echo-e“1.\tDisplaydiskspace” resultsintheoutputline: 1.Displaydiskspace This greatly helps in formatting the layout of the menu items. With just a few echo commands,youcancreateareasonable-lookingmenu: clear echo echo-e“\t\t\tSysAdminMenu\n” echo-e“\t1.Displaydiskspace” echo-e“\t2.Displayloggedonusers” echo-e“\t3.Displaymemoryusage” echo-e“\t0.Exitmenu\n\n” echo–en“\t\tEnteroption:“ The -enoptiononthelastlinedisplaysthelinewithoutaddingthenewlinecharacterat theend.Thisgivesthemenuamoreprofessionallook,becausethecursorstaysattheend ofthelinewaitingforthecustomer’sinput. Thelastpartofcreatingthemenuistoretrievetheinputfromthecustomer.Thisisdone usingthereadcommand(seeChapter14).Becauseweexpectonlysingle-characterinput, the nice thing to do is to use the -n option in the read command to retrieve only one character. This allows the customer to enter a number without having to press the Enter key: read-n1option Next,youneedtocreateyourmenufunctions. Createthemenufunctions Shell script menu options are easier to create as a group of separate functions. This enablesyoutocreateasimple,concisecasecommandthatiseasytofollow. Todothat,youneedtocreateseparateshellfunctionsforeachofyourmenuoptions.The first step in creating a menu shell script is to determine what functions you want your scripttoperformandlaythemoutasseparatefunctionsinyourcode. Itiscommonpracticetocreatestubfunctionsforfunctionsthataren’timplementedyet.A stubfunctionisafunctionthatdoesn’tcontainanycommandsyetorpossiblyjustanecho statementindicatingwhatshouldbethereeventually: functiondiskspace{ clear echo“Thisiswherethediskspacecommandswillgo” } Thisenablesyourmenutooperatesmoothlywhileyouworkontheindividualfunctions. You don’t have to code all the functions for your menu to work. You’ll notice that the functionstartsoutwiththe clearcommand.Thisenablesyoutostartthefunctionona cleanmonitorscreen,withoutthemenushowing. One thing that helps out in the shell script menu is to create the menu layout itself as a function: functionmenu{ clear echo echo-e“\t\t\tSysAdminMenu\n” echo-e“\t1.Displaydiskspace” echo-e“\t2.Displayloggedonusers” echo-e“\t3.Displaymemoryusage” echo-e“\t0.Exitprogram\n\n” echo-en“\t\tEnteroption:“ read-n1option } Thisenablesyoutoeasilyredisplaythemenuatanytimejustbycallingthemenufunction. Addthemenulogic Now that you have your menu layout and your functions, you just need to create the programming logic to put the two together. As mentioned, this requires the case command. The case command should call the appropriate function according to the character selection expected from the menu. It’s always a good idea to use the default case commandcharacter(theasterisk)tocatchanyincorrectmenuentries. Thefollowingcodeillustratestheuseofthecasecommandinatypicalmenu: menu case$optionin 0) break;; 1) diskspace;; 2) whoseon;; 3) memusage;; *) clear echo“Sorry,wrongselection”;; esac Thiscodefirstusesthe menufunctiontoclearthemonitorscreenanddisplaythemenu. The readcommandinthe menufunctionpausesuntilthecustomerhitsacharacteronthe keyboard.Afterthat’sbeendone,thecasecommandtakesover.Thecasecommandcalls theappropriatefunctionbasedonthereturnedcharacter.Afterthefunctioncompletes,the casecommandexits. Puttingitalltogether Nowthatyou’veseenallthepartsthatmakeupashellscriptmenu,let’sputthemtogether andseehowtheyallinteroperate.Here’sanexampleofafullmenuscript: $catmenu1 #!/bin/bash #simplescriptmenu functiondiskspace{ clear df-k } functionwhoseon{ clear who } functionmemusage{ clear cat/proc/meminfo } functionmenu{ clear echo echo-e“\t\t\tSysAdminMenu\n” echo-e“\t1.Displaydiskspace” echo-e“\t2.Displayloggedonusers” echo-e“\t3.Displaymemoryusage” echo-e“\t0.Exitprogram\n\n” echo-en“\t\tEnteroption:“ read-n1option } while[1] do menu case$optionin 0) break;; 1) diskspace;; 2) whoseon;; 3) memusage;; *) clear echo“Sorry,wrongselection”;; esac echo-en“\n\n\t\t\tHitanykeytocontinue” read-n1line done clear $ ThismenucreatesthreefunctionstoretrieveadministrativeinformationabouttheLinux system using common commands. It uses a while loop to continually loop through the menuuntilthecustomerselectsoption0,whichusesthe breakcommandtobreakoutof thewhileloop. You can use this same template to create any shell script menu interface. It provides a simplewaytointeractwithyourcustomers. Usingtheselectcommand Youmayhavenoticedthathalfthechallengeofcreatingatextmenuisjustcreatingthe menu layout and retrieving the answer that you enter. The bash shell provides a handy littleutilityforyouthatdoesallthisworkautomatically. The selectcommandallowsyoutocreateamenufromasinglecommandlineandthen retrieve the entered answer and automatically process it. The format of the select commandisasfollows: selectvariableinlist do commands done Thelistparameterisaspace-separatedlistoftextitemsthatbuildthemenu.Theselect commanddisplayseachiteminthelistasanumberedoptionandthendisplaysaspecial prompt,definedbythePS3environmentvariable,fortheselection. Here’sasimpleexampleoftheselectcommandinaction: $catsmenu1 #!/bin/bash #usingselectinthemenu functiondiskspace{ clear df-k } functionwhoseon{ clear who } functionmemusage{ clear cat/proc/meminfo } PS3=“Enteroption:“ selectoptionin“Displaydiskspace”“Displayloggedonusers”┐ “Displaymemoryusage”“Exitprogram” do case$optionin “Exitprogram”) break;; “Displaydiskspace”) diskspace;; “Displayloggedonusers”) whoseon;; “Displaymemoryusage”) memusage;; *) clear echo“Sorry,wrongselection”;; esac done clear $ The select statement must all be on one line in the code file. That’s indicated by the continuationcharacterinthelisting.Whenyouruntheprogram,itautomaticallyproduces thefollowingmenu: $./smenu1 1)Displaydiskspace3)Displaymemoryusage 2)Displayloggedonusers4)Exitprogram Enteroption: Whenyouusetheselectcommand,rememberthattheresultvaluestoredinthevariable istheentiretextstringandnotthenumberassociatedwiththemenuitem.Thetextstring valuesarewhatyouneedtocompareinyourcasestatements. DoingWindows Usingtextmenusisastepintherightdirection,butthere’sstillsomuchmissinginour interactivescripts,especiallyifwetrytocomparethemtothegraphicalWindowsworld. Fortunatelyforus,someveryresourcefulpeopleoutintheopensourceworldhavehelped usout. The dialog package is a nifty little tool originally created by Savio Lam and currently maintainedbyThomasE.Dickey.ThispackagerecreatesstandardWindowsdialogboxes inatextenvironmentusingANSIescapecontrolcodes.Youcaneasilyincorporatethese dialogboxesinyourshellscriptstointeractwithyourscriptusers.Thissectiondescribes thedialogpackageanddemonstrateshowtouseitinshellscripts. Note Thedialogpackageisn’tinstalledinallLinuxdistributionsbydefault.Ifit’snot installedbydefault,becauseofitspopularityit’salmostalwaysincludedinthe softwarerepository.CheckyourspecificLinuxdistributiondocumentationforhowto loadthedialogpackage.FortheUbuntuLinuxdistribution,thefollowingisthe commandlinecommandtoinstallit: 1. sudoapt-getinstalldialog Thatpackageinstallsthedialogpackageplustherequiredlibrariesforyoursystem. Thedialogpackage ThedialogcommandusescommandlineparameterstodeterminewhattypeofWindows widgettoproduce.AwidgetisthedialogpackagetermforatypeofWindowselement. ThedialogpackagecurrentlysupportsthetypesofwidgetsshowninTable18.1. Table18.1ThedialogWidgets Widget Description calendar Providesacalendarfromwhichtoselectadate checklist Displaysmultipleentrieswhereeachentrycanbeturnedonoroff form Allowsyoutobuildaformwithlabelsandtextfieldstobefilledout fselect Providesafileselectionwindowtobrowseforafile gauge Displaysametershowingapercentageofcompletion infobox Displaysamessagewithoutwaitingforaresponse inputbox Displaysasingletextformboxfortextentry inputmenu Providesaneditablemenu menu Displaysalistofselectionsfromwhichtochoose msgbox DisplaysamessageandrequirestheusertoselectanOKbutton pause Displaysametershowingthestatusofaspecifiedpauseperiod passwordbox Displaysasingletextboxthathidesenteredtext passwordform Displaysaformwithlabelsandhiddentextfields radiolist Providesagroupofmenuitemswhereonlyoneitemcanbeselected Displaystextfromafileinascrollwindowusingthetailcommand tailbox tailboxbg textbox timebox Sameastailbox,butoperatesinbackgroundmode Displaysthecontentsofafileinascrollwindow Providesawindowtoselectanhour,minute,andsecond yesno ProvidesasimplemessagewithYesandNobuttons AsyoucanseefromTable18.1,youcanchoosefromlotsofdifferentwidgets.Thiscan giveyourscriptsamoreprofessionallookwithverylittleeffort. Tospecifyaspecificwidgetonthecommandline,youneedtousethedoubledashformat: dialog—widgetparameters where widgetisthewidgetnameasseeninTable18.1,and parametersdefinesthesize ofthewidgetwindowandanytextrequiredforthewidget. Eachdialogwidgetprovidesoutputintwoforms: UsingSTDERR Usingtheexitcodestatus Theexitcodestatusofthedialogcommanddeterminesthebuttonselectedbytheuser.If anOKorYesbuttonisselected,the dialogcommandreturnsa0exitstatus.IfaCancel or No button is selected, the dialog command returns a 1 exit status. You can use the standard$?variabletodeterminewhichbuttonwasselectedinthedialogwidget. Ifawidgetreturnsanydata,suchasamenuselection,thedialogcommandsendsthedata toSTDERR.Youcanusethestandardbashshelltechniqueofredirectingthe STDERRoutput toanotherfileorfiledescriptor: dialog—inputbox“Enteryourage:”10202>age.txt Thiscommandredirectsthetextenteredinthetextboxtotheage.txtfile. Thefollowingsectionslookatsomeexamplesofthemorecommondialogwidgetsyou’ll useinyourshellscripts. Themsgboxwidget The msgboxwidgetisthemostcommontypeofdialogbox.Itdisplaysasimplemessage in a window and waits for the user to click an OK button before disappearing. The followingformatisrequiredtouseamsgboxwidget: dialog—msgboxtextheightwidth Thetextparameterisanystringyouwanttoplaceinthewindow.The dialogcommand automaticallywrapsthetexttofitthesizeofthewindowyoucreate,usingtheheightand widthparameters.Ifyouwanttoplaceatitleatthetopofthewindow,youcanalsouse the —title parameter, along with the text of the title. Here’s an example of using the msgboxwidget: $dialog—titleTesting—msgbox“Thisisatest”1020 After entering this command, the message box appears on the screen of the terminal emulatorsessionyou’reusing.Figure18.2showswhatthislookslike. Figure18.2Usingthemsgboxwidgetinthedialogcommand If your terminal emulator supports the mouse, you can click the OK button to close the dialog box. You can also use keyboard commands to simulate a click — just press the Enterkey. Theyesnowidget Theyesnowidgettakesthe msgboxwidgetonestepfurther,allowingtheusertoanswera yes/no question displayed in the window. It produces two buttons at the bottom of the window—oneforYesandanotherforNo.Theusercanswitchbetweenbuttonsbyusing the mouse, the tab key, or the keyboard arrow keys. To select the button, the user can eitherpressthespacebarortheEnterkey. Here’sanexampleofusingtheyesnowidget: $dialog—title“Pleaseanswer”—yesno“Isthisthingon?”1020 $echo$? 1 $ ThisproducesthewidgetshowninFigure18.3. Figure18.3Usingtheyesnowidgetinthedialogcommand Theexitstatusofthe dialogcommandissetdependingonwhichbuttontheuserselects. IftheNobuttonisselected,theexitstatusis1,andiftheYesbuttonisselected,theexit statusis0. Theinputboxwidget Theinputboxwidgetprovidesasimpletextboxareafortheusertoenteratextstring.The dialog command sends the value of the text string to STDERR. You must redirect that to retrievetheanswer.Figure18.4demonstrateswhattheinputboxwidgetlookslike. Figure18.4Theinputboxwidget AsyoucanseeinFigure18.4,the inputboxprovidestwobuttons—OKandCancel.If theCancelbuttonisselected,theexitstatusofthecommandis1;otherwise,theexitstatus is0: $dialog—inputbox“Enteryourage:”10202>age.txt $echo$? 0 $catage.txt 12$ You’llnoticewhenyouusethe catcommandtodisplaythecontentsofthetextfilethat there’s no newline character after the value. This enables you to easily redirect the file contentstoavariableinashellscripttoextractthestringenteredbytheuser. Thetextboxwidget Thetextboxwidgetisagreatwaytodisplaylotsofinformationinawindow.Itproduces ascrollablewindowcontainingthetextfromafilespecifiedintheparameters: $dialog—textbox/etc/passwd1545 The contents of the /etc/passwd file are shown within the scrollable text window, as illustratedinFigure18.5. Figure18.5Thetextboxwidget Youcanusethearrowkeystoscrollleftandright,aswellasupanddowninthetextfile. The bottom line in the window shows the percent location within the file that you’re viewing.ThetextboxcontainsonlyasingleExitbutton,whichshouldbeselectedtoexit thewidget. Themenuwidget The menu widget allows you to create a window version of the text menu we created earlierinthischapter.Yousimplyprovideaselectiontagandthetextforeachitem: $dialog—menu“SysAdminMenu”2030101“Displaydiskspace” 2“Displayusers”3“Displaymemoryusage”4“Exit”2>test.txt Thefirstparameterdefinesatitleforthemenu.Thenexttwoparametersdefinetheheight and width of the menu window, while the third parameter defines the number of menu itemsthatappearinthewindowatonetime.Iftherearemoremenuitems,youcanscroll throughthemusingthearrowkeys. Following those parameters, you must add menu item pairs. The first element is the tag usedtoselectthemenuitem.Eachtagshouldbeuniqueforeachmenuitemandcanbe selectedbypressingtheappropriatekeyonthekeyboard.Thesecondelementisthetext usedinthemenu.Figure18.6demonstratesthemenuproducedbytheexamplecommand. Figure18.6Themenuwidgetwithmenuitems Iftheuserselectsamenuitembypressingtheappropriatekeyforthetag,thatmenuitem ishighlightedbutnotselected.Aselectionisn’tmadeuntiltheOKbuttonisselectedby usingeitherthemouseortheEnterkey.The dialog command sends the selected menu itemtexttoSTDERR,whichyoucanredirectasneeded. Thefselectwidget There are several fancy built-in widgets provided by the dialog command. The fselect widget is extremely handy when working with filenames. Instead of forcing the user to typeafilename,youcanusethe fselectwidgettobrowsetothefilelocationandselect thefile,asshowninFigure18.7. Figure18.7Thefselectwidget Thefselectwidgetformatlookslike: $dialog—title“Selectafile”—fselect$HOME/10502>file.txt The first parameter after the fselect option is the starting folder location used in the window.Thefselectwidgetwindowconsistsofadirectorylistingontheleftside,afile listing on the right side that shows all the files in the selected directory, and a simple textbox that contains the currently selected file or directory. You can manually type a filenameinthetextbox,oryoucanusethedirectoryandfilelistingstoselectone(usethe spacebartoselectafiletoaddtothetextbox). Thedialogoptions In addition to the standard widgets, you can customize lots of different options in the dialogcommand.You’vealreadyseenthe —titleparameterinaction.Thisallowsyouto setatitleforthewidgetthatappearsatthetopofthewindow. Lots of other options allow you to completely customize both the appearance and the behavior of your windows. Table 18.2 shows the options available for the dialog command. Table18.2ThedialogCommandOptions Option —aspectratio Description ProceedstothenextdialogunlessEscortheCancelbuttonhasbeen pressed Specifiesthewidth/heightaspectratioofthewindow —backtitle title Specifiesatitletodisplayonthebackground,atthetopofthescreen —add-widget —beginxy Specifiesthestartinglocationofthetop-leftcornerofthewindow —cancel-label label SpecifiesanalternativelabelfortheCancelbutton Option —defaultno Description Clearsthedisplayusingthedefaultdialogbackgroundcolor EmbedsANSIcolorcodesindialogtext Allowsnewlinecharactersindialogtextandforcesalinewrap Dumpsasampleconfigurationfiletothespecifiedfile Makesthedefaultofayes/nodialogNo —default-item string Setsthedefaultiteminachecklist,form,ormenudialog —exit-label label SpecifiesanalternativelabelfortheExitbutton —extra-button DisplaysanextrabuttonbetweentheOKandCancelbuttons —extra-label label SpecifiesanalternativelabelfortheExtrabutton —help —help-button Displaysthedialogcommandhelpmessage DisplaysaHelpbuttonaftertheOKandCancelbuttons —help-label label SpecifiesanalternativelabelfortheHelpbutton —clear —colors —cr-wrap —create-rcfile Writesthechecklist,radiolist,orforminformationafterthehelp informationintheHelpbuttonwasselected —ignore Ignoresoptionsthatdialogdoesnotrecognize —input-fdfd Specifiesanalternativefiledescriptor,otherthanSTDIN —insecure Changesthepasswordwidgettodisplayasteriskswhentyping Addsahelpcolumnatthebottomofthescreenforeachtagina —item-help checklist,radiolist,ormenuforthetagitem —keep-window Doesn’tclearoldwidgetsfromthescreen —max-inputsize Specifiesamaximumstringsizefortheinput;defaultis2048 —nocancel SuppressestheCancelbutton —no-collapse Doesn’tconverttabstospacesindialogtext PlacesthetailboxbgdialoginbackgroundanddisablesSIGHUPfor —no-kill theprocess —no-labellabel SpecifiesanalternativelabelfortheNobutton —no-shadow Doesn’tdisplayshadowsfordialogwindows —ok-labellabel SpecifiesanalternativelabelfortheOKbutton —help-status —output-fdfd —print-maxsize —print-size —print-version SpecifiesanalternativeoutputfiledescriptorotherthanSTDERR Printsthemaximumsizeofdialogwindowsallowedtotheoutput Printsthesizeofeachdialogwindowtotheoutput Printsthedialogversiontooutput —separateoutput Outputstheresultofachecklistwidgetonelineatatimewithno quoting —separator string Specifiesastringthatseparatestheoutputforeachwidget —separatewidgetstring Specifiesastringthatseparatestheoutputforeachwidget Drawsashadowtotherightandbottomofeachwindow —single-quoted Usessinglequotingifneededforthechecklistoutput Delaysforthespecifiednumberofsecondsafterprocessingthedialog —sleepsec window —stderr SendsoutputtoSTDERR—thedefaultbehavior —stdout SendsoutputtoSTDOUT —tab-correct Convertstabstospaces —tab-lenn Specifiesthenumberofspacesatabcharacteruses;defaultis8 Specifiesthenumberofsecondsbeforeexitingwithanerrorcodeifno —timeoutsec userinput —titletitle Specifiesthetitleofthedialogwindow —trim Removesleadingspacesandnewlinecharactersfromdialogtext —visit-items Modifiesthetabstopsinthedialogwindowtoincludethelistofitems —shadow —yes-label label SpecifiesanalternativelabelfortheYesbutton The—backtitleoptionisahandywaytocreateacommontitleforyourmenuthroughthe script. If you specify it for each dialog window, it persists throughout your application, creatingaprofessionallooktoyourscript. AsyoucantellfromTable18.2,youcanoverwriteanyofthebuttonlabelsinyourdialog window.Thisfeatureallowsyoutocreatejustaboutanywindowsituationyouneed. Usingthedialogcommandinascript Usingthe dialogcommandinyourscriptsisasnap.Therearejusttwothingsyoumust remember: Checktheexitstatusofthedialogcommandifthere’saCancelorNobutton available. RedirectSTDERRtoretrievetheoutputvalue. If you follow these two rules, you’ll have a professional-looking interactive script in no time. Here’s an example using dialog widgets to reproduce the system admin menu createdearlierinthechapter: $catmenu3 #!/bin/bash #usingdialogtocreateamenu temp=$(mktemp-ttest.XXXXXX) temp2=$(mktemp-ttest2.XXXXXX) functiondiskspace{ df-k>$temp dialog—textbox$temp2060 } functionwhoseon{ who>$temp dialog—textbox$temp2050 } functionmemusage{ cat/proc/meminfo>$temp dialog—textbox$temp2050 } while[1] do dialog—menu“SysAdminMenu”2030101“Displaydiskspace”2 “Displayusers”3“Displaymemoryusage”0“Exit”2>$temp2 if[$?-eq1] then break fi selection=$(cat$temp2) case$selectionin 1) diskspace;; 2) whoseon;; 3) memusage;; 0) break;; *) dialog—msgbox“Sorry,invalidselection”1030 esac done rm-f$temp2>/dev/null rm-f$temp22>/dev/null $ The script uses the while loop with a constant true value to create an endless loop displaying the menu dialog. This means that, after every function, the script returns to displayingthemenu. ThemenudialogincludesaCancelbutton,sothescriptcheckstheexitstatusofthedialog commandincasetheuserpressestheCancelbuttontoexit.Becauseit’sina whileloop, exitingisaseasyasusingthebreakcommandtojumpoutofthewhileloop. Thescriptusesthemktempcommandtocreatetwotemporaryfilesforholdingdataforthe dialogcommands.Thefirstone,$temp,isusedtoholdtheoutputofthedf,whoeson,and meminfocommandssotheycanbedisplayedinthe textboxdialog(seeFigure18.8).The second temporary file, $temp2, is used to hold the selection value from the main menu dialog. Figure18.8Thememinfocommandoutputdisplayedusingthetextboxdialogoption Nowthisisstartingtolooklikearealapplicationthatyoucanshowofftopeople! GettingGraphic Ifyou’relookingforevenmoregraphicsforyourinteractivescripts,youcangoonestep further.BoththeKDEandGNOMEdesktopenvironments(seeChapter1)haveexpanded onthe dialog command idea and include commands that produce X Window graphical widgetsfortheirrespectiveenvironments. Thissectiondescribesthekdialogandzenitypackages,whichprovidegraphicalwindow widgetsfortheKDEandGNOMEdesktops,respectively. TheKDEenvironment The KDE graphical environment includes the kdialog package by default. The kdialog packageusesthe kdialogcommandtogeneratestandardwindows,similartothedialogstyle widgets, within your KDE desktop. However, instead of having the clunky feel to them,thesewindowsblendrightinwiththerestofyourKDEapplicationwindows!This allowsyoutoproduceWindows-qualityuserinterfacesdirectlyfromyourshellscripts! Note JustbecauseyourLinuxdistributionusestheKDEdesktopdoesn’tnecessarilymean ithasthekdialogpackageinstalledbydefault.Youmayneedtomanuallyinstallit fromthedistributionrepository. kdialogwidgets Just like the dialog command, the kdialog command uses command line options to specifywhattypeofwindowwidgettouse.Thefollowingistheformatofthe kdialog command: kdialogdisplay-optionswindow-optionsarguments The window-options options allow you to specify what type of window widget to use. TheavailableoptionsareshowninTable18.3. Table18.3kdialogWindowOptions Option —warningyesnotext Description Achecklistmenu,withstatusspecifyingiftheitemis checkedornot Errormessagebox Inputtextboxwhereyoucanspecifythedefaultvalueusing theinitvalue Menuselectionboxtitleandalistofitemsidentifiedbya tag Simplemessageboxwithspecifiedtext Passwordinputtextboxthathidesuserinput Aradiolistmenu,withstatusspecifyingiftheitemis selectedornot Returnsitemsonseparatelinesforchecklistandradiolist menus Sorrymessagebox Textboxdisplayingthecontentsoffile,alternatively specifiedbywidthandheight SpecifiesatitlefortheTitleBarareaofthedialogwindow WarningmessageboxwithYesandNobuttons —warningcontinuecancel text WarningmessageboxwithContinueandCancelbuttons —warningyesnocanceltext WarningmessageboxwithYes,No,andCancelbuttons QuestionboxwithYesandNobuttons QuestionboxwithYes,No,andCancelbuttons —checklisttitle[tag itemstatus] —errortext —inputboxtext[init] —menutitle[tagitem] —msgboxtext —passwordtext —radiolisttitle[tag itemstatus] —separate-output —sorrytext —textboxfile[width] [height] —titletitle —yesnotext —yesnocanceltext AsyoucanseefromTable18.3,allthestandardwindowdialogboxtypesarerepresented. However,whenyouuseakdialogwindowwidget,itappearsasaseparatewindowinthe KDEdesktop,notinsidetheterminalemulatorsession! The checklistand radiolist widgets allow you to define individual items in the lists andwhethertheyareselectedbydefault: $kdialog—checklist“ItemsIneed”1“Toothbrush”on2“Toothpaste” off3“Hairbrush”on4“Deodorant”off5“Slippers”off TheresultingchecklistwindowisshowninFigure18.9. Figure18.9Akdialogchecklistdialogwindow Theitemsspecifiedas“on”arehighlightedinthechecklist.Toselectordeselectanitem inthechecklist,justclickit.IfyouselecttheOKbutton,thekdialogsendsthetagvalues toSTDOUT: “1”“3” $ When you press the Enter key, the kdialog box appears with the selections. When you clicktheOKorCancelbuttons,thekdialogcommandreturnseachtagasastringvalueto STDOUT(thesearethe”1”,and ”3”valuesyouseeintheoutput).Yourscriptmustbeable toparsetheresultingvaluesandmatchthemwiththeoriginalvalues. Usingkdialog Youcanusethe kdialogwindowwidgetsinyourshellscriptssimilarlytohowyouuse thedialogwidgets.Thebigdifferenceisthatthe kdialogwindowwidgetsoutputvalues usingSTDOUTinsteadofSTDERR. Here’sascriptthatconvertsthesysadminmenucreatedearlierintoaKDEapplication: $catmenu4 #!/bin/bash #usingkdialogtocreateamenu temp=$(mktemp-ttemp.XXXXXX) temp2=$(mktemp-ttemp2.XXXXXX) functiondiskspace{ df-k>$temp kdialog—textbox$temp100010 } functionwhoseon{ who>$temp kdialog—textbox$temp50010 } functionmemusage{ cat/proc/meminfo>$temp kdialog—textbox$temp300500 } while[1] do kdialog—menu“SysAdminMenu”“1”“Displaydiskspace”“2”“Display users”“3”“Displaymemoryusage”“0”“Exit”>$temp2 if[$?-eq1] then break fi selection=$(cat$temp2) case$selectionin 1) diskspace;; 2) whoseon;; 3) memusage;; 0) break;; *) kdialog—msgbox“Sorry,invalidselection” esac done $ Thereisn’tmuchdifferenceinthescriptfromusingthekdialogcommandandthedialog command.TheresultingmainmenugeneratedisshowninFigure18.10. Figure18.10Thesysadminmenuscriptusingkdialog NowyoursimpleshellscriptlooksjustlikearealKDEapplication!There’snolimitto whatyoucandowithyourinteractivescriptsnow. TheGNOMEenvironment The GNOME graphical environment supports two popular packages that can generate standardwindows: gdialog zenity By far, zenity is the most commonly available package found in most GNOME desktop Linux distributions (it’s installed by default in both Ubuntu and Fedora). This section describesthefeaturesofzenityanddemonstrateshowtouseitinyourshellscripts. zenityWidgets As you would expect, zenity allows you to create different windows widgets by using commandlineoptions.Table18.4showsthedifferentwidgetsthatzenitycanproduce. Table18.4ThezenityWindowsWidgets Option Description —calendar Displaysafullmonthcalendar —entry Displaysatextentrydialogwindow —error Displaysanerrormessagedialogwindow —file-selection Displaysafullpathnameandfilenamedialogwindow —info Displaysaninformationaldialogwindow —list Displaysachecklistorradiolistdialogwindow —notification Displaysanotificationicon —progress Displaysaprogressbardialogwindow —question —scale —text-info —warning Displaysayes/noquestiondialogwindow Displaysascaledialogwindow Displaysatextboxcontainingtext Displaysawarningdialogwindow The zenity command line program works somewhat differently than the kdialog and dialog programs. Many of the widget types are defined using additional options on the commandline,insteadofincludingthemasargumentstoanoption. The zenity command does offer some pretty cool advanced dialog windows. The calendaroptionproducesafullmonthcalendar,asshowninFigure18.11. Figure18.11Thezenitycalendardialogwindow When you select a date from the calendar, the zenity command returns the value to STDOUT,justlikekdialog: $zenity—calendar 12/25/2011 $ Anotherprettycoolwindowinzenityisthefileselectionoption,showninFigure18.12. Figure18.12Thezenityfileselectiondialogwindow Youcanusethedialogwindowtobrowsetoanydirectorylocationonthesystem(aslong asyouhavetheprivilegestoviewthedirectory)andselectafile.Whenyouselectafile, thezenitycommandreturnsthefullfileandpathname: $zenity—file-selection /home/ubuntu/menu5 $ Withtoolslikethatatyourdisposal,thesky’sthelimitwithyourshellscriptcreations! Usingzenityinscripts Asyouwouldexpect,zenityperformswellinshellscripts.Unfortunately,zenitychosenot tofollowtheoptionconventionusedin dialogand kdialog,soconvertinganyexisting interactivescriptstozenitymayprovechallenging. In converting the sys admin menu from kdialogto zenity, we had to do quite a bit of manipulationofthewidgetdefinitions: $catmenu5 #!/bin/bash #usingzenitytocreateamenu temp=$(mktemp-ttemp.XXXXXX) temp2=$(mktemp-ttemp2.XXXXXX) functiondiskspace{ df-k>$temp zenity—text-info—title“Diskspace”—filename=$temp —width750—height10 } functionwhoseon{ who>$temp zenity—text-info—title“Loggedinusers”—filename=$temp —width500—height10 } functionmemusage{ cat/proc/meminfo>$temp zenity—text-info—title“Memoryusage”—filename=$temp —width300—height500 } while[1] do zenity—list—radiolist—title“SysAdminMenu”—column“Select” —column“MenuItem”FALSE“Displaydiskspace”FALSE“Displayusers” FALSE“Displaymemoryusage”FALSE“Exit”>$temp2 if[$?-eq1] then break fi selection=$(cat$temp2) case$selectionin “Displaydiskspace”) diskspace;; “Displayusers”) whoseon;; “Displaymemoryusage”) memusage;; Exit) break;; *) zenity—info“Sorry,invalidselection” esac done $ Becausezenitydoesn’tsupportthemenudialogwindow,weusedaradiolisttypewindow forthemainmenu,asshowninFigure18.13. Figure18.13Thesysadminmenuusingzenity The radiolist uses two columns, each with a column heading. The first column includes theradiobuttonstoselect.Thesecondcolumnistheitemtext.Theradiolistalsodoesn’t use tags for the items. When you select an item, the full text of the item is returned to STDOUT.Thismakeslifealittlemoreinterestingforthe casecommand.Youmustusethe fulltextfromtheitemsinthecaseoptions.Ifthereareanyspacesinthetext,youneedto usequotationmarksaroundthetext. Usingthezenitypackage,youcanaddaWindowsfeeltoyourinteractiveshellscriptsin theGNOMEdesktop. Summary Interactiveshellscriptshaveareputationforbeingdullandboring.Youcanchangethat byusingafewdifferenttechniquesandtoolsavailableonmostLinuxsystems.First,you cancreatemenusystemsforyourinteractivescriptsbyusingthecasecommandandshell scriptfunctions. The menucommandallowsyoutopaintamenu,usingthestandard echocommand,and readaresponsefromtheuser,usingthe readcommand.The casecommandthenselects theappropriateshellscriptfunctionbasedonthevalueentered. The dialog program provides several prebuilt text widgets for creating Windows-like objectsonatext-basedterminalemulator.Youcancreatedialogboxesfordisplayingtext, enteringtext,andchoosingfilesanddatesbyusingthedialogprogram.Thishelpsbring evenmorelifetoyourshellscript. Ifyou’rerunningyourshellscriptsinagraphicalXWindowenvironment,youcanutilize even more tools in your interactive scripts. For the KDE desktop, there’s the kdialog program.Thisprogramprovidessimplecommandstocreatewindowswidgetsforallthe basic windows functions. For the GNOME desktop, there are the gdialog and zenity programs.EachoftheseprogramsprovideswindowwidgetsthatblendintotheGNOME desktopjustlikearealWindowsapplication. Thenextchapterdivesintothesubjectofeditingandmanipulatingtextdatafiles.Often the biggest use of shell scripts revolves around parsing and displaying data in text files suchasloganderrorfiles.TheLinuxenvironmentincludestwoveryusefultools,sedand gawk,forworkingwithtextdatainyourshellscripts.Thenextchapterintroducesyouto thesetools,andshowsthebasicsofhowtousethem. Chapter19 Introducingsedandgawk InThisChapter 1. LearningaboutthesedEditor 2. GettingintroducedtothegawkEditor 3. ExploringsedEditorbasics Byfar,oneofthemostcommonfunctionsthatpeopleuseshellscriptsforistowork withtextfiles.Betweenexamininglogfiles,readingconfigurationfiles,andhandling dataelements,shellscriptscanhelpautomatethemundanetasksofmanipulatingany typeofdatacontainedintextfiles.However,tryingtomanipulatethecontentsoftext filesusingjustshellscriptcommandscanbesomewhatawkward.Ifyouperformany typeofdatamanipulationinyourshellscripts,youwanttobecomefamiliarwiththe sedandgawktoolsavailableinLinux.Thesetoolscangreatlysimplifyanydatahandlingtasksyouneedtoperform. ManipulatingText Chapter10showedyouhowtoedittextfilesusingdifferenteditorprogramsavailablein theLinuxenvironment.Theseeditorsenableyoutoeasilymanipulatetextcontainedina textfilebyusingsimplecommandsormouseclicks. Therearetimes,however,whenyou’llfindyourselfwantingtomanipulatetextinatext file on the fly, without having to pull out a full-fledged interactive text editor. In these situations, it would be useful to have a simple command line editor that could easily format,insert,modify,ordeletetextelementsautomatically. TheLinuxsystemprovidestwocommontoolsfordoingjustthat.Thissectiondescribes thetwomostpopularcommandlineeditorsusedintheLinuxworld,sedandgawk. Gettingtoknowthesededitor The sededitoriscalledastreameditor,asopposedtoanormalinteractivetexteditor.In aninteractivetexteditor,suchasvim,youinteractivelyusekeyboardcommandstoinsert, delete,orreplacetextinthedata.Astreameditoreditsastreamofdatabasedonasetof rulesyousupplyaheadoftime,beforetheeditorprocessesthedata. The sededitorcanmanipulatedatainadatastreambasedoncommandsyoueitherenter intothecommandlineorstoreinacommandtextfile.Thesededitordoesthesethings: 1. Readsonedatalineatatimefromtheinput 2. Matchesthatdatawiththesuppliededitorcommands 3. Changesdatainthestreamasspecifiedinthecommands 4. OutputsthenewdatatoSTDOUT Afterthestreameditormatchesallthecommandsagainstalineofdata,itreadsthenext lineofdataandrepeatstheprocess.Afterthestreameditorprocessesallthelinesofdata inthestream,itterminates. Because the commands are applied sequentially line by line, the sed editor makes only onepassthroughthedatastreamtomaketheedits.Thismakesthesededitormuchfaster thananinteractiveeditorandallowsyoutoquicklymakechangestodatainafileonthe fly. Here’stheformatforusingthesedcommand: sedoptionsscriptfile The options parameters allow you to customize the behavior of the sed command and includetheoptionsshowninTable19.1. Table19.1ThesedCommandOptions Option -e Description Addscommandsspecifiedinthescripttothecommandsrunwhileprocessing theinput Addsthecommandsspecifiedinthefiletothecommandsrunwhile -ffile processingtheinput -n Doesn’tproduceoutputforeachcommand,butwaitsfortheprintcommand script Thescriptparameterspecifiesasinglecommandtoapplyagainstthestreamdata.Ifmore thanonecommandisrequired,youmustuseeitherthe -eoptiontospecifytheminthe commandlineorthe-foptiontospecifytheminaseparatefile.Numerouscommandsare availableformanipulatingdata.Weexaminesomeofthebasiccommandsusedbythesed editorinthischapterandthenlookatsomeofthemoreadvancedcommandsinChapter 21. Defininganeditorcommandinthecommandline Bydefault,the sededitorappliesthespecifiedcommandstothe STDINinputstream.This allowsyoutopipedatadirectlytothe sededitorforprocessing.Here’saquickexample demonstratinghowtodothis: $echo“Thisisatest”|sed‘s/test/bigtest/’ Thisisabigtest $ Thisexampleusesthe scommandinthe sededitor.The scommandsubstitutesasecond text string for the first text string pattern specified between the forward slashes. In this example,thewordsbigtestweresubstitutedforthewordtest. Whenyourunthisexample,itshoulddisplaytheresultsalmostinstantaneously.That’sthe powerofusingthesededitor.Youcanmakemultipleeditstodatainaboutthesametime ittakesforsomeoftheinteractiveeditorsjusttostartup. Of course, this simple test just edited one line of data. You should get the same speedy resultswheneditingcompletefilesofdata: $catdata1.txt Thequickbrownfoxjumpsoverthelazydog. Thequickbrownfoxjumpsoverthelazydog. Thequickbrownfoxjumpsoverthelazydog. Thequickbrownfoxjumpsoverthelazydog. $ $sed‘s/dog/cat/’data1.txt Thequickbrownfoxjumpsoverthelazycat. Thequickbrownfoxjumpsoverthelazycat. Thequickbrownfoxjumpsoverthelazycat. Thequickbrownfoxjumpsoverthelazycat. $ The sed command executes and returns the data almost instantaneously. As it processes eachlineofdata,theresultsaredisplayed.You’llstartseeingresultsbeforethesededitor completesprocessingtheentirefile. It’simportanttonotethatthe sededitordoesn’tmodifythedatainthetextfileitself.It only sends the modified text to STDOUT. If you look at the text file, it still contains the originaldata: $catdata1.txt Thequickbrownfoxjumpsoverthelazydog. Thequickbrownfoxjumpsoverthelazydog. Thequickbrownfoxjumpsoverthelazydog. Thequickbrownfoxjumpsoverthelazydog. $ Usingmultipleeditorcommandsinthecommandline Toexecutemorethanonecommandfromthesedcommandline,justusethe-eoption: $sed-e‘s/brown/green/;s/dog/cat/’data1.txt Thequickgreenfoxjumpsoverthelazycat. Thequickgreenfoxjumpsoverthelazycat. Thequickgreenfoxjumpsoverthelazycat. Thequickgreenfoxjumpsoverthelazycat. $ Both commands are applied to each line of data in the file. The commands must be separated with a semicolon, and there shouldn’t be any spaces between the end of the commandandthesemicolon. Insteadofusingasemicolontoseparatethecommands,youcanusethesecondaryprompt inthebashshell.Justenterthefirstsinglequotationmarktoopenthe sedprogramscript (sed editor command list), and bash continues to prompt you for more commands until youentertheclosingquotationmark: $sed-e‘ >s/brown/green/ >s/fox/elephant/ >s/dog/cat/’data1.txt Thequickgreenelephantjumpsoverthelazycat. Thequickgreenelephantjumpsoverthelazycat. Thequickgreenelephantjumpsoverthelazycat. Thequickgreenelephantjumpsoverthelazycat. $ You must remember to finish the command on the same line where the closing single quotation mark appears. After the bash shell detects the closing quotation mark, it processes the command. After it starts, the sed command applies each command you specifiedtoeachlineofdatainthetextfile. Readingeditorcommandsfromafile Finally,ifyouhavelotsof sedcommandsyouwanttoprocess,itisofteneasiertojust storetheminaseparatefile.Usethe-foptiontospecifythefileinthesedcommand: $catscript1.sed s/brown/green/ s/fox/elephant/ s/dog/cat/ $ $sed-fscript1.seddata1.txt Thequickgreenelephantjumpsoverthelazycat. Thequickgreenelephantjumpsoverthelazycat. Thequickgreenelephantjumpsoverthelazycat. Thequickgreenelephantjumpsoverthelazycat. $ In this case, you don’t put a semicolon after each command. The sededitorknowsthat eachlinecontainsaseparatecommand.Aswithenteringcommandsonthecommandline, thesededitorreadsthecommandsfromthespecifiedfileandappliesthemtoeachlinein thedatafile. Tip Itcanbeeasytoconfuseyoursededitorscriptfileswithyourbashshellscriptfiles. Toeliminateconfusion,usea.sedfileextensiononyoursedscriptfiles. We’lllookatsomeother sededitorcommandsthatcomeinhandyformanipulatingdata inthe“Commandingatthe sedEditorBasics”section.Beforethat,let’squicklylookat theotherLinuxdataeditor. Gettingtoknowthegawkprogram Although the sed editor is a handy tool for modifying text files on the fly, it has its limitations.Often,youneedamoreadvancedtoolformanipulatingdatainafile,onethat provides a more programming-like environment allowing you to modify and reorganize datainafile.Thisiswheregawkcomesin. Note Thegawkprogramisnotinstalledbydefaultonalldistributions.IfyourLinux distributiondoesnothavethegawkprogram,installthegawkpackageusingChapter9 asaguide. The gawk program is the GNU version of the original awk program in Unix. The gawk program takes stream editing one step further than the sed editor by providing a programming language instead of just editor commands. Within the gawk programming language,youcandothefollowing: Definevariablestostoredata. Usearithmeticandstringoperatorstooperateondata. Usestructuredprogrammingconcepts,suchasif-thenstatementsandloops,toadd logictoyourdataprocessing. Generateformattedreportsbyextractingdataelementswithinthedatafileand repositioningtheminanotherorderorformat. The gawk program’s report-generating capabilities are often used for extracting data elements from large bulky text files and formatting them into a readable report. The perfectexampleofthisisformattinglogfiles.Tryingtoporethroughlinesoferrorsina logfilecanbedifficult.The gawkprogramallowsyoutofilterjustthedataelementsyou want to view from the log file, and then you can format them in a manner that makes readingtheimportantdataeasier. Visitingthegawkcommandformat Here’sthebasicformatofthegawkprogram: gawkoptionsprogramfile Table19.2showstheoptionsavailablewiththegawkprogram. Table19.2ThegawkOptions Option Description -Ffs Specifiesafileseparatorfordelineatingdatafieldsinaline -ffile Specifiesafilenametoreadtheprogramfrom -vvar=value Definesavariableanddefaultvalueusedinthegawkprogram -mfN Specifiesthemaximumnumberoffieldstoprocessinthedatafile -mrN Specifiesthemaximumrecordsizeinthedatafile -Wkeyword Specifiesthecompatibilitymodeorwarninglevelforgawk The command line options provide an easy way to customize features in the gawk program.We’lllookmorecloselyattheseasweexploregawk. Thepowerofgawkisintheprogramscript.Youcanwritescriptstoreadthedatawithina textlineandthenmanipulateanddisplaythedatatocreateanytypeofoutputreport. Readingtheprogramscriptfromthecommandline A gawk program script is defined by opening and closing braces. You must place script commandsbetweenthetwobraces({}).Ifyouincorrectlyuseaparenthesisinsteadofa bracetoencloseyourgawkscript,yougeterrormessages,similartothefollowing: $gawk‘(print“HelloWorld!”}’ gawk:(print“HelloWorld!”} gawk:∧syntaxerror Becausethe gawkcommandlineassumesthatthescriptisasingletextstring,youmust also enclose your script in single quotation marks. Here’s an example of a simple gawk programscriptspecifiedonthecommandline: $gawk‘{print“HelloWorld!”}’ Theprogramscriptdefinesasinglecommand,the printcommand.The printcommand does what it says: It prints text to STDOUT. If you try running this command, you’ll be somewhat disappointed, because nothing happens right away. Because no filename was definedinthecommandline,the gawkprogramretrievesdatafrom STDIN.Whenyourun theprogram,itjustwaitsfortexttocomeinviaSTDIN. IfyoutypealineoftextandpresstheEnterkey, gawkrunsthetextthroughtheprogram script.Justlikethe sededitor,the gawkprogramexecutestheprogramscriptoneachline of text available in the data stream. Because the program script is set to display a fixed textstring,nomatterwhattextyouenterinthedatastream,yougetthesametextoutput: $gawk‘{print“HelloWorld!”}’ Thisisatest HelloWorld! hello HelloWorld! Thisisanothertest HelloWorld! Toterminatethe gawkprogram,youmustsignalthatthedatastreamhasended.Thebash shellprovidesakeycombinationtogenerateanEnd-of-File(EOF)character.TheCtrl+D key combination generates an EOF character in bash. Using that key combination terminatesthegawkprogramandreturnsyoutoacommandlineinterfaceprompt. Usingdatafieldvariables Oneoftheprimaryfeaturesofgawkisitsabilitytomanipulatedatainthetextfile.Itdoes thisbyautomaticallyassigningavariabletoeachdataelementinaline.Bydefault, gawk assignsthefollowingvariablestoeachdatafielditdetectsinthelineoftext: $0representstheentirelineoftext. $1representsthefirstdatafieldinthelineoftext. $2representstheseconddatafieldinthelineoftext. $nrepresentsthenthdatafieldinthelineoftext. Each data field is determined in a text line by a fieldseparationcharacter. When gawk reads a line of text, it delineates each data field using the defined field separation character.Thedefaultfieldseparationcharacterin gawkisanywhitespacecharacter(such asthetaborspacecharacters). Here’sanexamplegawkprogramthatreadsatextfileanddisplaysonlythefirstdatafield value: $catdata2.txt Onelineoftesttext. Twolinesoftesttext. Threelinesoftesttext. $ $gawk‘{print$1}’data2.txt One Two Three $ Thisprogramusesthe $1fieldvariabletodisplayonlythefirstdatafieldforeachlineof text. Ifyou’rereadingafilethatusesadifferentfieldseparationcharacter,youcanspecifyitby usingthe-Foption: $gawk-F:‘{print$1}’/etc/passwd root bin daemon adm lp sync shutdown halt mail […] Thisshortprogramdisplaysthefirstdatafieldinthepasswordfileonthesystem.Because the/etc/passwdfileusesacolontoseparatethedatafields,ifyouwanttoseparateeach dataelement,youmustspecifyitasthefieldseparationcharacterinthegawkoptions. Usingmultiplecommandsintheprogramscript A programming language wouldn’t be very useful if you could only execute one command. The gawk programming language allows you to combine commands into a normal program. To use multiple commands in the program script specified on the commandline,justplaceasemicolonbetweeneachcommand: $echo“MynameisRich”|gawk‘{$4=“Christine”;print$0}’ MynameisChristine $ The first command assigns a value to the $4 field variable. The second command then prints the entire data field. Notice from the output that the gawk program replaced the fourthdatafieldintheoriginaltextwiththenewvalue. Youcanalsousethesecondaryprompttoenteryourprogramscriptcommandsonelineat atime: $gawk‘{ >$4=“Christine” >print$0}’ MynameisRich MynameisChristine $ Afteryouopenthesinglequotationmark,thebashshellprovidesthesecondarypromptto promptyouformoredata.Youcanaddyourcommandsoneatatimeoneachlineuntil you enter the closing single quotation mark. Because no filename was defined in the commandline,thegawkprogramretrievesdatafromSTDIN.Whenyouruntheprogram,it waits for text to come in via STDIN. To exit the program, just press the Ctrl+D key combinationtosignaltheendofthedata. Readingtheprogramfromafile As with the sed editor, the gawk editor allows you to store your programs in a file and refertotheminthecommandline: $catscript2.gawk {print$1“‘shomedirectoryis”$6} $ $gawk-F:-fscript2.gawk/etc/passwd root’shomedirectoryis/root bin’shomedirectoryis/bin daemon’shomedirectoryis/sbin adm’shomedirectoryis/var/adm lp’shomedirectoryis/var/spool/lpd […] Christine’shomedirectoryis/home/Christine Samantha’shomedirectoryis/home/Samantha Timothy’shomedirectoryis/home/Timothy $ The script2.gawk program script uses the print command again to print the /etc/passwdfile’shomedirectorydatafield(fieldvariable $6)andtheuseriddatafield (fieldvariable$1). You can specify multiple commands in the program file. To do so, just place each commandonaseparateline.Youdon’tneedtousesemicolons: $catscript3.gawk { text=“‘shomedirectoryis“ print$1text$6 } $ $gawk-F:-fscript3.gawk/etc/passwd root’shomedirectoryis/root bin’shomedirectoryis/bin daemon’shomedirectoryis/sbin adm’shomedirectoryis/var/adm lp’shomedirectoryis/var/spool/lpd […] Christine’shomedirectoryis/home/Christine Samantha’shomedirectoryis/home/Samantha Timothy’shomedirectoryis/home/Timothy $ Thescript3.gawkprogramscriptdefinesavariabletoholdatextstringusedintheprint command.Noticethatgawkprogramsdon’tuseadollarsignwhenreferencingavariable’s value,asashellscriptdoes. Runningscriptsbeforeprocessingdata The gawkprogramalsoallowsyoutospecifywhentheprogramscriptisrun.Bydefault, gawkreadsalineoftextfromtheinputandthenexecutestheprogramscriptonthedatain thelineoftext.Sometimes,youmayneedtorunascriptbeforeprocessingdata,suchasto create a header section for a report. The BEGIN keyword is used to accomplish this. It forces gawktoexecutetheprogramscriptspecifiedafterthe BEGINkeyword,before gawk readsthedata: $gawk‘BEGIN{print“HelloWorld!”}’ HelloWorld! $ Thistimethe printcommanddisplaysthetextbeforereadinganydata.However,afterit displaysthetext,itquicklyexits,withoutwaitingforanydata. The reason for this is that the BEGIN keyword only applies the specified script before it processesanydata.Ifyouwanttoprocessdatawithanormalprogramscript,youmust definetheprogramusinganotherscriptsection: $catdata3.txt Line1 Line2 Line3 $ $gawk‘BEGIN{print”Thedata3FileContents:”} >{print$0}’data3.txt Thedata3FileContents: Line1 Line2 Line3 $ NowaftergawkexecutestheBEGINscript,itusesthesecondscripttoprocessanyfiledata. Becarefulwhendoingthis;bothofthescriptsarestillconsideredonetextstringonthe gawkcommandline.Youneedtoplaceyoursinglequotationmarksaccordingly. Runningscriptsafterprocessingdata LiketheBEGINkeyword,theENDkeywordallowsyoutospecifyaprogramscriptthatgawk executesafterreadingthedata: $gawk‘BEGIN{print“Thedata3FileContents:”} >{print$0} >END{print“EndofFile”}’data3.txt Thedata3FileContents: Line1 Line2 Line3 EndofFile $ Whenthegawkprogramisfinishedprintingthefilecontents,itexecutesthecommandsin the END script. This is a great technique to use to add footer data to reports after all the normaldatahasbeenprocessed. Youcanputalltheseelementstogetherintoanicelittleprogramscriptfiletocreateafull reportfromasimpledatafile: $catscript4.gawk BEGIN{ print“Thelatestlistofusersandshells” print”UserID\tShell” print“––—\t––-” FS=”:” } { print$1“\t“$7 } END{ print“Thisconcludesthelisting” } $ Thisscriptusesthe BEGINscripttocreateaheadersectionforthereport.Italsodefinesa specialvariablecalled FS.Thisisyetanotherwaytodefinethefieldseparationcharacter. This way, you don’t have to depend on the script’s user to define the field separation characterinthecommandlineoptions. Here’sasomewhattruncatedoutputfromrunningthisgawkprogramscript: $gawk-fscript4.gawk/etc/passwd Thelatestlistofusersandshells UserIDShell ––—––root/bin/bash bin/sbin/nologin daemon/sbin/nologin […] Christine/bin/bash mysql/bin/bash Samantha/bin/bash Timothy/bin/bash Thisconcludesthelisting $ As expected, the BEGIN script created the header text, the program script processed the information from the specified data file (the /etc/passwd file), and the END script produced the footer text. The ∖t within the print command produces some nicely formattedtabbedoutput. This gives you a small taste of the power available when you use simple gawk scripts. Chapter 22 describes some more basic programming principles available for your gawk scripts,alongwithsomeevenmoreadvancedprogrammingconceptsyoucanuseinyour gawk program scripts to create professional looking reports from even the most cryptic datafiles. CommandingatthesedEditorBasics The key to successfully using the sed editor is to know its myriad of commands and formats,whichhelpyoutocustomizeyourtextediting.Thissectiondescribessomeofthe basic commands and features you can incorporate into your script to start using the sed editor. Introducingmoresubstitutionoptions You’vealreadyseenhowtousethescommandtosubstitutenewtextforthetextinaline. However, a few additional options are available for the substitute command that can helpmakeyourlifeeasier. Substitutingflags There’sacaveattohowthe substitutecommandreplacesmatchingpatternsinthetext string.Watchwhathappensinthisexample: $catdata4.txt Thisisatestofthetestscript. Thisisthesecondtestofthetestscript. $ $sed‘s/test/trial/’data4.txt Thisisatrialofthetestscript. Thisisthesecondtrialofthetestscript. $ Thesubstitutecommandworksfineinreplacingtextinmultiplelines,butbydefault,it replacesonlythefirstoccurrenceineachline.Togetthe substitutecommandtowork ondifferentoccurrencesofthetext,youmustuseasubstitutionflag.Thesubstitutionflag issetafterthesubstitutioncommandstrings: s/pattern/replacement/flags Fourtypesofsubstitutionflagsareavailable: Anumber,indicatingthepatternoccurrenceforwhichnewtextshouldbesubstituted g,indicatingthatnewtextshouldbesubstitutedforalloccurrencesoftheexisting text p,indicatingthatthecontentsoftheoriginallineshouldbeprinted wfile,whichmeanstowritetheresultsofthesubstitutiontoafile Inthefirsttypeofsubstitution,youcanspecifywhichoccurrenceofthematchingpattern thesededitorshouldsubstitutenewtextfor: $sed‘s/test/trial/2’data4.txt Thisisatestofthetrialscript. Thisisthesecondtestofthetrialscript. $ As a result of specifying a 2 as the substitution flag, the sed editor replaces the pattern onlyinthesecondoccurrenceineachline.The gsubstitutionflagenablesyoutoreplace everyoccurrenceofthepatterninthetext: $sed‘s/test/trial/g’data4.txt Thisisatrialofthetrialscript. Thisisthesecondtrialofthetrialscript. $ The p substitution flag prints a line that contains a matching pattern in the substitute command.Thisismostoftenusedinconjunctionwiththe-nsedoption: $catdata5.txt Thisisatestline. Thisisadifferentline. $ $sed-n‘s/test/trial/p’data5.txt Thisisatrialline. $ The -n option suppresses output from the sed editor. However, the p substitution flag outputs any line that has been modified. Using the two in combination produces output onlyforlinesthathavebeenmodifiedbythesubstitutecommand. Thewsubstitutionflagproducesthesameoutputbutstorestheoutputinthespecifiedfile: $sed‘s/test/trial/wtest.txt’data5.txt Thisisatrialline. Thisisadifferentline. $ $cattest.txt Thisisatrialline. $ Thenormaloutputofthe sededitorappearsin STDOUT,butonlythelinesthatincludethe matchingpatternarestoredinthespecifiedoutputfile. Replacingcharacters Sometimes, you run across characters in text strings that aren’t easy to use in the substitutionpattern.OnepopularexampleintheLinuxworldistheforwardslash(/). Substitutingpathnamesinafilecangetawkward.Forexample,ifyouwantedtosubstitute theCshellforthebashshellinthe/etc/passwdfile,you’dhavetodothis: $sed‘s/\/bin\/bash/\/bin\/csh/’/etc/passwd Because the forward slash is used as the string delimiter, you must use a backslash to escapeitifitappearsinthepatterntext.Thisoftenleadstoconfusionandmistakes. To solve this problem, the sed editor allows you to select a different character for the stringdelimiterinthesubstitutecommand: $sed‘s!/bin/bash!/bin/csh!’/etc/passwd In this example, the exclamation point is used for the string delimiter, making the pathnamesmucheasiertoreadandunderstand. Usingaddresses Bydefault,thecommandsyouuseinthe sededitorapplytoalllinesofthetextdata.If youwanttoapplyacommandonlytoaspecificlineoragroupoflines,youmustuseline addressing. Therearetwoformsoflineaddressinginthesededitor: Anumericrangeoflines Atextpatternthatfiltersoutaline Bothformsusethesameformatforspecifyingtheaddress: [address]command Youcanalsogroupmorethanonecommandtogetherforaspecificaddress: address{ command1 command2 command3 } The sed editor applies each of the commands you specify only to lines that match the addressspecified.Thissectiondemonstratesusingbothoftheseaddressingtechniquesin yoursededitorscripts. Addressingthenumericline When using numeric line addressing, you reference lines using their line position in the textstream.The sededitorassignsthefirstlineinthetextstreamaslinenumberoneand continuessequentiallyforeachnewline. Theaddressyouspecifyinthecommandcanbeasinglelinenumberorarangeoflines specified by a starting line number, a comma, and an ending line number. Here’s an exampleofspecifyingalinenumbertowhichthesedcommandwillbeapplied: $sed‘2s/dog/cat/’data1.txt Thequickbrownfoxjumpsoverthelazydog Thequickbrownfoxjumpsoverthelazycat Thequickbrownfoxjumpsoverthelazydog Thequickbrownfoxjumpsoverthelazydog $ Thesededitormodifiedthetextonlyinlinetwopertheaddressspecified.Here’sanother example,thistimeusingarangeoflineaddresses: $sed‘2,3s/dog/cat/’data1.txt Thequickbrownfoxjumpsoverthelazydog Thequickbrownfoxjumpsoverthelazycat Thequickbrownfoxjumpsoverthelazycat Thequickbrownfoxjumpsoverthelazydog $ Ifyouwanttoapplyacommandtoagroupoflinesstartingatsomepointwithinthetext, butcontinuingtotheendofthetext,youcanusethespecialaddress,thedollarsign: $sed‘2,$s/dog/cat/’data1.txt Thequickbrownfoxjumpsoverthelazydog Thequickbrownfoxjumpsoverthelazycat Thequickbrownfoxjumpsoverthelazycat Thequickbrownfoxjumpsoverthelazycat $ Becauseyoumaynotknowhowmanylinesofdataareinthetext,thedollarsignoften comesinhandy. Usingtextpatternfilters The other method of restricting which lines a command applies to is a bit more complicated.The sededitorallowsyoutospecifyatextpatternthatitusestofilterlines forthecommand.Thisistheformat: /pattern/command Youmustencapsulatethepatternyouspecifyinforwardslashes.The sededitorapplies thecommandonlytolinesthatcontainthetextpatternyouspecify. Forexample,ifyouwanttochangethedefaultshellforonlytheuserSamantha,you’duse thesedcommand: $grepSamantha/etc/passwd Samantha:x:502:502::/home/Samantha:/bin/bash $ $sed‘/Samantha/s/bash/csh/’/etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin […] Christine:x:501:501:ChristineB:/home/Christine:/bin/bash Samantha:x:502:502::/home/Samantha:/bin/csh Timothy:x:503:503::/home/Timothy:/bin/bash $ Thecommandwasappliedonlytothelinewiththematchingtextpattern.Althoughusing afixedtextpatternmaybeusefulforfilteringspecificvalues,asinthe useridexample, it’s somewhat limited in what you can do with it. The sed editor uses a feature called regular expressions in text patterns to allow you to create patterns that get pretty involved. Regular expressions allow you to create advanced text pattern–matching formulas to match all sorts of data. These formulas combine a series of wildcard characters, special characters,andfixedtextcharacterstoproduceaconcisepatternthatcanmatchjustabout any text situation. Regular expressions are one of the scarier parts of shell script programming,andChapter20coversthemingreatdetail. Groupingcommands If you need to perform more than one command on an individual line, group the commands together using braces. The sed editor processes each command listed on the addressline(s): $sed‘2{ >s/fox/elephant/ >s/dog/cat/ >}’data1.txt Thequickbrownfoxjumpsoverthelazydog. Thequickbrownelephantjumpsoverthelazycat. Thequickbrownfoxjumpsoverthelazydog. Thequickbrownfoxjumpsoverthelazydog. $ Both commands are processed against the address. And of course, you can specify an addressrangebeforethegroupedcommands: $sed‘3,${ >s/brown/green/ >s/lazy/active/ >}’data1.txt Thequickbrownfoxjumpsoverthelazydog. Thequickbrownfoxjumpsoverthelazydog. Thequickgreenfoxjumpsovertheactivedog. Thequickgreenfoxjumpsovertheactivedog. $ Thesededitorappliesallthecommandstoallthelinesintheaddressrange. Deletinglines Thetextsubstitutioncommandisn’ttheonlycommandavailableinthe sededitor.Ifyou needtodeletespecificlinesoftextinatextstream,youcanusethedeletecommand. The delete command, d, pretty much does what it says. It deletes any text lines that matchtheaddressingschemesupplied.Becarefulwiththe deletecommand,becauseif youforgettoincludeanaddressingscheme,allthelinesaredeletedfromthestream: $catdata1.txt Thequickbrownfoxjumpsoverthelazydog Thequickbrownfoxjumpsoverthelazydog Thequickbrownfoxjumpsoverthelazydog Thequickbrownfoxjumpsoverthelazydog $ $sed‘d’data1.txt $ Thedeletecommandisobviouslymostusefulwhenusedinconjunctionwithaspecified address.Thisallowsyoutodeletespecificlinesoftextfromthedatastream,eitherbyline number: $catdata6.txt Thisislinenumber1. Thisislinenumber2. Thisislinenumber3. Thisislinenumber4. $ $sed‘3d’data6.txt Thisislinenumber1. Thisislinenumber2. Thisislinenumber4. $ orbyaspecificrangeoflines: $sed‘2,3d’data6.txt Thisislinenumber1. Thisislinenumber4. $ orbyusingthespecialend-of-filecharacter: $sed‘3,$d’data6.txt Thisislinenumber1. Thisislinenumber2. $ Thepattern-matchingfeatureofthesededitoralsoappliestothedeletecommand: $sed‘/number1/d’data6.txt Thisislinenumber2. Thisislinenumber3. Thisislinenumber4. $ Thesededitorremovesthelinecontainingtextthatmatchesthepatternyouspecify. Note Rememberthatthesededitordoesn’ttouchtheoriginalfile.Anylinesyoudeleteare onlygonefromtheoutputofthesededitor.Theoriginalfilestillcontainsthe “deleted”lines. Youcanalsodeletearangeoflinesusingtwotextpatterns,butbecarefulifyoudothis. The first pattern you specify “turns on” the line deletion, and the second pattern “turns off” the line deletion. The sed editor deletes any lines between the two specified lines (includingthespecifiedlines): $sed‘/1/,/3/d’data6.txt Thisislinenumber4. $ Inaddition,youmustbecarefulbecausethedeletefeature“turnson”wheneverthe sed editordetectsthestartpatterninthedatastream.Thismayproduceanunexpectedresult: $catdata7.txt Thisislinenumber1. Thisislinenumber2. Thisislinenumber3. Thisislinenumber4. Thisislinenumber1again. Thisistextyouwanttokeep. Thisisthelastlineinthefile. $ $sed‘/1/,/3/d’data7.txt Thisislinenumber4. $ The second occurrence of a line with the number 1 in it triggered the deletecommand again, deleting the rest of the lines in the data stream, because the stop pattern wasn’t recognized.Ofcourse,theotherobviousproblemoccursifyouspecifyastoppatternthat neverappearsinthetext: $sed‘/1/,/5/d’data7.txt $ Becausethedeletefeatures“turnedon”atthefirstpatternmatch,butneverfoundtheend patternmatch,theentiredatastreamwasdeleted. Insertingandappendingtext Asyouwouldexpect,likeanyothereditor,thesededitorallowsyoutoinsertandappend textlinestothedatastream.Thedifferencebetweenthetwoactionscanbeconfusing: Theinsertcommand(i)addsanewlinebeforethespecifiedline. Theappendcommand(a)addsanewlineafterthespecifiedline. What is confusing about these two commands is their formats. You can’t use these commands on a single command line. You must specify the line to insert or append the linetoinsertonaseparatelinebyitself.Here’stheformatfordoingthis: sed‘[address]command\ newline’ Thetextinnewlineappearsinthesededitoroutputintheplaceyouspecify.Remember thatwhenyouusetheinsertcommand,thetextappearsbeforethedatastreamtext: $echo“TestLine2”|sed‘i\TestLine1’ TestLine1 TestLine2 $ Andwhenyouusetheappendcommand,thetextappearsafterthedatastreamtext: $echo“TestLine2”|sed‘a\TestLine1’ TestLine2 TestLine1 $ When you use the sed editor from the command line interface prompt, you get the secondary prompt to enter the new line of data. You must complete the sed editor command on this line. After you enter the ending single quotation mark, the bash shell processesthecommand: $echo“TestLine2”|sed‘i\ >TestLine1’ TestLine1 TestLine2 $ Thisworkswellforaddingtextbeforeorafterthetextinthedatastream,butwhatabout addingtextinsidethedatastream? Toinsertorappenddatainsidethedatastreamlines,youmustuseaddressingtotellthe sededitorwhereyouwantthedatatoappear.Youcanspecifyonlyasinglelineaddress when using these commands. You can match either a numeric line number or a text pattern, but you cannot use a range of addresses. This is logical, because you can only insertorappendbeforeorafterasingleline,andnotarangeoflines. Here’sanexampleofinsertinganewlinebeforeline3inthedatastream: $sed‘3i\ >Thisisaninsertedline.’data6.txt Thisislinenumber1. Thisislinenumber2. Thisisaninsertedline. Thisislinenumber3. Thisislinenumber4. $ Here’sanexampleofappendinganewlineafterline3inthedatastream: $sed‘3a\ >Thisisanappendedline.’data6.txt Thisislinenumber1. Thisislinenumber2. Thisislinenumber3. Thisisanappendedline. Thisislinenumber4. $ Thisusesthesameprocessasthe insertcommand;itjustplacesthenewtextlineafter thespecifiedlinenumber.Ifyouhaveamultilinedatastream,andyouwanttoappenda newlineoftexttotheendofadatastream,justusethedollarsign,whichrepresentsthe lastlineofdata: $sed‘$a\ >Thisisanewlineoftext.’data6.txt Thisislinenumber1. Thisislinenumber2. Thisislinenumber3. Thisislinenumber4. Thisisanewlineoftext. $ Thesameideaappliesifyouwanttoaddanewlineatthebeginningofthedatastream. Justinsertanewlinebeforelinenumberone. Toinsertorappendmorethanonelineoftext,youmustuseabackslashoneachlineof newtextuntilyoureachthelasttextlinewhereyouwanttoinsertorappendtext: $sed‘1i\ >Thisisonelineofnewtext.\ >Thisisanotherlineofnewtext.’data6.txt Thisisonelineofnewtext. Thisisanotherlineofnewtext. Thisislinenumber1. Thisislinenumber2. Thisislinenumber3. Thisislinenumber4. $ Bothofthespecifiedlinesareaddedtothedatastream. Changinglines The change command allows you to change the contents of an entire line of text in the datastream.Itworksthesamewayastheinsertandappendcommands,inthatyoumust specifythenewlineseparatelyfromtherestofthesedcommand: $sed‘3c\ >Thisisachangedlineoftext.’data6.txt Thisislinenumber1. Thisislinenumber2. Thisisachangedlineoftext. Thisislinenumber4. $ Inthisexample,the sededitorchangesthetextinlinenumber3.Youcanalsouseatext patternfortheaddress: $sed‘/number3/c\ >Thisisachangedlineoftext.’data6.txt Thisislinenumber1. Thisislinenumber2. Thisisachangedlineoftext. Thisislinenumber4. $ The text pattern change command changes any line of text in the data stream that it matches. $catdata8.txt Thisislinenumber1. Thisislinenumber2. Thisislinenumber3. Thisislinenumber4. Thisislinenumber1again. Thisisyetanotherline. Thisisthelastlineinthefile. $ $sed‘/number1/c\ >Thisisachangedlineoftext.’data8.txt Thisisachangedlineoftext. Thisislinenumber2. Thisislinenumber3. Thisislinenumber4. Thisisachangedlineoftext. Thisisyetanotherline. Thisisthelastlineinthefile. $ Youcanuseanaddressrangeinthechangecommand,buttheresultsmaynotbewhatyou expect: $sed‘2,3c\ >Thisisanewlineoftext.’data6.txt Thisislinenumber1. Thisisanewlineoftext. Thisislinenumber4. $ Insteadofchangingbothlineswiththetext,the sededitorusesthesinglelineoftextto replacebothlines. Transformingcharacters The transform command (y) is the only sed editor command that operates on a single character.Thetransformcommandusestheformat: [address]y/inchars/outchars/ The transform command performs a one-to-one mapping of the inchars and the outchars values. The first character in inchars is converted to the first character in outchars. The second character in inchars is converted to the second character in outchars.Thismappingcontinuesthroughoutthelengthofthespecifiedcharacters.Ifthe incharsandoutcharsarenotthesamelength,thesededitorproducesanerrormessage. Here’sasimpleexampleofusingthetransformcommand: $sed‘y/123/789/’data8.txt Thisislinenumber7. Thisislinenumber8. Thisislinenumber9. Thisislinenumber4. Thisislinenumber7again. Thisisyetanotherline. Thisisthelastlineinthefile. $ Asyoucanseefromtheoutput,eachinstanceofthecharactersspecifiedinthe inchars patternhasbeenreplacedbythecharacterinthesamepositionintheoutcharspattern. The transformcommandisaglobalcommand;thatis,itperformsthetransformationon anycharacterfoundinthetextlineautomatically,withoutregardtotheoccurrence: $echo“This1isatestof1try.”|sed‘y/123/456/’ This4isatestof4try. $ Thesededitortransformedbothinstancesofthematchingcharacter1inthetextline.You can’tlimitthetransformationtoaspecificoccurrenceofthecharacter. Printingrevisited The “Introducing more substitution options” section showed you how to use the p flag with the substitution command to display lines that the sed editor changed. In addition, threecommandsthatcanbeusedtoprintinformationfromthedatastream: Thepcommandtoprintatextline Theequalsign(=)commandtoprintlinenumbers Thel(lowercaseL)commandtolistaline Thefollowingsectionslookatthesethreeprintingcommandsinthesededitor. Printinglines Like the p flag in the substitution command, the p command prints a line in the sed editoroutput.Onitsown,thiscommanddoesn’toffermuchexcitement: $echo“thisisatest”|sed‘p’ thisisatest thisisatest $ Allitdoesisprintthedatatextthatyoualreadyknowisthere.Themostcommonusefor theprintcommandisprintinglinesthatcontainmatchingtextfromatextpattern: $catdata6.txt Thisislinenumber1. Thisislinenumber2. Thisislinenumber3. Thisislinenumber4. $ $sed-n‘/number3/p’data6.txt Thisislinenumber3. $ Byusingthe-noptiononthecommandline,youcansuppressalltheotherlinesandprint onlythelinethatcontainsthematchingtextpattern. Youcanalsousethisasaquickwaytoprintasubsetoflinesinadatastream: $sed-n‘2,3p’data6.txt Thisislinenumber2. Thisislinenumber3. $ Youcanalsousethe printcommandwhenyouneedtoseealinebeforeitgetsaltered, suchaswiththe substitutionor changecommand.Youcancreateascriptthatdisplays thelinebeforeit’schanged: $sed-n‘/3/{ >p >s/line/test/p >}’data6.txt Thisislinenumber3. Thisistestnumber3. $ This sededitorcommandsearchesforlinesthatcontainthenumber3andexecutestwo commands.First,thescriptusesthe pcommandtoprinttheoriginalversionoftheline; thenitusesthe scommandtosubstitutetext,alongwiththe pflagtoprinttheresulting text.Theoutputshowsboththeoriginallinetextandthenewlinetext. Printinglinenumbers The equal sign command prints the current line number for the line within the data stream. Line numbers are determined by using the newline character in the data stream. Eachtimeanewlinecharacterappearsinthedatastream,the sed editor assumes that it terminatesalineoftext: $catdata1.txt Thequickbrownfoxjumpsoverthelazydog. Thequickbrownfoxjumpsoverthelazydog. Thequickbrownfoxjumpsoverthelazydog. Thequickbrownfoxjumpsoverthelazydog. $ $sed‘=’data1.txt 1 Thequickbrownfoxjumpsoverthelazydog. 2 Thequickbrownfoxjumpsoverthelazydog. 3 Thequickbrownfoxjumpsoverthelazydog. 4 Thequickbrownfoxjumpsoverthelazydog. $ The sed editor prints the line number before the actual line of text. The equal sign commandcomesinhandyifyou’researchingforaspecifictextpatterninthedatastream: $sed-n‘/number4/{ >= >p >}’data6.txt 4 Thisislinenumber4. $ Byusingthe -noption,youcanhavethe sededitordisplayboththelinenumberandtext forthelinethatcontainsthematchingtextpattern. Listinglines The listcommand(l)allowsyoutoprintboththetextandnonprintablecharactersina data stream. Any nonprintable characters are shown using either their octal values, preceded by a backslash or the standard C-style nomenclature for common nonprintable characters,suchas∖tfortabcharacters: $catdata9.txt Thislinecontainstabs. $ $sed-n‘l’data9.txt This\tline\tcontains\ttabs.$ $ Thetabcharacterlocationsareshownwiththe∖tnomenclature.Thedollarsignattheend of the line indicates the newline character. If you have a data stream that contains an escapecharacter,thelistcommanddisplaysitusingtheoctalcodeifnecessary: $catdata10.txt Thislinecontainsanescapecharacter. $ $sed-n‘l’data10.txt Thislinecontainsanescapecharacter.\a$ $ Thedata10.txtfilecontainsanescapecontrolcode,whichgeneratesabellsound.When youusethe catcommandtodisplaythetextfile,youdon’tseetheescapecontrolcode; you just hear the sound (if your speakers are turned on). However, using the list command,youcandisplaytheescapecontrolcodeused. Usingfileswithsed The substitutioncommandcontainsflagsthatallowyoutoworkwithfiles.Thereare alsoregularsededitorcommandsthatletyoudothatwithouthavingtosubstitutetext. Writingtoafile Thewcommandisusedtowritelinestoafile.Here’stheformatforthewcommand: [address]wfilename Thefilenamecanbespecifiedaseitherarelativeorabsolutepathname,butineithercase, thepersonrunningthesededitormusthavewritepermissionsforthefile.Theaddresscan beanytypeofaddressingmethodusedinsed,suchasasinglelinenumber,atextpattern, orarangeoflinenumbersortextpatterns. Here’sanexamplethatprintsonlythefirsttwolinesofadatastreamtoatextfile: $sed‘1,2wtest.txt’data6.txt Thisislinenumber1. Thisislinenumber2. Thisislinenumber3. Thisislinenumber4. $ $cattest.txt Thisislinenumber1. Thisislinenumber2. $ Ofcourse,ifyoudon’twantthelinestodisplayon STDOUT,youcanusethe -noptionfor thesedcommand. Thisisagreattooltouseifyouneedtocreateadatafilefromamasterfileonthebasisof commontextvalues,suchasthoseinamailinglist: $catdata11.txt Blum,RBrowncoat McGuiness,AAlliance Bresnahan,CBrowncoat Harken,CAlliance $ $sed-n‘/Browncoat/wBrowncoats.txt’data11.txt $ $catBrowncoats.txt Blum,RBrowncoat Bresnahan,CBrowncoat $ Thesededitorwritestoadestinationfileonlythedatalinesthatcontainthetextpattern. Readingdatafromafile You’vealreadyseenhowtoinsertdataintoandappendtexttoadatastreamfromthe sed commandline.The readcommand(r)allowsyoutoinsertdatacontainedinaseparate file. Here’stheformatofthereadcommand: [address]rfilename The filenameparameterspecifieseitheranabsoluteorrelativepathnameforthefilethat containsthedata.Youcan’tusearangeofaddressesforthereadcommand.Youcanonly specifyasinglelinenumberortextpatternaddress.The sededitorinsertsthetextfrom thefileaftertheaddress. $catdata12.txt Thisisanaddedline. Thisisthesecondaddedline. $ $sed‘3rdata12.txt’data6.txt Thisislinenumber1. Thisislinenumber2. Thisislinenumber3. Thisisanaddedline. Thisisthesecondaddedline. Thisislinenumber4. $ The sed editor inserts into the data stream all the text lines in the data file. The same techniqueworkswhenusingatextpatternaddress: $sed‘/number2/rdata12.txt’data6.txt Thisislinenumber1. Thisislinenumber2. Thisisanaddedline. Thisisthesecondaddedline. Thisislinenumber3. Thisislinenumber4. $ Ifyouwanttoaddtexttotheendofadatastream,justusethedollarsignaddresssymbol: $sed‘$rdata12.txt’data6.txt Thisislinenumber1. Thisislinenumber2. Thisislinenumber3. Thisislinenumber4. Thisisanaddedline. Thisisthesecondaddedline. $ A cool application of the read command is to use it in conjunction with a delete command to replace a placeholder in a file with data from another file. For example, supposethatyouhadaformstoredinatextfilethatlookedlikethis: $catnotice.std Wouldthefollowingpeople: LIST pleasereporttotheship’scaptain. $ TheformletterusesthegenericplaceholderLISTinplaceofalistofpeople.Toinsertthe list of people after the placeholder, you just use the read command. However, this still leavestheplaceholdertextintheoutput.Toremovethat,justusethe deletecommand. Theresultlookslikethis: $sed‘/LIST/{ >rdata11.txt >d >}’notice.std Wouldthefollowingpeople: Blum,RBrowncoat McGuiness,AAlliance Bresnahan,CBrowncoat Harken,CAlliance pleasereporttotheship’scaptain. $ Nowtheplaceholdertextisreplacedwiththelistofnamesfromthedatafile. Summary Shellscriptscandolotsofworkontheirown,butit’softendifficulttomanipulatedata withjustashellscript.Linuxprovidestwohandyutilitiestohelpwithhandlingtextdata. The sededitorisastreameditorthatquicklyprocessesdataontheflyasitreadsit.You mustprovidethesededitorwithalistofeditingcommands,whichitappliestothedata. ThegawkprogramisautilityfromtheGNUorganizationthatmimicsandexpandsonthe functionalityoftheUnixawkprogram.Thegawkprogramcontainsabuilt-inprogramming languagethatyoucanusetowritescriptstohandleandprocessdata.Youcanusethegawk programtoextractdataelementsfromlargedatafilesandoutputtheminjustaboutany formatyoudesire.Thismakesprocessinglargelogfilesasnap,aswellascreatingcustom reportsfromdatafiles. Acrucialelementofusingboththesedandgawkprogramsisknowinghowtouseregular expressions.Regularexpressionsarekeytocreatingcustomizedfiltersforextractingand manipulatingdataintextfiles.Thenextchapterdivesintotheoftenmisunderstoodworld ofregularexpressions,showingyouhowtobuildregularexpressionsformanipulatingall typesofdata. Chapter20 RegularExpressions InThisChapter 1. Definingregularexpressions 2. Lookingatthebasics 3. Extendingourpatterns 4. Creatingexpressions Thekeytosuccessfullyworkingwiththesededitorandthegawkprograminyour shellscriptisyourcomfortusingregularexpressions.Thisisnotalwaysaneasy thingtodo,becausetryingtofilterspecificdatafromalargebatchofdatacan(and oftendoes)getcomplicated.Thischapterdescribeshowtocreateregularexpressions inboththesededitorandthegawkprogramthatcanfilteroutjustthedatayouneed. WhatAreRegularExpressions? Thefirststeptounderstandingregularexpressionsistodefinejustexactlywhattheyare. ThissectionexplainswhataregularexpressionisanddescribeshowLinuxusesregular expressions. Adefinition AregularexpressionisapatterntemplateyoudefinethataLinuxutilityusestofiltertext. A Linux utility (such as the sed editor or the gawk program) matches the regular expressionpatternagainstdataasthatdataflowsintotheutility.Ifthedatamatchesthe pattern, it’s accepted for processing. If the data doesn’t match the pattern, it’s rejected. ThisisillustratedinFigure20.1. Figure20.1Matchingdataagainstaregularexpressionpattern Theregularexpressionpatternmakesuseofwildcardcharacterstorepresentoneormore charactersinthedatastream.ThereareplentyofinstancesinLinuxwhereyoucanspecify a wildcard character to represent data you don’t know about. You’ve already seen an example of using wildcard characters with the Linux ls command for listing files and directories(seeChapter3). Theasteriskwildcardcharacterallowsyoutolistonlyfilesthatmatchacertaincriteria. Forexample: $ls-alda* -rw-r—r—1richrich45Nov2612:42data -rw-r—r—1richrich25Dec412:40data.tst -rw-r—r—1richrich180Nov2612:42data1 -rw-r—r—1richrich45Nov2612:44data2 -rw-r—r—1richrich73Nov2712:31data3 -rw-r—r—1richrich79Nov2814:01data4 -rw-r—r—1richrich187Dec409:45datatest $ The da*parameterinstructsthe lscommandtolistonlythefileswhosenamestartswith da.Therecanbeanynumberofcharactersafterthe dainthefilename(includingnone). Thelscommandreadstheinformationregardingallthefilesinthedirectorybutdisplays onlytheonesthatmatchthewildcardcharacter. Regular expression wildcard patterns work in a similar way. The regular expression patterncontainstextand/orspecialcharactersthatdefineatemplateforthesededitorand the gawkprogramtofollowwhenmatchingdata.Youcanusedifferentspecialcharacters inaregularexpressiontodefineaspecificpatternforfilteringdata. Typesofregularexpressions Thebiggestproblemwithusingregularexpressionsisthatthereisn’tjustonesetofthem. Several different applications use different types of regular expressions in the Linux environment. These include such diverse applications as programming languages (Java, Perl,andPython),Linuxutilities(suchasthe sededitor,the gawkprogram,andthegrep utility), and mainstream applications (such as the MySQL and PostgreSQL database servers). A regular expression is implemented using a regular expression engine. A regular expressionengineistheunderlyingsoftwarethatinterpretsregularexpressionpatternsand usesthosepatternstomatchtext. TheLinuxworldhastwopopularregularexpressionengines: ThePOSIXBasicRegularExpression(BRE)engine ThePOSIXExtendedRegularExpression(ERE)engine Most Linux utilities at a minimum conform to the POSIX BRE engine specifications, recognizing all the pattern symbols it defines. Unfortunately, some utilities (such as the sededitor)conformonlytoasubsetoftheBREenginespecifications.Thisisduetospeed constraints,becausethesededitorattemptstoprocesstextinthedatastreamasquicklyas possible. The POSIX ERE engine is often found in programming languages that rely on regular expressions for text filtering. It provides advanced pattern symbols as well as special symbols for common patterns, such as matching digits, words, and alphanumeric characters. The gawk program uses the ERE engine to process its regular expression patterns. Because there are so many different ways to implement regular expressions, it’s hard to presentasingle,concisedescriptionofallthepossibleregularexpressions.Thefollowing sections discuss the most commonly found regular expressions and demonstrate how to usetheminthesededitorandgawkprogram. DefiningBREPatterns The most basic BRE pattern is matching text characters in a data stream. This section demonstrateshowyoucandefinetextintheregularexpressionpatternandwhattoexpect fromtheresults. Plaintext Chapter18demonstratedhowtousestandardtextstringsinthe sededitorandthe gawk programtofilterdata.Here’sanexampletorefreshyourmemory: $echo“Thisisatest”|sed-n‘/test/p’ Thisisatest $echo“Thisisatest”|sed-n‘/trial/p’ $ $echo“Thisisatest”|gawk‘/test/{print$0}’ Thisisatest $echo“Thisisatest”|gawk‘/trial/{print$0}’ $ Thefirstpatterndefinesasingleword,test.Thesededitorand gawkprogramscriptseach use their own version of the print command to print any lines that match the regular expressionpattern.Becausethe echostatementcontainstheword“test”inthetextstring, the data stream text matches the defined regular expression pattern, and the sed editor displaystheline. Thesecondpatternagaindefinesjustasingleword,thistimetheword“trial.”Becausethe echostatementtextstringdoesn’tcontainthatword,theregularexpressionpatterndoesn’t match,soneitherthesededitornorthegawkprogramprintstheline. You probably already noticed that the regular expression doesn’t care where in the data streamthepatternoccurs.Italsodoesn’tmatterhowmanytimesthepatternoccurs.After the regular expression can match the pattern anywhere in the text string, it passes the stringalongtotheLinuxutilitythat’susingit. Thekeyismatchingtheregularexpressionpatterntothedatastreamtext.It’simportantto rememberthatregularexpressionsareextremelypickyaboutmatchingpatterns.Thefirst ruletorememberisthatregularexpressionpatternsarecasesensitive.Thismeansthey’ll matchonlythosepatternswiththepropercaseofcharacters: $echo“Thisisatest”|sed-n‘/this/p’ $ $echo“Thisisatest”|sed-n‘/This/p’ Thisisatest $ Thefirstattemptfailedtomatchbecausetheword“this”doesn’tappearinalllowercasein the text string, while the second attempt, which uses the uppercase letter in the pattern, workedjustfine. Youdon’thavetolimityourselftowholewordsintheregularexpression.Ifthedefined textappearsanywhereinthedatastream,theregularexpressionmatchesthefollowing: $echo“Thebooksareexpensive”|sed-n‘/book/p’ Thebooksareexpensive $ Even though the text in the data stream is books, the data in the stream contains the regularexpressionbook,sotheregularexpressionpatternmatchesthedata.Ofcourse,if youtrytheopposite,theregularexpressionfails: $echo“Thebookisexpensive”|sed-n‘/books/p’ $ Thecompleteregularexpressiontextdidn’tappearinthedatastream,sothematchfailed andthesededitordidn’tdisplaythetext. Youalsodon’thavetolimityourselftosingletextwordsintheregularexpression.You canincludespacesandnumbersinyourtextstringaswell: $echo“Thisislinenumber1”|sed-n‘/ber1/p’ Thisislinenumber1 $ Spacesaretreatedjustlikeanyothercharacterintheregularexpression: $echo“Thisislinenumber1”|sed-n‘/ber1/p’ $ Ifyoudefineaspaceintheregularexpression,itmustappearinthedatastream.Youcan evencreatearegularexpressionpatternthatmatchesmultiplecontiguousspaces: $catdata1 Thisisanormallineoftext. Thisisalinewithtoomanyspaces. $sed-n‘//p’data1 Thisisalinewithtoomanyspaces. $ Thelinewithtwospacesbetweenwordsmatchestheregularexpressionpattern.Thisisa greatwaytocatchspacingproblemsintextfiles! Specialcharacters Asyouusetextstringsinyourregularexpressionpatterns,there’ssomethingyouneedto be aware of. There are a few exceptions when defining text characters in a regular expression. Regular expression patterns assign a special meaning to a few characters. If you try to use these characters in your text pattern, you won’t get the results you were expecting. Thesespecialcharactersarerecognizedbyregularexpressions: .*[]^${}\+?|() Asthechapterprogresses,you’llfindoutjustwhatthesespecialcharactersdoinaregular expression. For now, however, just remember that you can’t use these characters by themselvesinyourtextpattern. Ifyouwanttouseoneofthespecialcharactersasatextcharacter,youneedtoescapeit. When you escape the special characters, you add a special character in front of it to indicate to the regular expression engine that it should interpret the next character as a normaltextcharacter.Thespecialcharacterthatdoesthisisthebackslashcharacter(∖). For example, if you want to search for a dollar sign in your text, just precede it with a backslashcharacter: $catdata2 Thecostis$4.00 $sed-n‘/\$/p’data2 Thecostis$4.00 $ Becausethebackslashisaspecialcharacter,ifyouneedtouseitinaregularexpression pattern,youneedtoescapeitaswell,producingadoublebackslash: $echo“\isaspecialcharacter”|sed-n‘/[[BackslashBackslash]]/p’ \isaspecialcharacter $ Finally,althoughtheforwardslashisn’taregularexpressionspecialcharacter,ifyouuse it in your regular expression pattern in the sed editor or the gawk program, you get an error: $echo“3/2”|sed-n‘///p’ sed:-eexpression#1,char2:Nopreviousregularexpression $ Touseaforwardslash,youneedtoescapethataswell: $echo“3/2”|sed-n‘/\//p’ 3/2 $ Nowthesededitorcanproperlyinterprettheregularexpressionpattern,andalliswell. Anchorcharacters Asshowninthe“PlainText”section,bydefault,whenyouspecifyaregularexpression pattern,ifthepatternappearsanywhereinthedatastream,itmatches.Youcanusetwo specialcharacterstoanchorapatterntoeitherthebeginningortheendoflinesinthedata stream. Startingatthebeginning Thecaretcharacter(∧)definesapatternthatstartsatthebeginningofalineoftextinthe datastream.Ifthepatternislocatedanyplaceotherthanthestartofthelineoftext,the regularexpressionpatternfails. To use the caret character, you must place it before the pattern specified in the regular expression: $echo“Thebookstore”|sed-n‘/^book/p’ $ $echo“Booksaregreat”|sed-n‘/^Book/p’ Booksaregreat $ Thecaretanchorcharacterchecksforthepatternatthebeginningofeachnewlineofdata, asdeterminedbythenewlinecharacter: $catdata3 Thisisatestline. thisisanothertestline. Alinethatteststhisfeature. Yetmoretestingofthis $sed-n‘/^this/p’data3 thisisanothertestline. $ Aslongasthepatternappearsatthestartofanewline,thecaretanchorcatchesit. Ifyoupositionthecaretcharacterinanyplaceotherthanatthebeginningofthepattern,it actslikeanormalcharacterandnotasaspecialcharacter: $echo“This^isatest”|sed-n‘/s^/p’ This^isatest $ Becausethecaretcharacterislistedlastintheregularexpressionpattern,the sededitor usesitasanormalcharactertomatchtext. Note Ifyouneedtospecifyaregularexpressionpatternusingonlythecaretcharacter,you don’tneedtoescapeitwithabackslash.However,ifyouspecifythecaretcharacter first,followedbyadditionaltextinthepattern,youneedtousetheescapecharacter beforethecaretcharacter. Lookingfortheending Theoppositeoflookingforapatternatthestartofalineislookingforitattheendofa line. The dollar sign ($) special character defines the end anchor. Add this special characterafteratextpatterntoindicatethatthelineofdatamustendwiththetextpattern: $echo“Thisisagoodbook”|sed-n‘/book$/p’ Thisisagoodbook $echo“Thisbookisgood”|sed-n‘/book$/p’ $ Theproblemwithanendingtextpatternisthatyoumustbecarefulwhatyou’relooking for: $echo“Therearealotofgoodbooks”|sed-n‘/book$/p’ $ Makingtheword“book”pluralattheendofthelinemeansthatitnolongermatchesthe regularexpressionpattern,eventhoughbookisinthedatastream.Thetextpatternmust bethelastthingonthelineforthepatterntomatch. Combininganchors Insomecommonsituations,youcancombineboththestartandendanchoronthesame line.Inthefirstsituation,supposeyouwanttolookforalineofdatacontainingonlya specifictextpattern: $catdata4 thisisatestofusingbothanchors Isaidthisisatest thisisatest I’msurethisisatest. $sed-n‘/^thisisatest$/p’data4 thisisatest $ Thesededitorignoresthelinesthatincludeothertextbesidesthespecifiedtext. Thesecondsituationmayseemalittleoddatfirstbutisextremelyuseful.Bycombining both anchors in a pattern with no text, you can filter blank lines from the data stream. Considerthisexample: $catdata5 Thisisonetestline. Thisisanothertestline. $sed‘/^$/d’data5 Thisisonetestline. Thisisanothertestline. $ Theregularexpressionpatternthatisdefinedlooksforlinesthathavenothingbetweenthe start and end of the line. Because blank lines contain no text between the two newline characters, they match the regular expression pattern. The sed editor uses the d delete commandtodeletelinesthatmatchtheregularexpressionpattern,thusremovingallblank linesfromthetext.Thisisaneffectivewaytoremoveblanklinesfromdocuments. Thedotcharacter Thedotspecialcharacterisusedtomatchanysinglecharacterexceptanewlinecharacter. Thedotcharactermustmatchacharacter,however;ifthere’snocharacterintheplaceof thedot,thenthepatternfails. Let’slookatafewexamplesofusingthedotcharacterinaregularexpressionpattern: $catdata6 Thisisatestofaline. Thecatissleeping. Thatisaverynicehat. Thistestisatlinefour. atteno’clockwe’llgohome. $sed-n‘/.at/p’data6 Thecatissleeping. Thatisaverynicehat. Thistestisatlinefour. $ You should be able to figure out why the first line failed and why the second and third linespassed.Thefourthlineisalittletricky.Noticethatwematchedtheat,butthere’sno characterinfrontofittomatchthedotcharacter.Ah,butthereis!Inregularexpressions, spacescountascharacters,sothespaceinfrontofthe atmatchesthepattern.Thefifth lineprovesthis,byputtingtheatinthefrontoftheline,whichfailstomatchthepattern. Characterclasses Thedotspecialcharacterisgreatformatchingacharacterpositionagainstanycharacter, butwhatifyouwanttolimitwhatcharacterstomatch?Thisiscalledacharacterclassin regularexpressions. Youcandefineaclassofcharactersthatwouldmatchapositioninatextpattern.Ifoneof thecharactersfromthecharacterclassisinthedatastream,itmatchesthepattern. To define a character class, you use square brackets. The brackets should contain any characteryouwanttoincludeintheclass.Youthenusetheentireclasswithinapattern justlikeanyotherwildcardcharacter.Thistakesalittlegettingusedtoatfirst,butafter youcatchon,itcangeneratesomeprettyamazingresults. Thefollowingisanexampleofcreatingacharacterclass: $sed-n‘/[ch]at/p’data6 Thecatissleeping. Thatisaverynicehat. $ Using the same data file as in the dot special character example, we came up with a differentresult.Thistimewemanagedtofilteroutthelinethatjustcontainedthewordat. The only words that match this pattern are cat and hat. Also notice that the line that startedwith atdidn’tmatchaswell.Theremustbeacharacterinthecharacterclassthat matchestheappropriateposition. Characterclassescomeinhandyifyou’renotsurewhichcaseacharacterisin: $echo“Yes”|sed-n‘/[Yy]es/p’ Yes $echo“yes”|sed-n‘/[Yy]es/p’ yes $ Youcanusemorethanonecharacterclassinasingleexpression: $echo“Yes”|sed-n‘/[Yy][Ee][Ss]/p’ Yes $echo“yEs”|sed-n‘/[Yy][Ee][Ss]/p’ yEs $echo“yeS”|sed-n‘/[Yy][Ee][Ss]/p’ yeS $ Theregularexpressionusedthreecharacterclassestocoverbothloweranduppercases forallthreecharacterpositions. Characterclassesdon’thavetocontainjustletters;youcanusenumbersinthemaswell: $catdata7 Thislinedoesn’tcontainanumber. Thislinehas1numberonit. Thislineanumber2onit. Thislinehasanumber4onit. $sed-n‘/[0123]/p’data7 Thislinehas1numberonit. Thislineanumber2onit. $ The regular expression pattern matches any lines that contain the numbers 0, 1, 2, or 3. Anyothernumbersareignored,asarelineswithoutnumbersinthem. You can combine character classes to check for properly formatted numbers, such as phonenumbersandZIPcodes.However,whenyou’retryingtomatchaspecificformat, youmustbecareful.Here’sanexampleofaZIPcodematchgonewrong: $catdata8 60633 46201 223001 4353 22203 $sed-n‘ >/[0123456789][0123456789][0123456789][0123456789][0123456789]/p >’data8 60633 46201 223001 22203 $ Thismightnothaveproducedtheresultyouwerethinkingof.Itdidafinejoboffiltering outthenumberthatwastooshorttobeaZIPcode,becausethelastcharacterclassdidn’t have a character to match against. However, it still passed the six-digit number, even thoughweonlydefinedfivecharacterclasses. Remember that the regular expression pattern can be found anywhere in the text of the data stream. You may always have additional characters besides the matching pattern characters.Ifyouwanttoensurethatyoumatchagainstonlyfivenumbers,youneedto delineatethemsomehow,eitherwithspaces,orasinthisexample,byshowingthatthey’re atthestartandendoftheline: $sed-n‘ >/^[0123456789][0123456789][0123456789][0123456789][0123456789]$/p >‘data8 60633 46201 22203 $ Nowthat’smuchbetter!Laterinthischapter,welookathowtosimplifythisevenfurther. Oneextremelypopularuseforcharacterclassesisparsingwordsthatmightbemisspelled, suchasdataenteredfromauserform.Youcaneasilycreateregularexpressionsthatcan acceptcommonmisspellingsindata: $catdata9 Ineedtohavesomemaintenencedoneonmycar. I’llpaythatinaseperateinvoice. AfterIpayforthemaintenancemycarwillbeasgoodasnew. $sed-n‘ /maint[ea]n[ae]nce/p /sep[ea]r[ea]te/p ‘data9 Ineedtohavesomemaintenencedoneonmycar. I’llpaythatinaseperateinvoice. AfterIpayforthemaintenancemycarwillbeasgoodasnew. $ Thetwo sedprintcommandsinthisexampleutilizeregularexpressioncharacterclasses to help catch the misspelled words, maintenance and separate, in the text. The same regularexpressionpatternalsomatchestheproperlyspelledoccurrenceof“maintenance.” Negatingcharacterclasses Inregularexpressionpatterns,youcanalsoreversetheeffectofacharacterclass.Instead oflookingforacharactercontainedintheclass,youcanlookforanycharacterthat’snot intheclass.Todothat,justplaceacaretcharacteratthebeginningofthecharacterclass range: $sed-n‘/[^ch]at/p’data6 Thistestisatlinefour. $ Bynegatingthecharacterclass,theregularexpressionpatternmatchesanycharacterthat’s neither a c nor an h, along with the text pattern. Because the space character fits this category,itpassedthepatternmatch.However,evenwiththenegation,thecharacterclass must still match a character, so the line with the at in the start of the line still doesn’t matchthepattern. Usingranges YoumayhavenoticedwhenIshowedtheZIPcodeexampleearlierthatitwassomewhat awkwardhavingtolistallthepossibledigitsineachcharacterclass.Fortunately,youcan useashortcutsoyoudon’thavetodothat. Youcanusearangeofcharacterswithinacharacterclassbyusingthedashsymbol.Just specifythefirstcharacterintherange,adash,andthenthelastcharacterintherange.The regular expression includes any character that’s within the specified character range, accordingtothecharactersetusedbytheLinuxsystem(seeChapter2). NowyoucansimplifytheZIPcodeexamplebyspecifyingarangeofdigits: $sed-n‘/^[0-9][0-9][0-9][0-9][0-9]$/p’data8 60633 46201 45902 $ Thatsavedlotsoftyping!Eachcharacterclassmatchesanydigitfrom0to9.Thepattern failsifaletterispresentanywhereinthedata: $echo“a8392”|sed-n‘/^[0-9][0-9][0-9][0-9][0-9]$/p’ $ $echo“1839a”|sed-n‘/^[0-9][0-9][0-9][0-9][0-9]$/p’ $ $echo“18a92”|sed-n‘/^[0-9][0-9][0-9][0-9][0-9]$/p’ $ Thesametechniqueworkswithletters: $sed-n‘/[c-h]at/p’data6 Thecatissleeping. Thatisaverynicehat. $ Thenewpattern [c-h]atmatcheswordswherethefirstletterisbetweenthelettercand theletterh.Inthiscase,thelinewithonlythewordatfailedtomatchthepattern. Youcanalsospecifymultiple,non-continuousrangesinasinglecharacterclass: $sed-n‘/[a-ch-m]at/p’data6 Thecatissleeping. Thatisaverynicehat. $ Thecharacterclassallowstherangesathroughc,andhthroughmtoappearbeforetheat text.Thisrangewouldrejectanylettersbetweendandg: $echo“I’mgettingtoofat.”|sed-n‘/[a-ch-m]at/p’ $ Thispatternrejectedthefattext,asitwasn’tinthespecifiedrange. Specialcharacterclasses In addition to defining your own character classes, the BRE contains special character classesyoucanusetomatchagainstspecifictypesofcharacters.Table20.1describesthe BREspecialcharactersyoucanuse. Table20.1BRESpecialCharacterClasses Class [[:alpha:]] [[:alnum:]] [[:blank:]] [[:digit:]] [[:lower:]] [[:print:]] [[:punct:]] [[:space:]] [[:upper:]] Description Matchesanyalphabeticalcharacter,eitherupperorlowercase Matchesanyalphanumericcharacter0–9,A–Z,ora–z MatchesaspaceorTabcharacter Matchesanumericaldigitfrom0through9 Matchesanylowercasealphabeticalcharactera–z Matchesanyprintablecharacter Matchesapunctuationcharacter Matchesanywhitespacecharacter:space,Tab,NL,FF,VT,CR MatchesanyuppercasealphabeticalcharacterA–Z Youusethespecialcharacterclassesjustasyouwouldanormalcharacterclassinyour regularexpressionpatterns: $echo“abc”|sed-n‘/[[:digit:]]/p’ $ $echo“abc”|sed-n‘/[[:alpha:]]/p’ abc $echo“abc123”|sed-n‘/[[:digit:]]/p’ abc123 $echo“Thisis,atest”|sed-n‘/[[:punct:]]/p’ Thisis,atest $echo“Thisisatest”|sed-n‘/[[:punct:]]/p’ $ Usingthespecialcharacterclassesisaneasywaytodefineranges.Insteadofhavingto usearange[0–9],youcanjustuse[[:digit:]]. Theasterisk Placinganasteriskafteracharactersignifiesthatthecharactermustappearzeroormore timesinthetexttomatchthepattern: $echo“ik”|sed-n‘/ie*k/p’ ik $echo“iek”|sed-n‘/ie*k/p’ iek $echo“ieek”|sed-n‘/ie*k/p’ ieek $echo“ieeek”|sed-n‘/ie*k/p’ ieeek $echo“ieeeek”|sed-n‘/ie*k/p’ ieeeek $ This pattern symbol is commonly used for handling words that have a common misspellingorvariationsinlanguagespellings.Forexample,ifyouneedtowriteascript thatmaybeusedineitherAmericanorBritishEnglish,youcouldwrite: $echo“I’mgettingacolorTV”|sed-n‘/colou*r/p’ I’mgettingacolorTV $echo“I’mgettingacolourTV”|sed-n‘/colou*r/p’ I’mgettingacolourTV $ Theu*inthepatternindicatesthattheletterumayormaynotappearinthetexttomatch the pattern. Similarly, if you know of a word that is commonly misspelled, you can accommodateitbyusingtheasterisk: $echo“Iateapotatoewithmylunch.”|sed-n‘/potatoe*/p’ Iateapotatoewithmylunch. $echo“Iateapotatowithmylunch.”|sed-n‘/potatoe*/p’ Iateapotatowithmylunch. $ Placing an asterisk next to the possible extra letter allows you to accept the misspelled word. Another handy feature is combining the dot special character with the asterisk special character.Thiscombinationprovidesapatterntomatchanynumberofanycharacters.It’s oftenusedbetweentwotextstringsthatmayormaynotappearnexttoeachotherinthe datastream: $echo“thisisaregularpatternexpression”|sed-n‘ >/regular.*expression/p’ thisisaregularpatternexpression $ Usingthispattern,youcaneasilysearchformultiplewordsthatmayappearanywhereina lineoftextinthedatastream. Theasteriskcanalsobeappliedtoacharacterclass.Thisallowsyoutospecifyagroupor rangeofcharactersthatcanappearmorethanonceinthetext: $echo“bt”|sed-n‘/b[ae]*t/p’ bt $echo“bat”|sed-n‘/b[ae]*t/p’ bat $echo“bet”|sed-n‘/b[ae]*t/p’ bet $echo“btt”|sed-n‘/b[ae]*t/p’ btt $ $echo“baat”|sed-n‘/b[ae]*t/p’ baat $echo“baaeeet”|sed-n‘/b[ae]*t/p’ baaeeet $echo“baeeaeeat”|sed-n‘/b[ae]*t/p’ baeeaeeat $echo“baakeeet”|sed-n‘/b[ae]*t/p’ $ Aslongastheaandecharactersappearinanycombinationbetweenthebandtcharacters (includingnotappearingatall),thepatternmatches.Ifanyothercharacteroutsideofthe definedcharacterclassappears,thepatternmatchfails. ExtendedRegularExpressions ThePOSIXEREpatternsincludeafewadditionalsymbolsthatareusedbysomeLinux applications and utilities. The gawk program recognizes the ERE patterns, but the sed editordoesn’t. Caution Rememberthattheregularexpressionenginesinthesededitorandthegawkprogram aredifferent.Thegawkprogramcanusemostoftheextendedregularexpression patternsymbols,anditcanprovidesomeadditionalfilteringcapabilitiesthatthesed editordoesn’thave.However,becauseofthis,itisoftenslowerinprocessingdata streams. ThissectiondescribesthemorecommonlyfoundEREpatternsymbolsthatyoucanusein yourgawkprogramscripts. Thequestionmark The question mark is similar to the asterisk, but with a slight twist. The question mark indicatesthattheprecedingcharactercanappearzerooronetime,butthat’sall.Itdoesn’t matchrepeatingoccurrencesofthecharacter: $echo“bt”|gawk‘/be?t/{print$0}’ bt $echo“bet”|gawk‘/be?t/{print$0}’ bet $echo“beet”|gawk‘/be?t/{print$0}’ $ $echo“beeet”|gawk‘/be?t/{print$0}’ $ Iftheecharacterdoesn’tappearinthetext,oraslongasitappearsonlyonceinthetext, thepatternmatches. Aswiththeasterisk,youcanusethequestionmarksymbolalongwithacharacterclass: $echo“bt”|gawk‘/b[ae]?t/{print$0}’ bt $echo“bat”|gawk‘/b[ae]?t/{print$0}’ bat $echo“bot”|gawk‘/b[ae]?t/{print$0}’ $ $echo“bet”|gawk‘/b[ae]?t/{print$0}’ bet $echo“baet”|gawk‘/b[ae]?t/{print$0}’ $ $echo“beat”|gawk‘/b[ae]?t/{print$0}’ $ $echo“beet”|gawk‘/b[ae]?t/{print$0}’ $ If zero or one character from the character class appears, the pattern match passes. However,ifbothcharactersappear,orifoneofthecharactersappearstwice,thepattern matchfails. Theplussign Theplussignisanotherpatternsymbolthat’ssimilartotheasterisk,butwithadifferent twist than the question mark. The plus sign indicates that the preceding character can appearoneormoretimes,butmustbepresentatleastonce.Thepatterndoesn’tmatchif thecharacterisnotpresent: $echo“beeet”|gawk‘/be+t/{print$0}’ beeet $echo“beet”|gawk‘/be+t/{print$0}’ beet $echo“bet”|gawk‘/be+t/{print$0}’ bet $echo“bt”|gawk‘/be+t/{print$0}’ $ If the e character is not present, the pattern match fails. The plus sign also works with characterclasses,thesamewayastheasteriskandquestionmarkdo: $echo“bt”|gawk‘/b[ae]+t/{print$0}’ $ $echo“bat”|gawk‘/b[ae]+t/{print$0}’ bat $echo“bet”|gawk‘/b[ae]+t/{print$0}’ bet $echo“beat”|gawk‘/b[ae]+t/{print$0}’ beat $echo“beet”|gawk‘/b[ae]+t/{print$0}’ beet $echo“beeat”|gawk‘/b[ae]+t/{print$0}’ beeat $ This time if either character defined in the character class appears, the text matches the specifiedpattern. Usingbraces CurlybracesareavailableinEREtoallowyoutospecifyalimitonarepeatableregular expression. This is often referred to as an interval. You can express the interval in two formats: m:Theregularexpressionappearsexactlymtimes. m,n:Theregularexpressionappearsatleastmtimes,butnomorethanntimes. This feature allows you to fine-tune exactly how many times you allow a character (or characterclass)toappearinapattern. Caution Bydefault,thegawkprogramdoesn’trecognizeregularexpressionintervals.You mustspecifythe—re-intervalcommandlineoptionforthegawkprogramto recognizeregularexpressionintervals. Here’sanexampleofusingasimpleintervalofonevalue: $echo“bt”|gawk—re-interval‘/be{1}t/{print$0}’ $ $echo“bet”|gawk—re-interval‘/be{1}t/{print$0}’ bet $echo“beet”|gawk—re-interval‘/be{1}t/{print$0}’ $ By specifying an interval of one, you restrict the number of times the character can be presentforthestringtomatchthepattern.Ifthecharacterappearsmoretimes,thepattern matchfails. Often,specifyingthelowerandupperlimitcomesinhandy: $echo“bt”|gawk—re-interval‘/be{1,2}t/{print$0}’ $ $echo“bet”|gawk—re-interval‘/be{1,2}t/{print$0}’ bet $echo“beet”|gawk—re-interval‘/be{1,2}t/{print$0}’ beet $echo“beeet”|gawk—re-interval‘/be{1,2}t/{print$0}’ $ Inthisexample,the e character can appear once or twice for the pattern match to pass; otherwise,thepatternmatchfails. Theintervalpatternmatchalsoappliestocharacterclasses: $echo“bt”|gawk—re-interval‘/b[ae]{1,2}t/{print$0}’ $ $echo“bat”|gawk—re-interval‘/b[ae]{1,2}t/{print$0}’ bat $echo“bet”|gawk—re-interval‘/b[ae]{1,2}t/{print$0}’ bet $echo“beat”|gawk—re-interval‘/b[ae]{1,2}t/{print$0}’ beat $echo“beet”|gawk—re-interval‘/b[ae]{1,2}t/{print$0}’ beet $echo“beeat”|gawk—re-interval‘/b[ae]{1,2}t/{print$0}’ $ $echo“baeet”|gawk—re-interval‘/b[ae]{1,2}t/{print$0}’ $ $echo“baeaet”|gawk—re-interval‘/b[ae]{1,2}t/{print$0}’ $ This regular expression pattern matches if there are exactly one or two instances of the letteraoreinthetextpattern,butitfailsifthereareanymoreinanycombination. Thepipesymbol Thepipesymbolallowstoyoutospecifytwoormorepatternsthattheregularexpression engineusesinalogicalORformulawhenexaminingthedatastream.Ifanyofthepatterns matchthedatastreamtext,thetextpasses.Ifnoneofthepatternsmatch,thedatastream textfails. Here’stheformatforusingthepipesymbol: expr1|expr2|… Here’sanexampleofthis: $echo“Thecatisasleep”|gawk‘/cat|dog/{print$0}’ Thecatisasleep $echo“Thedogisasleep”|gawk‘/cat|dog/{print$0}’ Thedogisasleep $echo“Thesheepisasleep”|gawk‘/cat|dog/{print$0}’ $ This example looks for the regular expression cat or dog in the data stream. You can’t placeanyspaceswithintheregularexpressionsandthepipesymbol,orthey’readdedto theregularexpressionpattern. Theregularexpressionsoneithersideofthepipesymbolcanuseanyregularexpression pattern,includingcharacterclasses,todefinethetext: $echo“Hehasahat.”|gawk‘/[ch]at|dog/{print$0}’ Hehasahat. $ Thisexamplewouldmatchcat,hat,ordoginthedatastreamtext. Groupingexpressions Regularexpressionpatternscanalsobegroupedbyusingparentheses.Whenyougroupa regularexpressionpattern,thegroupistreatedlikeastandardcharacter.Youcanapplya specialcharactertothegroupjustasyouwouldtoaregularcharacter.Forexample: $echo“Sat”|gawk‘/Sat(urday)?/{print$0}’ Sat $echo“Saturday”|gawk‘/Sat(urday)?/{print$0}’ Saturday $ The grouping of the “urday” ending along with the question mark allows the pattern to matcheitherthefulldaynameSaturdayortheabbreviatednameSat. It’s common to use grouping along with the pipe symbol to create groups of possible patternmatches: $echo“cat”|gawk‘/(c|b)a(b|t)/{print$0}’ cat $echo“cab”|gawk‘/(c|b)a(b|t)/{print$0}’ cab $echo“bat”|gawk‘/(c|b)a(b|t)/{print$0}’ bat $echo“bab”|gawk‘/(c|b)a(b|t)/{print$0}’ bab $echo“tab”|gawk‘/(c|b)a(b|t)/{print$0}’ $ $echo“tac”|gawk‘/(c|b)a(b|t)/{print$0}’ $ Thepattern (c|b)a(b|t)matchesanycombinationofthelettersinthefirstgroupalong withanycombinationofthelettersinthesecondgroup. RegularExpressionsinAction Now that you’ve seen the rules and a few simple demonstrations of using regular expression patterns, it’s time to put that knowledge into action. The following sections demonstratesomecommonregularexpressionexampleswithinshellscripts. Countingdirectoryfiles To start things out, let’s look at a shell script that counts the executable files that are presentinthedirectoriesdefinedinyour PATHenvironmentvariable.Todothat,youneed toparseoutthePATHvariableintoseparatedirectorynames.Chapter6showedyouhowto displaythePATHenvironmentvariable: $echo$PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/loca $ Your PATH environment variable will differ, depending on where the applications are locatedonyourLinuxsystem.Thekeyistorecognizethateachdirectoryinthe PATHis separatedbyacolon.Togetalistingofdirectoriesthatyoucanuseinascript,youmust replace each colon with a space. You now recognize that the sededitorcandojustthat usingasimpleregularexpression: $echo$PATH|sed‘s/://g’ /usr/local/sbin/usr/local/bin/usr/sbin/usr/bin/sbin/bin /usr/games/usr/local/games $ Afteryouhavethedirectoriesseparatedout,youcanusetheminastandardforstatement (seeChapter13)toiteratethrougheachdirectory: mypath=$(echo$PATH|sed‘s/://g’) fordirectoryin$mypath do … done After you have each directory, you can use the ls command to list each file in each directory, and use another for statement to iterate through each file, incrementing a counterforeachfile. Thefinalversionofthescriptlookslikethis: $catcountfiles #!/bin/bash #countnumberoffilesinyourPATH mypath=$(echo$PATH|sed‘s/://g’) count=0 fordirectoryin$mypath do check=$(ls$directory) foritemin$check do count=$[$count+1] done echo“$directory-$count” count=0 done $./countfiles/usr/local/sbin-0 /usr/local/bin-2 /usr/sbin-213 /usr/bin-1427 /sbin-186 /bin-152 /usr/games-5 /usr/local/games–0 $ Nowwe’restartingtoseesomeofthepowerbehindregularexpressions! Validatingaphonenumber The previous example showed how to incorporate the simple regular expression along withsedtoreplacecharactersinadatastreamtoprocessdata.Often,regularexpressions areusedtovalidatedatatoensurethatdataisinthecorrectformatforascript. A common data validation application checks phone numbers. Often, data entry forms request phone numbers, and often customers fail to enter a properly formatted phone number.PeopleintheUnitedStatesuseseveralcommonwaystodisplayaphonenumber: (123)456-7890 (123)456-7890 123-456-7890 123.456.7890 Thisleavesfourpossibilitiesforhowcustomerscanentertheirphonenumberinaform. Theregularexpressionmustberobustenoughtohandleanyofthesesituations. Whenbuildingaregularexpression,it’sbesttostartontheleftsideandbuildyourpattern tomatchthepossiblecharactersyou’llruninto.Inthisexample,theremayormaynotbe aleftparenthesisinthephonenumber.Thiscanbematchedbyusingthepattern: ^\(? The caret is used to indicate the beginning of the data. Because the left parenthesis is a special character, you must escape it to use it as a normal character. The question mark indicatesthattheleftparenthesismayormaynotappearinthedatatomatch. Nextisthethree-digitareacode.IntheUnitedStates,areacodesstartwiththenumber2 (noareacodesstartwiththedigits0or1),andcangoto9.Tomatchtheareacode,you’d usethefollowingpattern: [2-9][0-9]{2} This requires that the first character be a digit between 2 and 9, followed by any two digits.Aftertheareacode,theendingrightparenthesismayormaynotappear: \)? Aftertheareacode,therecanbeaspace,nospace,adash,oradot.Youcangroupthose usingacharactergroupalongwiththepipesymbol: (||-|.) Theveryfirstpipesymbolappearsimmediatelyaftertheleftparenthesistomatchtheno spacecondition.Youmustusetheescapecharacterforthedot;otherwise,itisinterpreted tomatchanycharacter. Nextisthethree-digitphoneexchangenumber.Nothingspecialisrequiredhere: [0-9]{3} Afterthephoneexchangenumber,youmustmatchaspace,adash,oradot(thistimeyou don’t have to worry about matching no space because there must be at least a space betweenthephoneexchangenumberandtherestofthenumber): (|-|.) Thentofinishthingsoff,youmustmatchthefour-digitlocalphoneextensionattheendof thestring: [0-9]{4}$ Puttingtheentirepatterntogetherresultsinthis: ^\(?[2-9][0-9]{2}\)?(||-|.)[0-9]{3}(|-|.)[0-9]{4}$ You can use this regular expression pattern in the gawk program to filter out bad phone numbers. Now you just need to create a simple script using the regular expression in a gawkprogramandfilteryourphonelistthroughthescript.Rememberthatwhenyouuse regular expression intervals in the gawk program, you must use the —re-interval commandlineoption,oryouwon’tgetthecorrectresults. Here’sthescript: $catisphone #!/bin/bash #scripttofilteroutbadphonenumbers gawk—re-interval‘/^\(?[2-9][0-9]{2}\)?(||-|\┐ [0-9]{3}(|-|.)[0-9]{4}/{print$0}’ $ Althoughyoucan’ttellfromthislisting,thegawkcommandisonasinglelineintheshell script.Youcanthenredirectphonenumberstothescriptforprocessing: $echo“317-555-1234”|./isphone 317-555-1234 $echo“000-555-1234”|./isphone $echo“312555-1234”|./isphone 312555-1234 $ Oryoucanredirectanentirefileofphonenumberstofilterouttheinvalidones: $catphonelist 000-000-0000 123-456-7890 212-555-1234 (317)555-1234 (202)555-9876 33523 1234567890 234.123.4567 $catphonelist|./isphone 212-555-1234 (317)555-1234 (202)555-9876 234.123.4567 $ Onlythevalidphonenumbersthatmatchtheregularexpressionpatternappear. Parsingane-mailaddress Thesedays,e-mailhasbecomeacrucialformofcommunication.Tryingtovalidatee-mail addresseshasbecomequiteachallengeforscriptbuildersbecauseofthemyriadwaysto createane-mailaddress.Thisisthebasicformofane-mailaddress: username@hostname The username value can use any alphanumeric character, along with several special characters: Dot Dash Plussign Underscore These characters can appear in any combination in a valid e-mail userid. The hostname portionofthee-mailaddressconsistsofoneormoredomainnamesandaservername. The server and domain names must also follow strict naming rules, allowing only alphanumericcharacters,alongwiththespecialcharacters: Dot Underscore Theserveranddomainnamesareeachseparatedbyadot,withtheservernamespecified first,anysubdomainnamesspecifiednext,andfinally,thetop-leveldomainnamewithout atrailingdot. At one time, the top-level domains were fairly limited, and regular expression pattern buildersattemptedtoaddthemallinpatternsforvalidation.Unfortunately,astheInternet grew,sodidthepossibletop-leveldomains.Thistechniqueisnolongeraviablesolution. Let’sstartbuildingtheregularexpressionpatternfromtheleftside.Weknowthatthere canbemultiplevalidcharactersintheusername.Thisshouldbefairlyeasy: ^([a-zA-Z0-9_-.\+]+)@ This grouping specifies the allowable characters in the username and the plus sign to indicatethatatleastonecharactermustbepresent.Thenextcharacterobviouslyisthe@ symbol—nosurprisesthere. The hostname pattern uses the same technique to match the server name and the subdomainnames: ([a-zA-Z0-9_-.]+) Thispatternmatchesthetext: server server.subdomain server.subdomain.subdomain There are special rules for the top-level domain. Top-level domains are only alphabetic characters,andtheymustbenofewerthantwocharacters(usedincountrycodes)andno morethanfivecharactersinlength.Thefollowingistheregularexpressionpatternforthe top-leveldomain: .([a-zA-Z]{2,5})$ Puttingtheentirepatterntogetherresultsinthefollowing: ^([a-zA-Z0-9_-.\+]+)@([a-zA-Z0-9_-.]+).([a-zA-Z]{2,5})$ This pattern filters out poorly formatted e-mail addresses from a data list. Now you can createyourscripttoimplementtheregularexpression: $echo“[email protected]”|./isemail [email protected] $echo“[email protected].”|./isemail $ $echo“[email protected]”|./isemail $ $echo“rich@here-now”|./isemail $ $echo“[email protected]”|./isemail [email protected] $echo“[email protected]”|./isemail [email protected] $echo“rich/[email protected]”|./isemail $ $echo“rich#[email protected]”|./isemail $ $echo“rich*[email protected]”|./isemail $ Summary If you manipulate data files in shell scripts, you need to become familiar with regular expressions. Regular expressions are implemented in Linux utilities, programming languages,andapplicationsusingregularexpressionengines.Ahostofdifferentregular expressionenginesisavailableintheLinuxworld.ThetwomostpopulararethePOSIX Basic Regular Expression (BRE) engine and the POSIX Extended Regular Expression (ERE) engine. The sed editor conforms mainly to the BRE engine, while the gawk programutilizesmostfeaturesfoundintheEREengine. Aregularexpressiondefinesapatterntemplatethat’susedtofiltertextinadatastream. The pattern consists of a combination of standard text characters and special characters. Thespecialcharactersareusedbytheregularexpressionenginetomatchaseriesofone ormorecharacters,similarlytohowwildcardcharactersworkinotherapplications. Bycombiningcharactersandspecialcharacters,youcandefineapatterntomatchalmost anytypeofdata.Youcanthenusethe sededitoror gawkprogramtofilterspecificdata fromalargerdatastream,orforvalidatingdatareceivedfromdataentryapplications. The next chapter digs deeper into using the sed editor to perform advanced text manipulation.Lotsofadvancedfeaturesareavailableinthe sededitorthatmakeituseful forhandlinglargedatastreamsandfilteringoutjustwhatyouneed. Chapter21 Advancedsed InThisChapter 1. Usingmultilinecommands 2. Understandingtheholdspace 3. Negatingacommand 4. Changingtheflow 5. Replacingviaapattern 6. Usingsedinscripts 7. Creatingsedutilities Chapter19showedyouhowtousethebasicsofthesededitortomanipulatetextin datastreams.Thebasicsededitorcommandsarecapableofhandlingmostofyour everydaytext-editingrequirements.Thischapterlooksatthemoreadvancedfeatures thatthesededitorhastooffer.Thesearefeaturesthatyoumightnotuseasoften.But whenyouneedthem,it’snicetoknowthatthey’reavailableandhowtousethem. LookingatMultilineCommands Whenusingthebasic sed editor commands, you may have noticed a limitation. All the sededitorcommandsperformfunctionsonasinglelineofdata.Asthe sededitorreadsa datastream,itdividesthedataintolinesbasedonthepresenceofnewlinecharacters.The sededitorhandleseachdatalineoneatatime,processingthedefinedscriptcommandson thedataline,andthenmovingontothenextlineandrepeatingtheprocessing. Sometimes, you need to perform actions on data that spans more than one line. This is especiallytrueifyou’retryingtofindorreplaceaphrase. Forexample,ifyou’relookingforthephrase Linux System Administrators Groupin your data, it’s quite possible that the phrase’s words can be split onto two lines. If you processedthetextusinganormal sededitorcommand,itwouldbeimpossibletodetect thesplitphrase. Fortunately, the designers behind the sed editor thought of that situation and devised a solution. The sed editor includes three special commands that you can use to process multilinetext: Naddsthenextlineinthedatastreamtocreateamultilinegroupforprocessing. Ddeletesasinglelineinamultilinegroup. Pprintsasinglelineinamultilinegroup. Thefollowingsectionsexaminethesemultilinecommandsmorecloselyanddemonstrate howyoucanusetheminyourscripts. Navigatingthenextcommand Beforeyoucanexaminethemultiline nextcommand,youfirstneedtolookathowthe single-lineversionofthenextcommandworks.Afteryouknowwhatthatcommanddoes, it’smucheasiertounderstandhowthemultilineversionofthenextcommandoperates. Usingthesingle-linenextcommand Thelowercase ncommandtellsthe sededitortomovetothenextlineoftextinthedata stream,withoutgoingbacktothebeginningofthecommands.Rememberthatnormally thesededitorprocessesallthedefinedcommandsonalinebeforemovingtothenextline oftextinthedatastream.Thesingle-linenextcommandaltersthisflow. Thismaysoundsomewhatcomplicated,andsometimesitis.Inthisexample,youhavea datafilethatcontainsfivelines,twoofwhichareempty.Thegoalistoremovetheblank lineaftertheheaderlinebutleavetheblanklinebeforethelastlineintact.Ifyouwritea sedscripttojustremoveblanklines,youremovebothblanklines: $catdata1.txt Thisistheheaderline. Thisisadataline. Thisisthelastline. $ $sed‘/∧$/d’data1.txt Thisistheheaderline. Thisisadataline. Thisisthelastline. $ Becausethelineyouwanttoremoveisblank,youdon’thaveanytextyoucansearchfor touniquelyidentifytheline.Thesolutionistousethe ncommand.Inthisnextexample, thescriptlooksforauniquelinethatcontainstheword header.Afterthescriptidentifies thatline,the ncommandmovesthe sededitortothenextlineoftext,whichistheempty line. $sed‘/header/{n;d}’data1.txt Thisistheheaderline. Thisisadataline. Thisisthelastline. $ At that point, the sed editor continues processing the command list, which uses the d commandtodeletetheemptyline.Whenthe sededitorreachestheendofthecommand script,itreadsthenextlineoftextfromthedatastreamandstartsprocessingcommands from the top of the command script. The sed editor does not find another line with the wordheader;thus,nofurtherlinesaredeleted. Combininglinesoftext Now that you’ve seen the single-line next command, you can look at the multiline version.Thesingle-line nextcommandmovesthenextlineoftextfromthedatastream into the processing space (called the pattern space) of the sed editor. The multiline versionofthenextcommand(whichusesacapitalN)addsthenextlineoftexttothetext alreadyinthepatternspace. This has the effect of combining two lines of text from the data stream into the same patternspace.Thelinesoftextarestillseparatedbyanewlinecharacter,butthesededitor cannowtreatbothlinesoftextasoneline. Here’sademonstrationofhowtheNcommandoperates: $catdata2.txt Thisistheheaderline. Thisisthefirstdataline. Thisistheseconddataline. Thisisthelastline. $ $sed‘/first/{N;s/\n//}’data2.txt Thisistheheaderline. Thisisthefirstdataline.Thisistheseconddataline. Thisisthelastline. $ Thesededitorscriptsearchesforthelineoftextthatcontainstheword“first”init.When itfindstheline,itusestheNcommandtocombinethenextlinewiththatline.Itthenuses the substitutioncommand(s)toreplacethenewlinecharacterwithaspace.Theresult isthatthetwolinesinthetextfileappearasonelineinthesededitoroutput. This has a practical application if you’re searching for a text phrase that may be split betweentwolinesinthedatafile.Here’sanexample: $catdata3.txt OnTuesday,theLinuxSystem Administrator’sgroupmeetingwillbeheld. AllSystemAdministratorsshouldattend. Thankyouforyourattendance. $ $sed‘N;s/SystemAdministrator/DesktopUser/’data3.txt OnTuesday,theLinuxSystem Administrator’sgroupmeetingwillbeheld. AllDesktopUsersshouldattend. Thankyouforyourattendance. $ The substitution command is looking for the specific two-word phrase System Administratorinthetextfile.Inthesinglelinewherethephraseappears,everythingis fine; the substitution command can replace the text. But in the situation where the phrase is split between two lines, the substitution command doesn’t recognize the matchingpattern. TheNcommandhelpssolvethisproblem: $sed‘N;s/System.Administrator/DesktopUser/’data3.txt OnTuesday,theLinuxDesktopUser’sgroupmeetingwillbeheld. AllDesktopUsersshouldattend. Thankyouforyourattendance. $ Byusingthe N command to combine the next line with the line where the first word is found,youcandetectwhenalinesplitoccursinthephrase. Notice that the substitution command uses a wildcard pattern (.) between the word Systemandtheword Administratortomatchboththespaceandthenewlinesituation. However,whenitmatchedthenewlinecharacter,itremoveditfromthestring,causingthe twolinestomergeintooneline.Thismaynotbeexactlywhatyouwant. Tosolvethisproblem,youcanusetwo substitutioncommandsinthe sededitorscript, onetomatchthemultilineoccurrenceandonetomatchthesingle-lineoccurrence: $sed‘N >s/System\nAdministrator/Desktop\nUser/ >s/SystemAdministrator/DesktopUser/ >‘data3.txt OnTuesday,theLinuxDesktop User’sgroupmeetingwillbeheld. AllDesktopUsersshouldattend. Thankyouforyourattendance. $ Thefirstsubstitutioncommandspecificallylooksforthenewlinecharacterbetweenthe two search words and includes it in the replacement string. This allows you to add the newlinecharacterinthesameplaceinthenewtext. There’sstillonesubtleproblemwiththisscript,however.Thescriptalwaysreadsthenext line of text into the pattern space before executing the sed editor commands. When it reachesthelastlineoftext,thereisn’tanextlineoftexttoread,sotheNcommandcauses the sed editor to stop. If the matching text is on the last line in the data stream, the commandsdon’tcatchthematchingdata: $catdata4.txt OnTuesday,theLinuxSystem Administrator’sgroupmeetingwillbeheld. AllSystemAdministratorsshouldattend. $ $sed‘N >s/System\nAdministrator/Desktop\nUser/ >s/SystemAdministrator/DesktopUser/ >‘data4.txt OnTuesday,theLinuxDesktop User’sgroupmeetingwillbeheld. AllSystemAdministratorsshouldattend. $ Becausethe SystemAdministratortextappearsinthelastlineinthedatastream,the N commandmissesit,asthereisn’tanotherlinetoreadintothepatternspacetocombine. You can easily resolve this problem by moving your single-line commands before the N commandandhavingonlythemultilinecommandsappearaftertheNcommand,likethis: $sed‘ >s/SystemAdministrator/DesktopUser/ >N >s/System\nAdministrator/Desktop\nUser/ >‘data4.txt OnTuesday,theLinuxDesktop User’sgroupmeetingwillbeheld. AllDesktopUsersshouldattend. $ Now,thesubstitutioncommandthatlooksforthephraseinasinglelineworksjustfine onthelastlineinthedatastream,andthemultiline substitutioncommandcoversthe occurrenceinthemiddleofthedatastream. Navigatingthemultilinedeletecommand Chapter 19 introduced the single-line delete command (d). The sed editor uses it to delete the current line in the pattern space. If you’re working with the N command, however,youmustbecarefulwhenusingthesingle-linedeletecommand: $sed‘N;/System\nAdministrator/d’data4.txt AllSystemAdministratorsshouldattend. $ The deletecommandlookedforthewords Systemand Administratorinseparatelines anddeletedbothofthelinesinthepatternspace.Thismayormaynothavebeenwhatyou intended. The sed editor provides the multiline deletecommand(D), which deletes only the first line in the pattern space. It removes all characters up to and including the newline character: $sed‘N;/System\nAdministrator/D’data4.txt Administrator’sgroupmeetingwillbeheld. AllSystemAdministratorsshouldattend. $ Thesecondlineoftext,addedtothepatternspacebytheNcommand,remainsintact.This comesinhandyifyouneedtoremovealineoftextthatappearsbeforealinethatyoufind adatastringin. Here’s an example of removing a blank line that appears before the first line in a data stream: $catdata5.txt Thisistheheaderline. Thisisadataline. Thisisthelastline. $ $sed‘/∧$/{N;/header/D}’data5.txt Thisistheheaderline. Thisisadataline. Thisisthelastline. $ This sededitorscriptlooksforblanklinesandthenusesthe Ncommandtoaddthenext line of text into the pattern space. If the new pattern space contents contain the word header, the D command removes the first line in the pattern space. Without the combinationofthe Nand Dcommands,itwouldbeimpossibletoremovethefirstblank linewithoutremovingallotherblanklines. Navigatingthemultilineprintcommand By now, you’re probably catching on to the difference between the single-line and multilineversionsofthecommands.Themultilineprintcommand(P)followsalongusing thesametechnique.Itprintsonlythefirstlineinamultilinepatternspace.Thisincludes allcharactersuptothenewlinecharacterinthepatternspace.Itisusedinmuchthesame wayasthesingle-line pcommandtodisplaytextwhenyouusethe -noptiontosuppress outputfromthescript. $sed-n‘N;/System\nAdministrator/P’data3.txt OnTuesday,theLinuxSystem $ Whenthemultilinematchoccurs,the Pcommandprintsonlythefirstlineinthepattern space.Thepowerofthemultiline Pcommandcomesintoplaywhenyoucombineitwith theNandDmultilinecommands. The D command has a unique feature in that it forces the sed editor to return to the beginningofthescriptandrepeatthecommandsonthesamepatternspace(itdoesn’tread a new line of text from the data stream). By including the N command in the command script,youcaneffectivelysingle-stepthroughthepatternspace,matchingmultiplelines together. Next,byusingthePcommand,youcanprintthefirstline,andthenusingtheDcommand, you can delete the first line and loop back to the beginning of the script. When you are backatthescript’sbeginning,the Ncommandreadsinthenextlineoftextandstartsthe processalloveragain.Thisloopcontinuesuntilyoureachtheendofthedatastream. HoldingSpace Thepatternspaceisanactivebufferareathatholdsthetextexaminedbythe sededitor whileitprocessescommands.However,itisn’ttheonlyspaceavailableinthe sededitor forstoringtext. The sededitorutilizesanotherbufferareacalledthe hold space. You can use the hold spacetotemporarilyholdlinesoftextwhileworkingonotherlinesinthepatternspace. The five commands associated with operating with the hold space are shown in Table 21.1. Table21.1ThesedEditorHoldSpaceCommands Command h H g G x Description Copiespatternspacetoholdspace Appendspatternspacetoholdspace Copiesholdspacetopatternspace Appendsholdspacetopatternspace Exchangescontentsofpatternandholdspaces Thesecommandsletyoucopytextfromthepatternspacetotheholdspace.Thisfreesup thepatternspacetoloadanotherstringforprocessing. Usually,afterusingthe hor Hcommandstomoveastringtotheholdspace,eventually youwanttousethe g, G,or xcommandstomovethestoredstringbackintothepattern space(otherwise,youwouldn’thavecaredaboutsavingtheminthefirstplace). With two buffer areas, trying to determine what line of text is in which buffer area can sometimesgetconfusing.Here’sashortexamplethatdemonstrateshowtousethehandg commandstomovedatabackandforthbetweenthesededitorbufferspaces: $catdata2.txt Thisistheheaderline. Thisisthefirstdataline. Thisistheseconddataline. Thisisthelastline. $ $sed-n‘/first/{h;p;n;p;g;p}’data2.txt Thisisthefirstdataline. Thisistheseconddataline. Thisisthefirstdataline. $ Lookattheprecedingcodeexamplestepbystep: 1. Thesedscriptusesaregularexpressionintheaddresstofilterthelinecontainingthe wordfirst. 2. Whenthelinecontainingthewordfirstappears,theinitialcommandin{},theh command,placesthelineintheholdspace. 3. Thenextcommand,thepcommand,printsthecontentsofthepatternspace,whichis stillthefirstdataline. 4. Thencommandretrievesthenextlineinthedatastream(Thisistheseconddata line)andplacesitinthepatternspace. 5. Thepcommandprintsthecontentsofthepatternspace,whichisnowtheseconddata line. 6. Thegcommandplacesthecontentsoftheholdspace(Thisisthefirstdata line)backintothepatternspace,replacingthecurrenttext. 7. Thepcommandprintsthecurrentcontentsofthepatternspace,whichisnowbackto thefirstdataline. Byshufflingthetextlinesaroundusingtheholdspace,youcanforcethefirstdatalineto appearaftertheseconddatalineintheoutput.Ifyoujustdropthefirst pcommand,you canoutputthetwolinesinreverseorder: $sed-n‘/first/{h;n;p;g;p}’data2.txt Thisistheseconddataline. Thisisthefirstdataline. $ Thisisthestartofsomethinguseful.Youcanusethistechniquetocreatea sedscriptthat reverses an entire file of text lines! To do that, however, you need to see the negating featureofthesededitor,whichiswhatthenextsectionisallabout. NegatingaCommand Chapter19showedthatthe sededitorappliescommandseithertoeverytextlineinthe datastreamortolinesspecificallyindicatedbyeitherasingleaddressoranaddressrange. Youcanalsoconfigureacommandtonotapplytoaspecificaddressoraddressrangein thedatastream. The exclamation mark command (!) is used to negate a command. This means in situations where the command would normally have been activated, it isn’t. Here’s an exampledemonstratingthisfeature: $sed-n‘/header/!p’data2.txt Thisisthefirstdataline. Thisistheseconddataline. Thisisthelastline. $ Thenormal pcommandwouldhaveprintedonlythelineinthe data2filethatcontained thewordheader.Byaddingtheexclamationmark,theoppositehappens—alllinesinthe fileareprintedexcepttheonethatcontainedthewordheader. Usingtheexclamationmarkcomesinhandyinseveralapplications.Recallthatearlierin thechapter,the“Navigatingthenextcommand”sectionshowedasituationwherea sed editorcommandwouldn’toperateonthelastlineoftextinthedatastreambecausethere wasn’talineafterit.Youcanusetheexclamationpointtofixthatproblem: $sed‘N; >s/System\nAdministrator/Desktop\nUser/ >s/SystemAdministrator/DesktopUser/ >‘data4.txt OnTuesday,theLinuxDesktop User’sgroupmeetingwillbeheld. AllSystemAdministratorsshouldattend. $ $sed‘$!N; >s/System\nAdministrator/Desktop\nUser/ >s/SystemAdministrator/DesktopUser/ >‘data4.txt OnTuesday,theLinuxDesktop User’sgroupmeetingwillbeheld. AllDesktopUsersshouldattend. $ This example shows the exclamation mark used with the N command, along with the dollarsign($)specialaddress.Thedollarsignrepresentsthelastlineoftextinthedata stream, so when the sed editor reaches the last line, it doesn’t execute the N command. However,forallotherlines,itdoesexecutethecommand. Usingthistechnique,youcanreversetheorderoftextlinesinadatastream.Toreverse theorderofthelinesastheyappearinthetextstream(displaythelastlinefirstandthe firstlinelast),youneedtodosomefancyfootworkusingtheholdspace. Thepatternyouneedtoworkwithgoeslikethis: 1. Placealineinthepatternspace. 2. Placethelinefromthepatternspacetotheholdspace. 3. Putthenextlineoftextinthepatternspace. 4. Appendtheholdspacetothepatternspace. 5. Placeeverythinginthepatternspaceintotheholdspace. 6. RepeatSteps3through5untilyou’veputallthelinesinreverseorderinthehold space. 7. Retrievethelines,andprintthem. Figure21.1diagramswhatthislookslikeinmoredetail. Figure21.1Reversingtheorderofatextfileusingtheholdspace When using this technique, you do not want to print lines as they are processed. This meansusingthe -ncommandlineoptionfor sed.Thenextthingtodetermineishowto appendtheholdspacetexttothepatternspacetext.ThisisdonebyusingtheGcommand. Theonlyproblemisthatyoudon’twanttoappendtheholdspacetothefirstlineoftext processed.Thisiseasilysolvedbyusingtheexclamationmarkcommand: 1!G The next step is to place the new pattern space (the text line with the appended reverse lines)intotheholdspace.Thisissimpleenough;justusethehcommand. Whenyou’vegottheentiredatastreaminthepatternspaceinreverseorder,youjustneed toprinttheresults.Youknowyouhavetheentiredatastreaminthepatternspacewhen you’vereachedthelastlineinthedatastream.Toprinttheresults,justusethefollowing command: $p Thosearethepiecesyouneedtocreateyourline-reversing sededitorscript.Nowtryit outinatestrun: $catdata2.txt Thisistheheaderline. Thisisthefirstdataline. Thisistheseconddataline. Thisisthelastline. $ $sed-n‘{1!G;h;$p}’data2.txt Thisisthelastline. Thisistheseconddataline. Thisisthefirstdataline. Thisistheheaderline. $ The sed editor script performed as expected. The output from the script reverses the originallinesinthetextfile.Thisdemonstratesthepowerofusingtheholdspaceinyour sedscripts.Itprovidesaneasywaytomanipulatetheorderoflinesinthescriptoutput. Note Incaseyou’rewondering,abashshellcommandcanperformthefunctionof reversingatextfile.Thetaccommanddisplaysatextfileinreverseorder.You probablynoticedtheclevernameofthecommandbecauseitperformsthereverse functionofthecatcommand. ChangingtheFlow Normally,the sed editor processes commands starting at the top and proceeding toward theendofthescript(theexceptionistheDcommand,whichforcesthesededitortoreturn to the top of the script without reading a new line of text). The sed editor provides a methodforalteringtheflowofthecommandscript,producingaresultsimilartothatofa structuredprogrammingenvironment. Branching Intheprevioussection,yousawhowtheexclamationmarkcommandisusedtonegatethe effectofacommandonalineoftext.The sededitorprovidesawaytonegateanentire sectionofcommands,basedonanaddress,anaddresspattern,oranaddressrange.This allows you to perform a group of commands only on a specific subset within the data stream. Here’stheformatofthebranchcommand: [address]b[label] The address parameter determines which line or lines of data trigger the branch command.The labelparameterdefinesthelocationtobranchto.Ifthe labelparameter isnotpresent,thebranchcommandproceedstotheendofthescript. $catdata2.txt Thisistheheaderline. Thisisthefirstdataline. Thisistheseconddataline. Thisisthelastline. $ $sed‘{2,3b;s/Thisis/Isthis/;s/line./test?/}’data2.txt Isthistheheadertest? Thisisthefirstdataline. Thisistheseconddataline. Isthisthelasttest? $ The branch command skips the two substitution commands for the second and third linesinthedatastream. Insteadofgoingtotheendofthescript,youcandefinealabelforthebranchcommandto jumpto.Labelsstartwithacolonandcanbeuptosevencharactersinlength: :label2 To specify the label, just add it after the b command. Using labels allows you to skip commandsthatmatchthebranchaddressbutstillprocessothercommandsinthescript: $sed‘{/first/bjump1;s/Thisisthe/Nojumpon/ >:jump1 >s/Thisisthe/Jumphereon/}’data2.txt Nojumponheaderline Jumphereonfirstdataline Nojumponseconddataline Nojumponlastline $ The branch command specifies that the program should jump to the script line labeled jump1 if the matching text “first” appears in the line. If the branch command pattern doesn’tmatch,the sededitorcontinuesprocessingcommandsinthescript,includingthe commandafterthe branchlabel.(Thus,allthree substitutioncommandsareprocessed onlinesthatdon’tmatchthebranchpattern.) If a line matches the branch pattern, the sed editor branches to the branch label line. Thus,onlythelastsubstitutioncommandisexecuted. The example shows branching to a label further down in the sed script. You can also branchtoalabelthatappearsearlierinthescript,thuscreatingaloopingeffect: $echo“This,is,a,test,to,remove,commas.”|sed-n‘{ >:start >s/,//1p >bstart >}’ Thisis,a,test,to,remove,commas. Thisisa,test,to,remove,commas. Thisisatest,to,remove,commas. Thisisatestto,remove,commas. Thisisatesttoremove,commas. Thisisatesttoremovecommas. ^C $ Each script iteration removes the first occurrence of a comma from the text string and printsthestring.There’sonecatchtothisscript:Itneverends.Thissituationcreatesan endlessloop,searchingforcommasuntilyoumanuallystopitbysendingasignalwiththe Ctrl+Ckeycombination. Topreventthisproblem,youshouldspecifyanaddresspatternforthebranchcommandto lookfor.Ifthepatternisn’tpresent,thebranchingshouldstop: $echo“This,is,a,test,to,remove,commas.”|sed-n‘{ >:start >s/,//1p >/,/bstart >}’ Thisis,a,test,to,remove,commas. Thisisa,test,to,remove,commas. Thisisatest,to,remove,commas. Thisisatestto,remove,commas. Thisisatesttoremove,commas. Thisisatesttoremovecommas. $ Now the branch command branches only if there’s a comma in the line. After the last comma has been removed, the branch command doesn’t execute, allowing the script to properlyfinish. Testing Similartothebranchcommand,thetestcommand(t)isalsousedtomodifytheflowof thesededitorscript.Insteadofjumpingtoalabelbasedonanaddress,thetestcommand jumpstoalabelbasedontheoutcomeofasubstitutioncommand. If the substitution command successfully matches and substitutes a pattern, the test command branches to the specified label. If the substitution command doesn’t match thespecifiedpattern,thetestcommanddoesn’tbranch. Thetestcommandusesthesameformatasthebranchcommand: [address]t[label] Like the branch command, if you don’t specify a label, sed branches to the end of the scriptifthetestsucceeds. The test command provides a cheap way to perform a basic if-then statement on the textinthedatastream.Forexample,ifyoudon’tneedtomakeasubstitutionifanother substitutionwasmade,thetestcommandcanhelp: $sed‘{ >s/first/matched/ >t >s/Thisisthe/Nomatchon/ >}’data2.txt Nomatchonheaderline Thisisthematcheddataline Nomatchonseconddataline Nomatchonlastline $ Thefirstsubstitutioncommandlooksforthepatterntextfirst.Ifitmatchesthepattern in the line, it replaces the text, and the test command jumps over the second substitutioncommand.Ifthefirst substitution command doesn’t match the pattern, thesecondsubstitutioncommandisprocessed. Usingthetestcommand,youcancleanuptheloopyoutriedusingthebranchcommand: $echo“This,is,a,test,to,remove,commas.”|sed-n‘{ >:start >s/,//1p >tstart >}’ Thisis,a,test,to,remove,commas. Thisisa,test,to,remove,commas. Thisisatest,to,remove,commas. Thisisatestto,remove,commas. Thisisatesttoremove,commas. Thisisatesttoremovecommas. $ When there are no more substitutions to make, the test command doesn’t branch and continueswiththerestofthescript. ReplacingviaaPattern You’veseenhowtousepatternsinthe sedcommandstoreplacetextinthedatastream. However, when using wildcard characters it’s not easy to know exactly what text will matchthepattern. Forexample,saythatyouwanttoplacedoublequotationmarksaroundawordyoumatch inaline.That’ssimpleenoughifyou’rejustlookingforonewordinthepatterntomatch: $echo“Thecatsleepsinhishat.”|sed‘s/cat/“cat”/’ The“cat”sleepsinhishat. $ Butwhatifyouuseawildcardcharacter(.)inthepatterntomatchmorethanoneword? $echo“Thecatsleepsinhishat.”|sed‘s/.at/”.at”/g’ The“.at”sleepsinhis“.at”. $ Thesubstitutionstringusedthedotwildcardcharactertomatchanyoccurrenceofaletter followed by “at”. Unfortunately, the replacement string doesn’t match the wildcard charactervalueofthematchingword. Usingtheampersand The sededitorhasasolutionforyou.Theampersandsymbol(&)isusedtorepresentthe matching pattern in the substitution command. Whatever text matches the pattern defined,youcanusetheampersandsymboltorecallitinthereplacementpattern.Thislets youmanipulatewhateverwordmatchesthepatterndefined: $echo“Thecatsleepsinhishat.”|sed‘s/.at/”&”/g’ The“cat”sleepsinhis“hat”. $ When the pattern matches the word cat, “cat” appears in the substituted word. When it matchesthewordhat,“hat”appearsinthesubstitutedword. Replacingindividualwords Theampersandsymbolretrievestheentirestringthatmatchesthepatternyouspecifyin the substitution command. Sometimes, you’ll only want to retrieve a subset of the string.Youcandothat,too,butit’salittletricky. The sed editor uses parentheses to define a substring component within the substitution pattern.Youcanthenreferenceeachsubstringcomponentusingaspecialcharacterinthe replacementpattern.Thereplacementcharacterconsistsofabackslashandanumber.The number indicates the substring component’s position. The sed editor assigns the first componentthecharacter∖1,thesecondcomponentthecharacter∖2,andsoon. Caution Whenyouuseparenthesesinthesubstitutioncommand,youmustusetheescape charactertoidentifythemasgroupingcharactersandnotnormalparentheses.Thisis thereverseofwhenyouescapeotherspecialcharacters. Lookatanexampleofusingthisfeatureinasededitorscript: $echo“TheSystemAdministratormanual”|sed‘ >s/\(System\)Administrator/\1User/’ TheSystemUsermanual $ This substitution command uses one set of parentheses around the word System identifyingitasasubstringcomponent.Itthenusesthe ∖1inthereplacementpatternto recall the first identified component. This isn’t too exciting, but it can really be useful whenworkingwithwildcardpatterns. Ifyouneedtoreplaceaphrasewithjustasingleword,that’sasubstringofthephrase,but thatsubstringjusthappenstobeusingawildcardcharacter;usingsubstringcomponentsis alifesaver: $echo“Thatfurrycatispretty”|sed‘s/furry\(.at\)/\1/’ Thatcatispretty $ $echo“Thatfurryhatispretty”|sed‘s/furry\(.at\)/\1/’ Thathatispretty $ Inthissituation,youcan’tusetheampersandsymbol,becauseitwouldreplacetheentire matching pattern. The substring component provides the answer, allowing you to select justwhichpartofthepatterntouseasthereplacementpattern. Thisfeaturecanbeespeciallyhelpfulwhenyouneedtoinserttextbetweentwoormore substringcomponents.Here’sascriptthatusessubstringcomponentstoinsertacommain longnumbers: $echo“1234567”|sed‘{ >:start >s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/ >tstart >}’ 1,234,567 $ Thescriptdividesthematchingpatternintotwocomponents: .*[0-9] [0-9]{3} This pattern looks for two substrings. The first substring is any number of characters, ending in a digit. The second substring is a series of three digits (see Chapter 20 for informationabouthowtousebracesinaregularexpression).Ifthispatternisfoundinthe text,thereplacementtextputsacommabetweenthetwocomponents,eachidentifiedby itscomponentposition.Thescriptusesthe testcommandtoiteratethroughthenumber untilallcommashavebeenplaced. PlacingsedCommandsinScripts Nowthatyou’veseenthevariouspartsofthesededitor,it’stimetoputthemtogetherand use them in your shell scripts. This section demonstrates some of the features that you shouldknowaboutwhenusingthesededitorinyourbashshellscripts. Usingwrappers You may have noticed that trying to implement a sed editor script can be cumbersome, especiallyifthescriptislong.Insteadofhavingtoretypetheentirescripteachtimeyou want to use it, you can place the sed editor command in a shell script wrapper. The wrapperactsasago-betweenforthesededitorscriptandthecommandline. Onceinsidetheshellscript,youcanusenormalshellvariablesandparameterswithyour sededitorscripts.Here’sanexampleofusingthecommandlineparametervariableasthe inputtoasedscript: $catreverse.sh #!/bin/bash #Shellwrapperforsededitorscript. #toreversetextfilelines. # sed-n‘{1!G;h;$p}’$1 # $ The shell script called reverse uses the sed editor script to reverse text lines in a data stream. It uses the $1 shell parameter to retrieve the first parameter from the command line,whichshouldbethenameofthefiletoreverse: $./reverse.shdata2.txt Thisisthelastline. Thisistheseconddataline. Thisisthefirstdataline. Thisistheheaderline. $ Now you can easily use the sed editor script on any file, without having to constantly retypetheentirecommandline. Redirectingsedoutput Bydefault,the sededitoroutputstheresultsofthescriptto STDOUT.Youcanemployall thestandardmethodsofredirectingtheoutputofthesededitorinyourshellscripts. You can use dollar sign/parenthesis, $(), to redirect the output of your sed editor commandtoavariableforuselaterinthescript.Thefollowingisanexampleofusingthe sedscripttoaddcommastotheresultofanumericcomputation: $catfact.sh #!/bin/bash #Addcommastonumberinfactorialanswer # factorial=1 counter=1 number=$1 # while[$counter-le$number] do factorial=$[$factorial*$counter] counter=$[$counter+1] done # result=$(echo$factorial|sed‘{ :start s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/ tstart }’) # echo“Theresultis$result” # $ $./fact.sh20 Theresultis2,432,902,008,176,640,000 $ Afteryouusethenormalfactorialcalculationscript,theresultofthatscriptisusedasthe input to the sed editor script, which adds commas. This value is then used in the echo statementtoproducetheresult. CreatingsedUtilities Asyou’veseenintheshortexamplespresentedsofarinthischapter,youcandolotsof cool data-formatting things with the sed editor. This section shows a few handy wellknownsededitorscriptsforperformingcommondata-handlingfunctions. Spacingwithdoublelines Tostartthingsoff,lookatasimple sedscripttoinsertablanklinebetweenlinesinatext file: $sed‘G’data2.txt Thisistheheaderline. Thisisthefirstdataline. Thisistheseconddataline. Thisisthelastline. $ That was pretty simple! The key to this trick is the default value of the hold space. Remember that the G command simply appends the contents of the hold space to the currentpatternspacecontents.Whenyoustartthe sededitor,theholdspacecontainsan emptyline.Byappendingthattoanexistingline,youcreateablanklineaftertheexisting line. You may have noticed that this script also adds a blank line to the last line in the data stream,producingablanklineattheendofthefile.Ifyouwanttogetridofthis,youcan use the negate symbol and the last line symbol to ensure that the script doesn’t add the blanklinetothelastlineofthedatastream: $sed‘$!G’data2.txt Thisistheheaderline. Thisisthefirstdataline. Thisistheseconddataline. Thisisthelastline. $ Nowthatlooksalittlebetter.Aslongasthelineisn’tthelastline,theGcommandappends the contents of the hold space. When the sed editor gets to the last line, it skips the G command. Spacingfilesthatmayhaveblanks Totakedoublespacingonestepfurther,whatifthetextfilealreadyhasafewblanklines, butyouwanttodoublespaceallthelines?Ifyouusethepreviousscript,you’llgetsome areasthathavetoomanyblanklines,becauseeachexistingblanklinegetsdoubled: $catdata6.txt Thisislineone. Thisislinetwo. Thisislinethree. Thisislinefour. $ $sed‘$!G’data6.txt Thisislineone. Thisislinetwo. Thisislinethree. Thisislinefour. $ Nowyouhavethreeblanklineswheretheoriginalblanklinewaslocated.Thesolutionto this problem is to first delete any blank lines from the data stream and then use the G commandtoinsertnewblanklinesafterallthelines.Todeleteexistingblanklines,you justneedtousethedcommandwithapatternthatmatchesablankline: /∧$/d Thispatternusesthestartlinetag(thecaret)andtheendlinetag(thedollarsign).Adding thispatterntothescriptproducesthedesiredresults: $sed‘/∧$/d;$!G’data6.txt Thisislineone. Thisislinetwo. Thisislinethree. Thisislinefour. $ Perfect!Itworksjustasexpected. Numberinglinesinafile Chapter19showedyouhowtousetheequalsigntodisplaythelinenumbersoflinesin thedatastream: $sed‘=’data2.txt 1 Thisistheheaderline. 2 Thisisthefirstdataline. 3 Thisistheseconddataline. 4 Thisisthelastline. $ Thiscanbealittleawkwardtoread,becausethelinenumberisonalineabovetheactual lineinthedatastream.Abettersolutionistoplacethelinenumberonthesamelineasthe text. Nowthatyou’veseenhowtocombinelinesusingtheNcommand,itshouldn’tbetoohard toutilizethatinformationinthe sededitorscript.Thetricktothisutility,however,isthat youcan’tcombinethetwocommandsinthesamescript. Afteryouhavetheoutputfortheequalsigncommand,youcanpipetheoutputtoanother sededitorscriptthatusesthe Ncommandtocombinethetwolines.Youalsoneedtouse the substitutioncommandtoreplacethenewlinecharacterwitheitheraspaceoratab character.Here’swhatthefinalsolutionlookslike: $sed‘=’data2.txt|sed‘N;s/\n//’ 1Thisistheheaderline. 2Thisisthefirstdataline. 3Thisistheseconddataline. 4Thisisthelastline. $ Nowthatlooksmuchbetter.Thisisagreatlittleutilitytohavearoundwhenworkingon programswhereyouneedtoseethelinenumbersusedinerrormessages. Therearebashshellcommandsthatcanalsoaddlinenumbers.However,theyaddsome additional(andpotentiallyunwantedspacing): $nldata2.txt 1Thisistheheaderline. 2Thisisthefirstdataline. 3Thisistheseconddataline. 4Thisisthelastline. $ $cat-ndata2.txt 1Thisistheheaderline. 2Thisisthefirstdataline. 3Thisistheseconddataline. 4Thisisthelastline. $ Thesededitorscripthandlestheoutputwithoutanyadditionalspacing. Printinglastlines Sofar,you’veseenhowtousethepcommandtoprintallthelinesinadatastreamorjust linesthatmatchaspecificpattern.Whatifyoujustneedtoworkwiththelastfewlinesof alonglisting,suchasalogfile? Thedollarsignrepresentsthelastlineofadatastream,soit’seasytodisplayjustthelast line: $sed-n‘$p’data2.txt Thisisthelastline. $ Nowhowcanyouusethedollarsignsymboltodisplayasetnumberoflinesattheendof thedatastream?Theansweristocreatearollingwindow. Arollingwindowisacommonwaytoexamineblocksoftextlinesinthepatternspaceby combiningthemusingthe Ncommand.The Ncommandappendsthenextlineoftextto thetextalreadyinthepatternspace.Afteryouhaveablockof10textlinesinthepattern space,youcanchecktoseeifyou’reattheendofthedatastreamusingthedollarsign.If you’renotattheend,continueaddingmorelinestothepatternspace,whileremovingthe originallines(remembertheDcommand,whichdeletesthefirstlineinthepatternspace). Byloopingthroughthe Nand Dcommands,youaddnewlinestotheblockoflinesinthe pattern space while removing old lines. The branch command is the perfect fit for the loop.Toendtheloop,justidentifythelastlineandusetheqcommandtoquit. Here’swhatthefinalsededitorscriptlookslike: $catdata7.txt Thisisline1. Thisisline2. Thisisline3. Thisisline4. Thisisline5. Thisisline6. Thisisline7. Thisisline8. Thisisline9. Thisisline10. Thisisline11. Thisisline12. Thisisline13. Thisisline14. Thisisline15. $ $sed‘{ >:start >$q;N;11,$D >bstart >}’data7.txt Thisisline6. Thisisline7. Thisisline8. Thisisline9. Thisisline10. Thisisline11. Thisisline12. Thisisline13. Thisisline14. Thisisline15. $ Thescriptfirstcheckswhetherthelineisthelastlineinthedatastream.Ifitis,the quit commandstopstheloop.The Ncommandappendsthenextlinetothecurrentlineinthe patternspace.The 11,$Dcommanddeletesthefirstlineinthepatternspaceifthecurrent lineisafterline10.Thiscreatestheslidingwindoweffectinthepatternspace.Thus,the sedprogramscriptdisplaysonlythelast10linesofthedata7.txtfile. Deletinglines Another useful utility for the sed editor is to remove unwanted blank lines in a data stream. It’s easy to remove all the blank lines from a data stream, but it takes a little ingenuitytoselectivelyremoveblanklines.Thissectionshowsyouacoupleofquick sed editorscriptsthatyoucanusetohelpremoveunwantedblanklinesfromyourdata. Deletingconsecutiveblanklines Itcanbeanuisancewhenextrablanklinescropupindatafiles.Oftenyouhaveadatafile that contains blank lines, but sometimes a data line is missing and produces too many blanklines(asyousawinthedouble-spacingexampleearlier). The easiest way to remove consecutive blank lines is to check the data stream using a rangeaddress.Chapter19showedyouhowtouserangesinaddresses,includinghowto incorporate patterns in the address range. The sed editor executes the command for all linesthatmatchwithinthespecifiedaddressrange. Thekeytoremovingconsecutiveblanklinesistocreateanaddressrangethatincludesa non-blank line and a blank line. If the sed editor comes across this range, it shouldn’t deletetheline.However,forlinesthatdon’tmatchthatrange(twoormoreblanklinesina row),itshoulddeletethelines. Here’sthescripttodothis: /./,/∧$/!d Therangeis/./to /∧$/.Thestartaddressintherangematchesanylinethatcontainsat leastonecharacter.Theendaddressintherangematchesablankline.Lineswithinthis rangearen’tdeleted. Here’sthescriptinaction: $catdata8.txt Thisislineone. Thisislinetwo. Thisislinethree. Thisislinefour. $ $sed‘/./,/∧$/!d’data8.txt Thisislineone. Thisislinetwo. Thisislinethree. Thisislinefour. $ Nomatterhowmanyblanklinesappearbetweenlinesofdatainthefile,theoutputplaces onlyoneblanklinebetweenthelines. Deletingleadingblanklines It is also a nuisance when data files contain multiple blank lines at the start of the file. Oftenwhenyouaretryingtoimportdatafromatextfileintoadatabase,theblanklines createnullentries,throwingoffanycalculationsusingthedata. Removingblanklinesfromthetopofadatastreamisnotadifficulttask.Here’sthescript thataccomplishesthatfunction: /./,$!d Thescriptusesanaddressrangetodeterminewhatlinesaredeleted.Therangestartswith alinethatcontainsacharacterandcontinuestotheendofthedatastream.Anylinewithin thisrangeisnotdeletedfromtheoutput.Thismeansthatanylinesbeforethefirstlinethat containacharacteraredeleted. Lookatthissimplescriptinaction: $catdata9.txt Thisislineone. Thisislinetwo. $ $sed‘/./,$!d’data9.txt Thisislineone. Thisislinetwo. $ Thetestfilecontainstwoblanklinesbeforethedatalines.Thescriptsuccessfullyremoves bothoftheleadingblanklines,whilekeepingtheblanklinewithinthedataintact. Deletingtrailingblanklines Unfortunately,deletingtrailingblanklinesisnotassimpleasdeletingleadingblanklines. Justlikeprintingtheendofadatastream,deletingblanklinesattheendofadatastream requiresalittleingenuityandlooping. Beforewestartthediscussion,let’sseewhatthescriptlookslike: sed‘{ :start /∧\n*$/{$d;N;bstart} }’ Thismaylookalittleoddtoyouatfirst.Noticethattherearebraceswithinthenormal scriptbraces.Thisallowsyoutogroupcommandstogetherwithintheoverallcommand script. The group of commands applies to the specified address pattern. The address patternmatchesanylinethatcontainsonlyanewlinecharacter.Whenoneisfound,ifit’s the last line, the delete command deletes it. If it’s not the last line, the N command appendsthenextlinetoit,andthebranchcommandloopstothebeginningtostartover. Here’sthescriptinaction: $catdata10.txt Thisisthefirstline. Thisisthesecondline. $sed‘{ >:start >/∧\n*$/{$d;N;bstart} >}’data10.txt Thisisthefirstline. Thisisthesecondline. $ Thescriptsuccessfullyremovedtheblanklinesfromtheendofthetextfile. RemovingHTMLtags Thesedays,it’snotuncommontodownloadtextfromawebsitetosaveoruseasdatain anapplication.Sometimes,however,whenyoudownloadtextfromthewebsite,youalso gettheHTMLtagsusedtoformatthedata.Thiscanbeaproblemwhenallyouwantto seeisthedata. A standard HTML web page contains several different types of HTML tags, identifying formattingfeaturesrequiredtoproperlydisplaythepageinformation.Here’sasampleof whatanHTMLfilelookslike: $catdata11.txt <html> <head> <titletype=“main”>Thisisthepagetitle</title> </head> <body> <p> Thisisthe<b>first</b>lineintheWebpage. Thisshouldprovidesome<i>useful</i> informationtouseinoursedscript. </body> </html> $ HTML tags are identified by the less-than and greater-than symbols. Most HTML tags comeinpairs.Onetagstartstheformattingprocess(forexample, <b> for bolding), and anothertagstopstheformattingprocess(forexample,</b>toturnoffbolding). RemovingHTMLtagscreatesaproblem,however,ifyou’renotcareful.Atfirstglance, you’dthinkthatthewaytoremoveHTMLtagswouldbetojustlookforatextstringthat starts with a less-than symbol (<), ends with a greater-than symbol (>), and has data in betweenthesymbols: s/<.*>//g Unfortunately,thiscommandhassomeunintendedconsequences: $sed‘s/<.*>//g’data11.txt ThisisthelineintheWebpage. Thisshouldprovidesome informationtouseinoursedscript. $ Noticethatthetitletextismissing,alongwiththetextthatwasboldedanditalicized.The sed editor literally interpreted the script to mean any text between the less-than and greater-thansign,includingotherless-thanandgreater-thansigns!Eachtimethetextwas enclosedinHTMLtags(suchas<b>first</b>),thesedscriptremovedtheentiretext. Thesolutiontothisproblemistohavethe sededitorignoreanyembeddedgreater-than signsbetweentheoriginaltags.Todothat,youcancreateacharacterclassthatnegates thegreater-thansign.Thischangesthescriptto: s/<[∧>]*>//g This script now works properly, displaying the data you need to see from the web page HTMLcode: $sed‘s/<[∧>]*>//g’data11.txt Thisisthepagetitle ThisisthefirstlineintheWebpage. Thisshouldprovidesomeuseful informationtouseinoursedscript. $ That’salittlebetter.Tocleanthingsupsome,youcanaddadeletecommandtogetridof thosepeskyblanklines: $sed‘s/<[∧>]*>//g;/∧$/d’data11.txt Thisisthepagetitle ThisisthefirstlineintheWebpage. Thisshouldprovidesomeuseful informationtouseinoursedscript. $ Nowthat’smuchmorecompact;there’sonlythedatayouneedtosee. Summary Thesededitorprovidessomeadvancedfeaturesthatallowyoutoworkwithtextpatterns acrossmultiplelines.Thischaptershowedyouhowtousethe nextcommandtoretrieve thenextlineinadatastreamandplaceitinthepatternspace.Onceinthepatternspace, you can perform complex substitution commands to replace phrases that span more thanonelineoftext. Themultilinedeletecommandallowsyoutoremovethefirstlinewhenthepatternspace containstwoormorelines.Thisisaconvenientwaytoiteratethroughmultiplelinesin thedatastream.Similarly,themultiline printcommandallowsyoutoprintjustthefirst line when the pattern space contains two or more lines of text. The combination of the multilinecommandsallowsyoutoiteratethroughthedatastreamandcreateamultiline substitutionsystem. Next, we covered the hold space. The hold space allows you to set aside a line of text whileprocessingmorelinesoftext.Youcanrecallthecontentsoftheholdspaceatany time and either replace the text in the pattern space or append the contents of the hold spacetothetextinthepatternspace.Usingtheholdspaceallowsyoutosortthroughdata streams,reversingtheorderoftextlinesastheyappearinthedata. Nextwereviewedthevarious sededitorflowcontrolcommands.The branchcommand provides a way for you to alter the normal flow of sed editor commands in the script, creating loops or skipping commands under certain conditions. The test command provides an if-then type of statement for your sed editor command scripts. The test commandbranchesonlyifapriorsubstitutioncommandsucceedsinreplacingtextina line. Thechapterconcludedwithadiscussionofhowtouse sedscriptsinyourshellscripts.A commontechniqueforlarge sedscriptsistoplacethescriptinashellwrapper.Youcan use command line parameter variables within the sed script to pass shell command line values. This creates an easy way to utilize your sed editor scripts directly from the commandline,orevenfromothershellscripts. The next chapter digs deeper into the gawk world. The gawk program supports many featuresofhigher-levelprogramminglanguages.Youcancreatesomeprettyinvolveddata manipulationandreportingprogramsjustbyusinggawk.Thechapterdescribesthevarious programming features and demonstrates how to use them to generate your own fancy reportsfromsimpledata. Chapter22 Advancedgawk InThisChapter 1. Reexamininggawk 2. Usingvariablesingawk 3. Usingstructuredcommands 4. Formattingyourprinting 5. Workingwithfunctions Chapter19introducedthegawkprogramanddemonstratedthebasicsofusingitto produceformattedreportsfromrawdatafiles.Thischapterdivesmoredeeplyinto customizinggawktoproducereports.Thegawkprogramisafull-fledged programminglanguage,providingfeaturesthatallowyoutowriteadvanced programstomanipulatedata.Ifyouarejumpingintotheshellscriptworldfrom anotherprogramminglanguage,youshouldfeelrightathomewithgawk.Inthis chapter,you’llseehowtousethegawkprogramminglanguagetowriteprogramsto handlejustaboutanydata-formattingtaskyou’llruninto. UsingVariables One important feature of any programming language is the ability to store and recall values using variables. The gawk programming language supports two different types of variables: Built-invariables User-definedvariables Several built-in variables are available for you to use in gawk. The built-in variables containinformationusedinhandlingthedatafieldsandrecordsinthedatafile.Youcan alsocreateyourownvariablesinyour gawkprograms.Thefollowingsectionswalkyou throughhowtousevariablesinyourgawkprograms. Built-invariables Thegawkprogramusesbuilt-invariablestoreferencespecificfeatureswithintheprogram data. This section describes the built-in variables available for you to use in your gawk programsanddemonstrateshowtousethem. Thefieldandrecordseparatorvariables Chapter 19 demonstrated one type of built-in variable available in gawk: the data field variables.Thedatafieldvariablesallowyoutoreferenceindividualdatafieldswithina datarecordusingadollarsignandthenumericalpositionofthedatafieldintherecord. Thus,toreferencethefirstdatafieldintherecord,youusethe $1variable.Toreference theseconddatafield,youusethe$2variable,andsoon. Data fields are delineated by a field separator character. By default, the field separator characterisawhitespacecharacter,suchasaspaceoratab.Chapter19showedhowto changethefieldseparatorcharactereitheronthecommandlinebyusingthe-Fcommand lineparameterorwithinthegawkprogramusingthespecialFSbuilt-invariable. The FS built-in variable belongs to a group of built-in variables that control how gawk handlesfieldsandrecordsinbothinputdataandoutputdata.Table22.1liststhebuilt-in variablescontainedinthisgroup. Table22.1ThegawkDataFieldandRecordVariables Variable Description Aspace-separatedlistofnumbersdefiningtheexactwidth(inspaces)of FIELDWIDTHS eachdatafield FS Inputfieldseparatorcharacter RS Inputrecordseparatorcharacter OFS Outputfieldseparatorcharacter ORS Outputrecordseparatorcharacter The FSand OFS variables define how your gawk program handles data fields in the data stream.You’vealreadyseenhowtousethe FSvariabletodefinewhatcharacterseparates datafieldsinarecord.The OFSvariableperformsthesamefunctionbutfortheoutputby usingtheprintcommand. Bydefault,gawksetstheOFSvariabletoaspace,sowhenyouusethecommand: print$1,$2,$3 youseetheoutputas: field1field2field3 Youcanseethisinthefollowingexample: $catdata1 data11,data12,data13,data14,data15 data21,data22,data23,data24,data25 data31,data32,data33,data34,data35 $gawk‘BEGIN{FS=”,”}{print$1,$2,$3}’data1 data11data12data13 data21data22data23 data31data32data33 $ TheprintcommandautomaticallyplacesthevalueoftheOFSvariablebetweeneachdata field in the output. By setting the OFS variable, you can use any string to separate data fieldsintheoutput: $gawk‘BEGIN{FS=”,”;OFS=”-“}{print$1,$2,$3}’data1 data11-data12-data13 data21-data22-data23 data31-data32-data33 $gawk‘BEGIN{FS=”,”;OFS=”—”}{print$1,$2,$3}’data1 data11—data12—data13 data21—data22—data23 data31—data32—data33 $gawk‘BEGIN{FS=”,”;OFS=”<—>”}{print$1,$2,$3}’data1 data11<—>data12<—>data13 data21<—>data22<—>data23 data31<—>data32<—>data33 $ The FIELDWIDTHS variable allows you to read records without using a field separator character.Insomeapplications,insteadofusingafieldseparatorcharacter,dataisplaced in specific columns within the record. In these instances, you must set the FIELDWIDTHS variabletomatchthelayoutofthedataintherecords. After you set the FIELDWIDTHS variable, gawk ignores the FS and calculates data fields basedontheprovidedfieldwidthsizes.Here’sanexampleusingfieldwidthsinsteadof fieldseparatorcharacters: $catdata1b 1005.3247596.37 115-2.349194.00 05810.1298100.1 $gawk‘BEGIN{FIELDWIDTHS=“3525”}{print$1,$2,$3,$4}’data1b 1005.3247596.37 115-2.349194.00 05810.1298100.1 $ The FIELDWIDTHS variable defines four data fields, and gawk parses the data record accordingly.Thestringofnumbersineachrecordissplitbasedonthedefinedfieldwidth values. Caution It’simportanttorememberthatafteryousettheFIELDWIDTHSvariable,thosevalues mustremainconstant.Thismethodcan’taccommodatevariable-lengthdatafields. The RS and ORS variables define how your gawk program handles records in the data stream.Bydefault,gawksetstheRSandORSvariablestothenewlinecharacter.Thedefault RS variable value indicates that each new line of text in the input data stream is a new record. Sometimes,yourunintosituationswheredatafieldsarespreadacrossmultiplelinesinthe datastream.Aclassicexampleofthisisdatathatincludesanaddressandphonenumber, eachonaseparateline: RileyMullen 123MainStreet Chicago,IL60601 (312)555-1234 IfyoutrytoreadthisdatausingthedefaultFSandRSvariablevalues,gawkreadseachline asaseparaterecordandinterpretseachspaceintherecordasafieldseparator.Thisisn’t whatyouintended. To solve this problem, you need to set the FS variable to the newline character. This indicates that each line in the data stream is a separate field and all the data on a line belongstothedatafield.However,whenyoudothat,youdon’tknowwhereanewrecord starts. To solve this problem, set the RS variable to an empty string, and leave a blank line betweendatarecordsinthedatastream.The gawkprograminterpretseachblanklineasa recordseparator. Thefollowingisanexampleofusingthistechnique: $catdata2 RileyMullen 123MainStreet Chicago,IL60601 (312)555-1234 FrankWilliams 456OakStreet Indianapolis,IN46201 (317)555-9876 HaleySnell 4231ElmStreet Detroit,MI48201 (313)555-4938 $gawk‘BEGIN{FS=”\n”;RS=””}{print$1,$4}’data2 RileyMullen(312)555-1234 FrankWilliams(317)555-9876 HaleySnell(313)555-4938 $ Perfect!The gawk program interpreted each line in the file as a data field and the blank linesasrecordseparators. Datavariables Besides the field and record separator variables, gawk provides some other built-in variablestohelpyouknowwhat’sgoingonwithyourdataandextractinformationfrom theshellenvironment.Table22.2showstheotherbuilt-invariablesingawk. Table22.2MoregawkBuilt-InVariables Variable ARGC ARGIND ARGV CONVFMT ENVIRON ERRNO FILENAME FNR IGNORECASE NF NR OFMT RLENGTH RSTART Description Thenumberofcommandlineparameterspresent TheindexinARGVofthecurrentfilebeingprocessed Anarrayofcommandlineparameters Theconversionformatfornumbers(seetheprintfstatement),witha defaultvalueof%.6g Anassociativearrayofthecurrentshellenvironmentvariablesandtheir values Thesystemerrorifanerroroccurswhenreadingorclosinginputfiles Thefilenameofthedatafileusedforinputtothegawkprogram Thecurrentrecordnumberinthedatafile Ifsettoanon-zerovalue,ignoresthecaseofcharactersinstringsusedin thegawkcommand Thetotalnumberofdatafieldsinthedatafile Thenumberofinputrecordsprocessed Theoutputformatfordisplayingnumbers,withadefaultof%.6g Thelengthofthesubstringmatchedinthematchfunction Thestartindexofthesubstringmatchedinthematchfunction You should recognize a few of these variables from your shell script programming. The ARGCandARGVvariablesallowyoutoretrievethenumberofcommandlineparametersand theirvaluesfromtheshell.Thiscanbealittletricky,however,becausegawkdoesn’tcount theprogramscriptaspartofthecommandlineparameters: $gawk‘BEGIN{printARGC,ARGV[1]}’data1 2data1 $ The ARGCvariableindicatesthattwoparametersareonthecommandline.Thisincludes the gawk command and the data1 parameter (remember that the program script doesn’t count as a parameter). The ARGV array starts with an index of 0, which represents the command. The first array value is the first command line parameter after the gawk command. Note Notethatunlikeshellvariables,whenyoureferenceagawkvariableinthescript,you don’taddadollarsignbeforethevariablename. TheENVIRONvariablemayseemalittleoddtoyou.Itusesanassociativearraytoretrieve shell environment variables. An associative array uses text for the array index values insteadofnumericvalues. Thetextinthearrayindexistheshellenvironmentvariable.Thevalueofthearrayisthe valueoftheshellenvironmentvariable.Thefollowingisanexampleofthis: $gawk‘ >BEGIN{ >printENVIRON[“HOME”] >printENVIRON[“PATH”] >}’ /home/rich /usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin $ The ENVIRON[”HOME”] variable retrieves the HOME environment variable value from the shell. Likewise, the ENVIRON[”PATH”] variable retrieves the PATH environment variable value.Youcanusethistechniquetoretrieveanyenvironmentvariablevaluefromtheshell touseinyourgawkprograms. The FNR, NF, and NR variables come in handy when you’re trying to keep track of data fieldsandrecordsinyourgawkprogram.Sometimes,you’reinasituationwhereyoudon’t knowexactlyhowmanydatafieldsareinarecord.The NFvariableallowsyoutospecify thelastdatafieldintherecordwithouthavingtoknowitsposition: $gawk‘BEGIN{FS=”:”;OFS=”:”}{print$1,$NF}’/etc/passwd rich:/bin/bash testy:/bin/csh mark:/bin/bash dan:/bin/bash mike:/bin/bash test:/bin/bash $ TheNFvariablecontainsthenumericalvalueofthelastdatafieldinthedatafile.Youcan thenuseitasadatafieldvariablebyplacingadollarsigninfrontofit. TheFNRandNRvariablesaresimilartoeachother,butslightlydifferent.The FNRvariable containsthenumberofrecordsprocessedinthecurrentdatafile.TheNRvariablecontains the total number of records processed. Let’s look at a couple of examples to see this difference: $gawk‘BEGIN{FS=”,”}{print$1,“FNR=“FNR}’data1data1 data11FNR=1 data21FNR=2 data31FNR=3 data11FNR=1 data21FNR=2 data31FNR=3 $ Inthisexample,the gawkprogramcommandlinedefinestwoinputfiles.(Itspecifiesthe sameinputfiletwice.)Thescriptprintsthefirstdatafieldvalueandthecurrentvalueof the FNR variable. Notice that the FNR value was reset to 1 when the gawk program processedtheseconddatafile. Now,let’saddtheNRvariableandseewhatthatproduces: $gawk‘ >BEGIN{FS=”,”} >{print$1,“FNR=“FNR,“NR=“NR} >END{print“Therewere”,NR,“recordsprocessed”}’data1data1 data11FNR=1NR=1 data21FNR=2NR=2 data31FNR=3NR=3 data11FNR=1NR=4 data21FNR=2NR=5 data31FNR=3NR=6 Therewere6recordsprocessed $ The FNR variable value was reset when gawk processed the second data file, but the NR variable maintained its count into the second data file. The bottom line is that if you’re using only one data file for input, the FNR and NR values are the same. If you’re using multiple data files for input, the FNR value is reset for each data file, and the NR value keepscountthroughoutallthedatafiles. Note Whenusinggawk,noticethatthegawkscriptcanoftenbecomelargerthantherestof yourshellscript.Intheexamplesinthischapter,forsimplicitywejustrunthegawk scriptsdirectlyfromthecommandline,usingthemultilinefeatureoftheshell.When youusegawkinashellscript,youshouldplacedifferentgawkcommandsonseparate lines.Thismakesitmucheasierforyoutoreadandfollow,ratherthantryingtocram itallontoonelineintheshellscript.Also,ifyoufindyourselfusingthesamegawk scriptsindifferentshellscripts,youcansavethegawkscriptinaseparatefileand referenceitusingthe–fparameter(seeChapter19). User-definedvariables Justlikeanyotherself-respectingprogramminglanguage, gawkallowsyoutodefineyour ownvariablesforusewithintheprogramcode.Agawkuser-definedvariablenamecanbe any number of letters, digits, and underscores, but it can’t begin with a digit. It is also importanttorememberthatgawkvariablenamesarecasesensitive. Assigningvariablesinscripts Assigning values to variables in gawk programs is similar to doing so in a shell script, usinganassignmentstatement: $gawk‘ >BEGIN{ >testing=“Thisisatest” >printtesting >}’ Thisisatest $ Theoutputofthe printstatementisthecurrentvalueofthe testingvariable.Likeshell scriptvariables,gawkvariablescanholdeithernumericortextvalues: $gawk‘ >BEGIN{ >testing=“Thisisatest” >printtesting >testing=45 >printtesting >}’ Thisisatest 45 $ In this example, the value of the testing variable is changed from a text value to a numericvalue. Assignment statements can also include mathematical algorithms to handle numeric values: $gawk‘BEGIN{x=4;x=x*2+3;printx}’ 11 $ Asyoucanseefromthisexample,the gawkprogramminglanguageincludesthestandard mathematicaloperatorsforprocessingnumericalvalues.Thesecanincludetheremainder symbol(%)andtheexponentiationsymbol(usingeither∧or**). Assigningvariablesonthecommandline You can also use the gawk command line to assign values to variables for the gawk program.Thisallowsyoutosetvaluesoutsideofthenormalcode,changingvaluesonthe fly.Here’sanexampleofusingacommandlinevariabletodisplayaspecificdatafieldin thefile: $catscript1 BEGIN{FS=”,”} {print$n} $gawk-fscript1n=2data1 data12 data22 data32 $gawk-fscript1n=3data1 data13 data23 data33 $ Thisfeatureallowsyoutochangethebehaviorofthescriptwithoutnecessitatingthatyou changetheactualscriptcode.Thefirstexampledisplaystheseconddatafieldinthefile, whilethesecondexampledisplaysthethirddatafield,justbysettingthevalueofthe n variableinthecommandline. There’soneproblemwithusingcommandlineparameterstodefinevariablevalues.When yousetthevariable,thevalueisn’tavailableintheBEGINsectionofthecode: $catscript2 BEGIN{print“Thestartingvalueis”,n;FS=”,”} {print$n} $gawk-fscript2n=3data1 Thestartingvalueis data13 data23 data33 $ You can solve this using the -v command line parameter. This allows you to specify variablesthataresetbeforethe BEGIN section of code. The -vcommandlineparameter mustbeplacedbeforethescriptcodeinthecommandline: $gawk-vn=3-fscript2data1 Thestartingvalueis3 data13 data23 data33 $ NowthenvariablecontainsthevaluesetinthecommandlineduringtheBEGINsectionof code. WorkingwithArrays Many programming languages provide arrays for storing multiple values in a single variable. The gawk programming language provides the array feature using associative arrays. Associativearraysaredifferentfromnumericalarraysinthattheindexvaluecanbeany textstring.Youdon’thavetousesequentialnumberstoidentifydataelementscontained inthearray.Instead,anassociativearrayconsistsofahodge-podgeofstringsreferencing values.Eachindexstringmustbeuniqueanduniquelyidentifiesthedataelementthat’s assigned to it. If you’re familiar with other programming languages, this is the same conceptashashmapsordictionaries. The following sections walk you through using associative array variables in your gawk programs. Definingarrayvariables Youcandefineanarrayvariableusingastandardassignmentstatement.Here’stheformat ofthearrayvariableassignment: var[index]=element Inthisexample, varisthevariablename, indexistheassociativearrayindexvalue,and elementisthedataelementvalue.Herearesomeexamplesofarrayvariablesingawk: capital[“Illinois”]=“Springfield” capital[“Indiana”]=“Indianapolis” capital[“Ohio”]=“Columbus” When you reference an array variable, you must include the index value to retrieve the appropriatedataelementvalue: $gawk‘BEGIN{ >capital[“Illinois”]=“Springfield” >printcapital[“Illinois”] >}’ Springfield $ When you reference the array variable, the data element value appears. This also works withnumericdataelementvalues: $gawk‘BEGIN{ >var[1]=34 >var[2]=3 >total=var[1]+var[2] >printtotal >}’ 37 $ Asyoucanseefromthisexample,youcanusearrayvariablesjustasyouwouldanyother variableinthegawkprogram. Iteratingthrougharrayvariables The problem with associative array variables is that you might not have any way of knowingwhattheindexvaluesare.Unlikenumericarrays,whichusesequentialnumbers forindexvalues,anassociativearrayindexcanbeanything. Ifyouneedtoiteratethroughanassociatearrayin gawk,youcanuseaspecialformatof theforstatement: for(varinarray) { statements } The forstatementloopsthroughthestatements,eachtimeassigningthevariable varthe next index value from the array associative array. It’s important to remember that the variableisthevalueoftheindexandnotthedataelementvalue.Youcaneasilyextractthe dataelementvaluebyusingthevariableasthearrayindex: $gawk‘BEGIN{ >var[“a”]=1 >var[“g”]=2 >var[“m”]=3 >var[“u”]=4 >for(testinvar) >{ >print“Index:”,test,”-Value:”,var[test] >} >}’ Index:u-Value:4 Index:m-Value:3 Index:a-Value:1 Index:g-Value:2 $ Noticethattheindexvaluesaren’treturnedinanyparticularorder,buttheyeachreference the appropriate data element value. This is somewhat important to know, because you can’t count on the returned values being in the same order, just that the index and data valuesmatch. Deletingarrayvariables Removinganarrayindexfromanassociativearrayrequiresaspecialcommand: deletearray[index] The delete command removes the associative index value and the associated data elementvaluefromthearray: $gawk‘BEGIN{ >var[“a”]=1 >var[“g”]=2 >for(testinvar) >{ >print“Index:”,test,”-Value:”,var[test] >} >deletevar[“g”] >print“–” >for(testinvar) >print“Index:”,test,”-Value:”,var[test] >}’ Index:a-Value:1 Index:g-Value:2 – Index:a-Value:1 $ Afteryoudeleteanindexvaluefromtheassociativearray,youcan’tretrieveit. UsingPatterns The gawk program supports several types of matching patterns to filter data records, in muchthesamewayasthe sededitor.Chapter19showedtwospecialpatternsinaction. The BEGINand ENDkeywordsarespecialpatternsthatexecutestatementsbeforeorafter the data stream data has been read. Similarly, you can create other patterns to execute statementswhenmatchingdataappearsinthedatastream. Thissectiondemonstrateshowtousematchingpatternsinyour gawkscriptstolimitwhat recordsaprogramscriptappliesto. Regularexpressions Chapter 20 showed how to use regular expressions as matching patterns. You can use either a Basic Regular Expression (BRE) or an Extended Regular Expression (ERE) to filterwhichlinesinthedatastreamtheprogramscriptappliesto. Whenusingaregularexpression,theregularexpressionmustappearbeforetheleftbrace oftheprogramscriptthatitcontrols: $gawk‘BEGIN{FS=”,”}/11/{print$1}’data1 data11 $ The regular expression /11/ matches records that contain the string 11 anywhere in the datafields.The gawkprogrammatchesthedefinedregularexpressionagainstallthedata fieldsintherecord,includingthefieldseparatorcharacter: $gawk‘BEGIN{FS=”,”}/,d/{print$1}’data1 data11 data21 data31 $ This example matches the comma used as the field separator in the regular expression. Thisisnotalwaysagoodthing.Itcanleadtoproblemstryingtomatchdataspecificto onedatafieldthatmayalsoappearinanotherdatafield.Ifyouneedtomatcharegular expressiontoaspecificdatainstance,youshouldusethematchingoperator. Thematchingoperator Thematchingoperatorallowsyoutorestrictaregularexpressiontoaspecificdatafield intherecords.Thematchingoperatoristhetildesymbol(∼).Youspecifythematching operator,alongwiththedatafieldvariable,andtheregularexpressiontomatch: $1~/^data/ The$1variablerepresentsthefirstdatafieldintherecord.Thisexpressionfiltersrecords wherethefirstdatafieldstartswiththetextdata.Thefollowingisanexampleofusingthe matchingoperatorinagawkprogramscript: $gawk‘BEGIN{FS=”,”}$2~/^data2/{print$0}’data1 data21,data22,data23,data24,data25 $ The matching operator compares the second data field with the regular expression / ∧data2/,whichindicatesthestringstartswiththetextdata2. This is a powerful tool that is commonly used in gawk program scripts to search for specificdataelementsinadatafile: $gawk-F:‘$1~/rich/{print$1,$NF}’/etc/passwd rich/bin/bash $ Thisexamplesearchesthefirstdatafieldforthetext rich.Whenitfindsthepatternina record,itprintsthefirstandlastdatafieldvaluesoftherecord. Youcanalsonegatetheregularexpressionmatchbyusingthe!symbol: $1!~/expression/ If the regular expression isn’t found in the record, the program script is applied to the recorddata: $gawk–F:‘$1!∼/rich/{print$1,$NF}’/etc/passwd root/bin/bash daemon/bin/sh bin/bin/sh sys/bin/sh –outputtruncated– $ Inthisexample,thegawkprogramscriptprintstheuseridandshellforalltheentriesinthe /etc/passwdfilethatdon’tmatchtheuseridrich! Mathematicalexpressions In addition to regular expressions, you can also use mathematical expressions in the matching pattern. This feature comes in handy when matching numerical values in data fields. For example, if you want to display all the system users who belong to the root usersgroup(groupnumber0),youcouldusethisscript: $gawk-F:‘$4==0{print$1}’/etc/passwd root sync shutdown halt operator $ The script checks for records where the fourth data field contains the value 0. On this Linuxsystem,fiveuseraccountsbelongtotherootusergroup. Youcanuseanyofthenormalmathematicalcomparisonexpressions: x==y:Valuexisequaltoy. x<=y:Valuexislessthanorequaltoy. x<y:Valuexislessthany. x>=y:Valuexisgreaterthanorequaltoy. x>y:Valuexisgreaterthany. You can also use expressions with text data, but you must be careful. Unlike regular expressions,expressionsareanexactmatch.Thedatamustmatchexactlywiththepattern: $gawk-F,‘$1==“data”{print$1}’data1 $ $gawk-F,‘$1==“data11”{print$1}’data1 data11 $ Thefirsttestdoesn’tmatchanyrecordsbecausethefirstdatafieldvalueisn’tdatainany oftherecords.Thesecondtestmatchesonerecordwiththevaluedata11. StructuredCommands The gawk programming language supports the usual cast of structured programming commands.Thissectiondescribeseachofthesecommandsanddemonstrateshowtouse themwithinagawkprogrammingenvironment. Theifstatement The gawk programming language supports the standard if-then-else format of the if statement. You must define a condition for the if statement to evaluate, enclosed in parentheses. If the condition evaluates to a TRUE condition, the statement immediately followingtheifstatementisexecuted.IftheconditionevaluatestoaFALSEcondition,the statementisskipped.Youcanusethisformat: if(condition) statement1 Oryoucanplaceitononeline,likethis: if(condition)statement1 Here’sasimpleexampledemonstratingthisformat: $catdata4 10 5 13 50 34 $gawk‘{if($1>20)print$1}’data4 50 34 $ Nottoocomplicated.Ifyouneedtoexecutemultiplestatementsinthe ifstatement,you mustenclosethemwithbraces: $gawk‘{ >if($1>20) >{ >x=$1*2 >printx >} >}’data4 100 68 $ Becarefulthatyoudon’tconfusetheifstatementbraceswiththebracesusedtostartand stop the program script. The gawk program can detect missing braces and produces an errormessageifyoumessup: $gawk‘{ >if($1>20) >{ >x=$1*2 >printx >}’data4 gawk:cmd.line:6:} gawk:cmd.line:6:^unexpectednewlineorendofstring $ Thegawkifstatementalsosupportstheelseclause,allowingyoutoexecuteoneormore statementsiftheifstatementconditionfails.Here’sanexampleofusingtheelseclause: $gawk‘{ >if($1>20) >{ >x=$1*2 >printx >}else >{ >x=$1/2 >printx >}}’data4 5 2.5 6.5 100 68 $ Youcanusethe elseclauseonasingleline,butyoumustuseasemicolonafterthe if statementsection: if(condition)statement1;elsestatement2 Here’sthesameexampleusingthesinglelineformat: $gawk‘{if($1>20)print$1*2;elseprint$1/2}’data4 5 2.5 6.5 100 68 $ Thisformatismorecompactbutcanbehardertofollow. Thewhilestatement The while statement provides a basic looping feature for gawk programs. Here’s the formatofthewhilestatement: while(condition) { statements } Thewhileloopallowsyoutoiterateoverasetofdata,checkingaconditionthatstopsthe iteration.Thisisusefulifyouhavemultipledatavaluesineachrecordthatyoumustuse incalculations: $catdata5 130120135 160113140 145170215 $gawk‘{ >total=0 >i=1 >while(i<4) >{ >total+=$i >i++ >} >avg=total/3 >print“Average:”,avg >}’data5 Average:128.333 Average:137.667 Average:176.667 $ Thewhilestatementiteratesthroughthedatafieldsintherecord,addingeachvaluetothe totalvariableandincrementingthecountervariable,i.Whenthecountervalueisequalto 4,the whileconditionbecomes FALSE,andtheloopterminates,droppingthroughtothe nextstatementinthescript.Thatstatementcalculatestheaverageandprintstheaverage. Thisprocessisrepeatedforeachrecordinthedatafile. The gawk programming language supports using the break and continue statements in whileloops,allowingyoutojumpoutofthemiddleoftheloop: $gawk‘{ >total=0 >i=1 >while(i<4) >{ >total+=$i >if(i==2) >break >i++ >} >avg=total/2 >print“Theaverageofthefirsttwodataelementsis:”,avg >}’data5 Theaverageofthefirsttwodataelementsis:125 Theaverageofthefirsttwodataelementsis:136.5 Theaverageofthefirsttwodataelementsis:157.5 $ Thebreakstatementisusedtobreakoutofthewhileloopifthevalueoftheivariableis 2. Thedo-whilestatement The do-while statement is similar to the while statement but performs the statements beforecheckingtheconditionstatement.Here’stheformatforthedo-whilestatement: do { statements }while(condition) This format guarantees that the statements are executed at least one time before the conditionisevaluated.Thiscomesinhandywhenyouneedtoperformstatementsbefore evaluatingthecondition: $gawk‘{ >total=0 >i=1 >do >{ >total+=$i >i++ >}while(total<150) >printtotal}’data5 250 160 315 $ Thescriptreadsthedatafieldsfromeachrecordandtotalsthemuntilthecumulativevalue reaches150.Ifthefirstdatafieldisover150(asseeninthesecondrecord),thescriptis guaranteedtoreadatleastthefirstdatafieldbeforeevaluatingthecondition. Theforstatement The for statement is a common method used in many programming languages for looping.ThegawkprogramminglanguagesupportstheC-styleofforloops: for(variableassignment;condition;iterationprocess) Thishelpssimplifytheloopbycombiningseveralfunctionsinonestatement: $gawk‘{ >total=0 >for(i=1;i<4;i++) >{ >total+=$i >} >avg=total/3 >print“Average:”,avg >}’data5 Average:128.333 Average:137.667 Average:176.667 $ By defining the iteration counter in the for loop, you don’t have to worry about incrementingityourselfasyoudidwhenusingthewhilestatement. FormattedPrinting Youmayhavenoticedthattheprintstatementdoesn’texactlygiveyoumuchcontrolover how gawk displays your data. About all you can do is control the output field separator character (OFS). If you’re creating detailed reports, often you need to place data in a specificformatandlocation. Thesolutionistousetheformattedprintingcommand,called printf.Ifyou’refamiliar withCprogramming,the printfcommandin gawkperformsthesameway,allowingyou tospecifydetailedinstructionsonhowtodisplaydata. Here’stheformatoftheprintfcommand: printf“formatstring“,var1,var2… Theformatstringisthekeytotheformattedoutput.Itspecifiesexactlyhowtheformatted outputshouldappear,usingbothtextelementsandformatspecifiers.Aformatspecifieris aspecialcodethatindicateswhattypeofvariableisdisplayedandhowtodisplayit.The gawk program uses each format specifier as a placeholder for each variable listed in the command.Thefirstformatspecifiermatchesthefirstvariablelisted,thesecondmatches thesecondvariable,andsoon. Theformatspecifiersusethefollowingformat: %[modifier]control-letter Inthisexample, control-letterisaone-charactercodethatindicateswhattypeofdata valuewillbedisplayed,andmodifierdefinesanoptionalformattingfeature. Table22.3liststhecontrollettersthatcanbeusedintheformatspecifier. Table22.3FormatSpecifierControlLetters ControlLetter c d i e f g o s x X Description DisplaysanumberasanASCIIcharacter Displaysanintegervalue Displaysanintegervalue(sameasd) Displaysanumberinscientificnotation Displaysafloating-pointvalue Displayseitherscientificnotationorfloatingpoint,whicheverisshorter Displaysanoctalvalue Displaysatextstring Displaysahexadecimalvalue Displaysahexadecimalvalue,butusingcapitallettersforAthroughF Thus,ifyouneedtodisplayastringvariable,youusetheformatspecifier %s.Ifyouneed todisplayanintegervariable,youuseeither %dor %i(%distheC-stylefordecimals).If youwanttodisplayalargevalueusingscientificnotation,youusethe%eformatspecifier: $gawk‘BEGIN{ >x=10*100 >printf“Theansweris:%e\n”,x >}’ Theansweris:1.000000e+03 $ Inadditiontothecontrolletters,youcanusethreemodifiersforevenmorecontrolover youroutput: width:Thisisanumericvaluethatspecifiestheminimumwidthoftheoutputfield. Iftheoutputisshorter,printfpadsthespacewithspaces,usingrightjustification forthetext.Iftheoutputislongerthanthespecifiedwidth,itoverridesthewidth value. prec:Thisisanumericvaluethatspecifiesthenumberofdigitstotherightofthe decimalplaceinfloating-pointnumbers,orthemaximumnumberofcharacters displayedinatextstring. -(minussign):Theminussignindicatesthatleftjustificationshouldbeusedinstead ofrightjustificationwhenplacingdataintheformattedspace. When using the printf statement, you have complete control over how your output appears.Forexample,inthe“Built-invariables”section,weusedthe printcommandto displaydatafieldsfromourrecords: $gawk‘BEGIN{FS=”\n”;RS=””}{print$1,$4}’data2 RileyMullen(312)555-1234 FrankWilliams(317)555-9876 HaleySnell(313)555-4938 $ Youcanusethe printfcommandtohelpformattheoutputsoitlooksbetter.First,let’s justconverttheprintcommandtoaprintfcommandandseewhatthatdoes: $gawk‘BEGIN{FS=”\n”;RS=””}{printf“%s%s\n”,$1,$4}’data2 RileyMullen(312)555-1234 FrankWilliams(317)555-9876 HaleySnell(313)555-4938 $ Thatproducesthesameoutputasthe printcommand.The printfcommandusesthe %s formatspecifierasaplaceholderforthetwostringvalues. Notice that you have to manually add the newline character at the end of the printf command to force a new line. Without it, the printf command uses the same line on subsequentprints. This is useful if you need to print multiple things on the same line, but using separate printfcommands: $gawk‘BEGIN{FS=”,”}{printf“%s“,$1}END{printf“\n”}’data1 data11data21data31 $ Both printf outputs appear on the same line. To be able to terminate the line, the END sectionprintsasinglenewlinecharacter. Next,let’suseamodifiertoformatthefirststringvalue: $gawk‘BEGIN{FS=”\n”;RS=””}{printf“%16s%s\n”,$1,$4}’data2 RileyMullen(312)555-1234 FrankWilliams(317)555-9876 HaleySnell(313)555-4938 $ Byaddingthe 16modifiervalue,weforcetheoutputforthefirststringtouse16spaces. By default, the printf command uses right justification to place the data in the format space.Tomakeitleftjustified,justaddaminussigntothemodifier: $gawk‘BEGIN{FS=”\n”;RS=””}{printf“%-16s%s\n”,$1,$4}’data2 RileyMullen(312)555-1234 FrankWilliams(317)555-9876 HaleySnell(313)555-4938 $ Nowthatlooksprettyprofessional! The printfcommandalsocomesinhandywhendealingwithfloating-pointvalues.By specifyingaformatforthevariable,youcanmaketheoutputlookmoreuniform: $gawk‘{ >total=0 >for(i=1;i<4;i++) >{ >total+=$i >} >avg=total/3 >printf“Average:%5.1f\n”,avg >}’data5 Average:128.3 Average:137.7 Average:176.7 $ By using the %5.1f format specifier, you can force the printf command to round the floating-pointvaluestoasingledecimalplace. Built-InFunctions The gawk programming language provides quite a few built-in functions that perform commonmathematical,string,andeventimefunctions.Youcanutilizethesefunctionsin your gawk programs to help cut down on the coding requirements in your scripts. This sectionwalksyouthroughthedifferentbuilt-infunctionsavailableingawk. Mathematicalfunctions Ifyou’vedoneprogramminginanytypeoflanguage,you’reprobablyfamiliarwithusing built-in functions in your code to perform common mathematical functions. The gawk programming language doesn’t disappoint those looking for advanced mathematical features. Table22.4showsthemathematicalbuilt-infunctionsavailableingawk. Table22.4ThegawkMathematicalFunctions Function Description atan2(x,y) Thearctangentofx/y,withxandyspecifiedinradians cos(x) Thecosineofx,withxspecifiedinradians exp(x) Theexponentialofx int(x) Theintegerpartofx,truncatedtoward0 log(x) Thenaturallogarithmofx rand() Arandomfloatingpointvaluelargerthan0andlessthan1 sin(x) Thesineofx,withxspecifiedinradians sqrt(x) Thesquarerootofx srand(x) Specifiesaseedvalueforcalculatingrandomnumbers Althoughitdoesnothaveanextensivelistofmathematicalfunctions, gawkdoesprovide some of the basic elements you need for standard mathematical processing. The int() functionproducestheintegerportionofavalue,butitdoesn’troundthevalue.Itbehaves muchlikeafloorfunctionfoundinotherprogramminglanguages.Itproducesthenearest integertoavaluebetweenthevalueand0. Thismeansthattheint()functionofthevalue5.6returns5,whilethe int()functionof thevalue-5.6returns-5. The rand()functionisgreatforcreatingrandomnumbers,butyouneedtouseatrickto getmeaningfulvalues.The rand()functionreturnsarandomnumber,butonlybetween thevalues0and1(notincluding0or1).Togetalargernumber,youneedtoscalethe returnedvalue. Acommonmethodforproducinglargerintegerrandomnumbersistocreateanalgorithm thatusestherand()function,alongwiththeint()function: x=int(10*rand()) Thisreturnsarandomintegervaluebetween(andincluding)0and9.Justsubstitutethe10 intheequationwiththeupperlimitvalueforyourapplication,andyou’rereadytogo. Be careful when using some of the mathematical functions, because the gawk programminglanguagedoeshavealimitedrangeofnumericvaluesitcanworkwith.If yougooverthatrange,yougetanerrormessage: $gawk‘BEGIN{x=exp(100);printx}’ 26881171418161356094253400435962903554686976 $gawk‘BEGIN{x=exp(1000);printx}’ gawk:warning:expargument1000isoutofrange inf $ Thefirstexamplecalculatesthenaturalexponentialfunctionof100,whichisaverylarge numberbutwithintherangeofthesystem.Thesecondexampleattemptstocalculatethe natural exponential function of 1,000, which goes over the numerical range limit of the systemandproducesanerrormessage. Besides the standard mathematical functions, gawk also provides a few functions for bitwisemanipulatingofdata: and(v1,v2):PerformsabitwiseANDofvaluesv1andv2 compl(val):Performsthebitwisecomplementofval lshift(val,count):Shiftsthevaluevalcountnumberofbitsleft or(v1,v2):PerformsabitwiseORofvaluesv1andv2 rshift(val,count):Shiftsthevaluevalcountnumberofbitsright xor(v1,v2):PerformsabitwiseXORofvaluesv1andv2 Thebitmanipulationfunctionsareusefulwhenworkingwithbinaryvaluesinyourdata. Stringfunctions The gawk programming language also provides several functions you can use to manipulatestringvalues,showninTable22.5. Table22.5ThegawkStringFunctions Function Description Thisfunctionsortsanarraysbasedonthedataelementvalues.The indexvaluesarereplacedwithsequentialnumbersindicatingthenew asort(s[,d]) sortorder.Alternatively,thenewsortedarrayisstoredinarraydif specified. Thisfunctionsortsanarraysbasedontheindexvalues.Theresulting arraycontainstheindexvaluesasthedataelementvalues,with asorti(s[,d]) sequentialnumberindexesindicatingthesortorder.Alternatively,the newsortedarrayisstoredinarraydifspecified. Thisfunctionsearcheseitherthevariable$0,orthetargetstringtif supplied,formatchesoftheregularexpressionr.Ifhisastring beginningwitheithergorG,itreplacesthematchingtextwiths.Ifh isanumber,itrepresentswhichoccurrenceofrtoreplace. Thisfunctionsearcheseitherthevariable$0,orthetargetstringtif gsub(r,s[,t]) supplied,formatchesoftheregularexpressionr.Iffound,it substitutesthestringsglobally. Thisfunctionreturnstheindexofthestringtinstrings,or0ifnot index(s,t) found. Thisfunctionreturnsthelengthofstrings,orifnotspecified,the length([s]) lengthof$0. Thisfunctionreturnstheindexofthestringswheretheregular match(s,r expressionroccurs.Ifarrayaisspecified,itcontainstheportionofs [,a]) thatmatchestheregularexpression. ThisfunctionsplitssintoarrayausingeithertheFScharacter,orthe split(s,a [,r]) regularexpressionrifsupplied.Itreturnsthenumberoffields. sprintf(format, Thisfunctionreturnsastringsimilartotheoutputofprintfusingthe variables) formatandvariablessupplied. Thisfunctionsearcheseitherthevariable$0,orthetargetstringt,for sub(r,s[,t]) matchesoftheregularexpressionr.Iffound,itsubstitutesthestrings forthefirstoccurrence. Thisfunctionreturnsthenthcharactersubstringofs,startingatindex substr(s,i [,n]) i.Ifnisnotsupplied,therestofsisused. tolower(s) Thisfunctionconvertsallcharactersinstolowercase. toupper(s) Thisfunctionconvertsallcharactersinstouppercase. gensub(r,s,h [,t]) Somestringfunctionsarerelativelyself-explanatory: $gawk‘BEGIN{x=“testing”;printtoupper(x);printlength(x)}’ TESTING 7 $ However, some string functions can get pretty complicated. The asort and asorti functionsarenew gawkfunctionsthatallowyoutosortanarrayvariablebasedoneither thedataelementvalues(asort)ortheindexvalues(asorti).Here’sanexampleofusing asort: $gawk‘BEGIN{ >var[“a”]=1 >var[“g”]=2 >var[“m”]=3 >var[“u”]=4 >asort(var,test) >for(iintest) >print“Index:”,i,”-value:”,test[i] >}’ Index:4-value:4 Index:1-value:1 Index:2-value:2 Index:3-value:3 $ Thenewarray,test,containsthenewlysorteddataelementsoftheoriginalarray,butthe indexvaluesarenowchangedtonumericalvalues,indicatingthepropersortorder. Thesplitfunctionisagreatwaytopushdatafieldsintoanarrayforfurtherprocessing: $gawk‘BEGIN{FS=”,”}{ >split($0,var) >printvar[1],var[5] >}’data1 data11data15 data21data25 data31data35 $ The new array uses sequential numbers for the array index, starting with index value 1 containingthefirstdatafield. Timefunctions The gawk programming language contains a few functions to help you deal with time values,showninTable22.6. Table22.6ThegawkTimeFunctions Function Description ConvertsadatespecifiedintheformatYYYYMMDDHHMMSS mktime(datespec) [DST]intoatimestampvalue Formatseitherthecurrenttimeofdaytimestamp,ortimestampif strftime(format provided,intoaformatteddayanddate,usingthedate()shell [,timestamp]) functionformat systime() Returnsthetimestampforthecurrenttimeofday Thetimefunctionsareoftenusedwhenworkingwithlogfilesthatcontaindatesthatyou need to compare. By converting the text representation of a date to the epoch time (the numberofsecondssincemidnight,January1,1970),youcaneasilycomparedates. Thefollowingisanexampleofusingthetimefunctionsinagawkprogram: $gawk‘BEGIN{ >date=systime() >day=strftime(“%A,%B%d,%Y”,date) >printday >}’ Friday,December26,2014 $ Thisexampleusesthe systimefunctiontoretrievethecurrentepochtimestampfromthe system and then uses the strftime function to convert it into a human-readable format usingthedateshellcommand’sdateformatcharacters. User-DefinedFunctions You’re not limited to just using the built-in functions available in gawk. You can create yourownfunctionsforusein gawkprograms.Thissectionshowsyouhowtodefineand useyourownfunctionsingawkprograms. Definingafunction Todefineyouownfunction,youmustusethefunctionkeyword: functionname([variables]) { statements } The function name must uniquely identify your function. You can pass one or more variablesintothefunctionfromthecallinggawkprogram: functionprintthird() { print$3 } Thisfunctionprintsthethirddatafieldintherecord. Thefunctioncanalsoreturnavalueusingthereturnstatement: returnvalue Thevaluecanbeavariable,oranequationthatevaluatestoavalue: functionmyrand(limit) { returnint(limit*rand()) } Youcanassignthevaluereturnedfromthefunctiontoavariableinthegawkprogram: x=myrand(100) Thevariablecontainsthevaluereturnedfromthefunction. Usingyourfunctions Whenyoudefineafunction,itmustappearbyitselfbeforeyoudefineanyprogramming sections(includingtheBEGINsection).Thismaylookalittleoddatfirst,butithelpskeep thefunctioncodeseparatefromtherestofthegawkprogram: $gawk‘ >functionmyprint() >{ >printf“%-16s-%s\n”,$1,$4 >} >BEGIN{FS=”\n”;RS=””} >{ >myprint() >}’data2 RileyMullen-(312)555-1234 FrankWilliams-(317)555-9876 HaleySnell-(313)555-4938 $ Thefunctiondefinesthemyprint()function,whichformatsthefirstandfourthdatafields in the record for printing. The gawk program then uses the function to display the data fromthedatafile. Afteryoudefineafunction,youcanuseitasoftenasnecessaryintheprogramsectionof thecode.Thissaveslotsofworkwhenusinglongalgorithms. Creatingafunctionlibrary Obviously, having to rewrite your gawk functions every time you need them is not a pleasant experience. However, gawk provides a way for you to combine your functions intoasinglelibraryfilethatyoucanuseinallyourgawkprogramming. First,youneedtocreateafilethatcontainsallyourgawkfunctions: $catfunclib functionmyprint() { printf“%-16s-%s\n”,$1,$4 } functionmyrand(limit) { returnint(limit*rand()) } functionprintthird() { print$3 } $ The funclibfilecontainsthreefunctiondefinitions.Tousethem,youneedtousethe -f command line parameter. Unfortunately, you can’t combine the -f command line parameterwithaninline gawkscript,butyoucanusemultiple -fparametersonthesame commandline. Thus,touseyourlibrary,justcreateafilethatcontainsyour gawkprogram,andspecify boththelibraryfileandyourprogramfileonthecommandline: $catscript4 BEGIN{FS=”\n”;RS=””} { myprint() } $gawk-ffunclib-fscript4data2 RileyMullen-(312)555-1234 FrankWilliams-(317)555-9876 HaleySnell-(313)555-4938 $ Nowyoujustneedtoaddthefunclibfiletoyourgawkcommandlinewheneveryouneed touseafunctiondefinedinthelibrary. WorkingthroughaPracticalExample Theadvancedgawkfeaturescomeinhandyifyouhavetohandledatavaluesinadatafile, suchastabulatingsalesfiguresorcalculatingbowlingscores.Whenyouworkwithdata files, the key is to first group related data records together and then perform any calculationsrequiredontherelateddata. For example, let’s work with a data file that contains the bowling scores from a game betweentwoteams,eachwithtwoplayers: $catscores.txt RichBlum,team1,100,115,95 BarbaraBlum,team1,110,115,100 ChristineBresnahan,team2,120,115,118 TimBresnahan,team2,125,112,116 $ Each player has scores from three separate games in the data file, and each player is identifiedbyateamnameinthesecondcolumn.Here’stheshellscripttosortthedatafor eachteamandcalculatethetotalsandaverages: $catbowling.sh #!/bin/bash forteamin$(gawk–F,‘{print$2}’scores.txt|uniq) do gawk–vteam=$team‘BEGIN{FS=”,”;total=0} { if($2==team) { total+=$3+$4+$5; } } END{ avg=total/6; print“Totalfor”,team,“is”,total,“,theaverageis”,avg }’scores.txt done $ Thefirstgawkstatementinsidetheforloopfiltersouttheteamnamesinthedatafileand thenusestheuniqfunctiontoreturnonevalueforeachseparateteamname.Theforloop theniteratesforeachseparateteamname. The gawk statement inside the for loop is what’s doing the calculations. For each data record,itfirstdeterminesiftheteamnamematchestheloopteam.That’sdonebyusing the–voptionin gawk,whichallowsustopassashellvariableinsidethe gawkprogram.If the team name matches, the code keeps a running sum of the three scores in the data record, adding each data record’s values, as long as that data record matches the team name. Attheendofeachloopiteration,the gawk code displays the score totals, as well as the averageofthescores.Theoutputshouldlooklikethis: $./bowling.sh Totalforteam1is635,theaverageis105.833 Totalforteam2is706,theaverageis117.667 $ Now you have a handy shell script to calculate the results of all your bowling tournaments;youjustneedtoplugthedatafromeachplayerintothedatatextfileandrun thescript! Summary This chapter walked you through the more advanced features of the gawk programming language.Everyprogramminglanguagerequiresusingvariables,and gawkisnodifferent. The gawk programming language includes some built-in variables that you can use to reference specific data field values and retrieve information about the number of data fieldsandrecordsprocessedinthedatafile.Youcanalsocreateyourownvariablesfor useinyourscripts. Thegawkprogramminglanguagealsoprovidesmanyofthestandardstructuredcommands you expect from a programming language. You can easily create fancy programs using if-thenlogicand while, do-while,and forloops.Eachofthesecommandsallowsyou toaltertheflowofyour gawkprogramscripttoiteratethroughdatafieldvaluestocreate detaileddatareports. Theprintfcommandisagreattooltohaveifyouneedtocustomizeyourreportoutput. Itallowsyoutospecifytheexactformatfordisplayingdatafromthegawkprogramscript. You can easily create formatted reports, placing data elements in exactly the correct position. Finally, this chapter discussed the many built-in functions available in the gawk programming language and showed you how to create your own functions. The gawk program contains many useful functions for handling mathematical features, such as standard square roots and logarithms, as well as trigonometric functions. There are also several string-related functions that make extracting substrings from larger strings a breeze. Youaren’tlimitedtothebuilt-infunctionsinthe gawkprogram.Ifyou’reworkingonan applicationthatuseslotsofspecializedalgorithms,youcancreateyourownfunctionsto processthealgorithmsandusethosefunctionsinyourowncode.Youcanalsosetupa libraryfilecontainingallthefunctionsyouuseinyour gawkprograms,savingyoutime andeffortinallyourcoding. Thenextchapterswitchesgearsalittle.Itexaminesafewothershellenvironmentsyou mayrunintoinyourLinuxshell-scriptingendeavors.Althoughthebashshellisthemost commonshellusedinLinux,it’snottheonlyshell.Ithelpstoknowalittleaboutsomeof theothershellsavailableandhowtheydifferfromthebashshell. Chapter23 WorkingwithAlternativeShells InThisChapter 1. Understandingthedashshell 2. Programminginthedashshell 3. Introducingthezshshell 4. Writingscriptsforzsh AlthoughthebashshellisthemostwidelyusedshellinLinuxdistributions,itisn’t theonlyone.Nowthatyou’veseenthestandardLinuxbashshellandwhatyoucan dowithit,it’stimetoexamineafewothershellsavailableintheLinuxworld.This chapterdescribestwoothershellsthatyoumayrunintoinyourLinuxjourneyand howtheydifferfromthebashshell. WhatIsthedashShell? TheDebiandashshellhashadaninterestingpast.It’sadirectdescendantoftheashshell, a simple copy of the original Bourne shell available on Unix systems (see Chapter 1). KennethAlmquistcreatedasmall-scaleversionoftheBourneshellforUnixsystemsand calledittheAlmquistshell,whichwasthenshortenedtoash.Thisoriginalversionofthe ash shell was extremely small and fast but without many advanced features, such as commandlineeditingorhistoryfeatures,makingitdifficulttouseasaninteractiveshell. The NetBSD Unix operating system adopted the ash shell and still uses it today as the default shell. The NetBSD developers customized the ash shell by adding several new features, making it closer to the Bourne shell. The new features include command line editingusingbothemacsandvieditorcommands,aswellasahistorycommandtorecall previouslyenteredcommands.ThisversionoftheashshellisalsousedbytheFreeBSD operatingsystemasthedefaultloginshell. TheDebianLinuxdistributioncreateditsownversionoftheashshell(calledDebianash, ordash)forinclusioninitsversionofLinux.Forthemostpart,dashcopiesthefeaturesof the NetBSD version of the ash shell, providing the advanced command line editing capabilities. However,toaddtotheshellconfusion,thedashshellisactuallynotthedefaultshellin many Debian-based Linux distributions. Because of the popularity of the bash shell in Linux,mostDebian-basedLinuxdistributionsusethebashshellasthenormalloginshell and use the dash shell only as a quick-start shell for the installation script to install the distributionfiles. The exception is the popular Ubuntu distribution. This often confuses shell script programmersandcausesagreatnumberofproblemswithrunningshellscriptsinaLinux environment.TheUbuntuLinuxdistributionusesthebashshellasthedefaultinteractive shell,butusesthedashshellasthedefault /bin/shshell.This“feature”reallyconfuses shellscriptprogrammers. AsyousawinChapter11,everyshellscriptmuststartwithalinethatdeclarestheshell usedforthescript.Inourbashshellscripts,we’vebeenusingthis: #!/bin/bash Thistellstheshelltousetheshellprogramlocatedat /bin/bashtoexecutethescript.In the Unix world, the default shell was always /bin/sh. Many shell script programmers familiarwiththeUnixenvironmentcopythisintotheirLinuxshellscripts: #!/bin/sh On most Linux distributions, the /bin/sh file is a symbolic link (see Chapter 3) to the /bin/bash shell program. This allows you to easily port shell scripts designed for the UnixBourneshelltotheLinuxenvironmentwithouthavingtomodifythem. Unfortunately,theUbuntuLinuxdistributionlinksthe/bin/shfiletothe/bin/dashshell program.Becausethedashshellcontainsonlyasubsetofthecommandsavailableinthe originalBourneshell,thiscan—andoftendoes—causesomeshellscriptstonotwork properly. Thenextsectionwalksyouthroughthebasicsofthedashshellandhowitdiffersfromthe bash shell. This is especially important to know if you write bash shell scripts that may needtoberuninanUbuntuenvironment. ThedashShellFeatures AlthoughboththebashshellandthedashshellaremodeledaftertheBourneshell,they havesomedifferences.ThissectionwalksyouthroughthefeaturesfoundintheDebian dash shell to acquaint you with how the dash shell works before we dive into the shell scriptingfeatures. Thedashcommandlineparameters Thedashshellusescommandlineparameterstocontrolitsbehavior.Table23.1liststhe commandlineparametersanddescribeswhateachonedoes. Table23.1ThedashCommandLineParameters Parameter -a -c -e -f -n -u -v -x -I -i -m -s -E -V Description Exportsallvariablesassignedtotheshell Readscommandsfromaspecifiedcommandstring Ifnotinteractive,exitsimmediatelyifanyuntestedcommandfails Displayspathnamewildcardcharacters Ifnotinteractive,readscommandsbutdoesn’texecutethem WritesanerrormessagetoSTDERRwhenattemptingtoexpandavariablethat isnotset WritesinputtoSTDERRasitisread WriteseachcommandtoSTDERRasitisexecuted IgnoresEOFcharactersfromtheinputwhenininteractivemode Forcestheshelltooperateininteractivemode Turnsonjobcontrol(enabledbydefaultininteractivemode) ReadscommandsfromSTDIN(thedefaultbehaviorifnofileargumentsare present) Enablestheemacscommandlineeditor Enablesthevicommandlineeditor Debian added a few additional command line parameters to the original ash shell commandlineparameterlist.The -Eand -Vcommandlineparametersenablethespecial commandlineeditingfeaturesofthedashshell. The-Ecommandlineparameterallowsyoutousetheemacseditorcommandsforediting command line text (see Chapter 10). You can use all the emacs commands for manipulatingtextonasinglelineusingtheCtrlandMetakeycombinations. The -V command line parameter allows you to use the vi editor commands for editing command line text (again, see Chapter 10). This feature allows you to switch between normalmodeandvieditormodeonthecommandlinebyusingtheEsckey.Whenyou’re invieditormode,youcanuseallthestandardvieditorcommands(suchas xtodeletea character,anditoinserttext).Afteryoufinisheditingthecommandline,youmustpress theEsckeyagaintoexitvieditormode. Thedashenvironmentvariables Thedashshellusesquiteafewdefaultenvironmentvariablesusestotrackinformation, and you can create your own environment variables as well. This section describes the environmentvariablesandhowdashhandlesthem. Defaultenvironmentvariables Thedashenvironmentvariablesareverysimilartotheenvironmentvariablesusedinbash (seeChapter6).Thisisnotbyaccident.Rememberthatboththedashandbashshellsare extensions of the Bourne shell, so they both incorporate many of its features. However, becauseofitsgoalofsimplicity,thedashshellcontainssignificantlyfewerenvironment variablesthanthebashshell.Youneedtotakethisintoconsiderationwhencreatingshell scriptsinadashshellenvironment. Thedashshellusesthesetcommandtodisplayenvironmentvariables: $set COLORTERM=” DESKTOP_SESSION=‘default’ DISPLAY=’:0.0’ DM_CONTROL=’/var/run/xdmctl’ GS_LIB=’/home/atest/.fonts’ HOME=’/home/atest’ IFS=’ ’ KDEROOTHOME=’/root/.kde’ KDE_FULL_SESSION=‘true’ KDE_MULTIHEAD=‘false’ KONSOLE_DCOP=‘DCOPRef(konsole-5293,konsole)’ KONSOLE_DCOP_SESSION=‘DCOPRef(konsole-5293,session-1)’ LANG=‘en_US’ LANGUAGE=‘en’ LC_ALL=‘en_US’ LOGNAME=‘atest’ OPTIND=‘1’ PATH=’/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin’ PPID=‘5293’ PS1=’$‘ PS2=’>‘ PS4=’+‘ PWD=’/home/atest’ SESSION_MANAGER=‘local/testbox:/tmp/.ICE-unix/5051’ SHELL=’/bin/dash’ SHLVL=‘1’ TERM=‘xterm’ USER=‘atest’ XCURSOR_THEME=‘default’ _=‘ash’ $ Your default dash shell environment will most likely differ, because different Linux distributionsassigndifferentdefaultenvironmentvariablesatlogin. Positionalparameters In addition to the default environment variables, the dash shell also assigns special variables to any parameters defined in the command line. Here are the positional parametervariablesavailableforuseinthedashshell: $0:Thenameoftheshell $n:Thenthpositionparameter $*:Asinglevaluewiththecontentsofalltheparameters,separatedbythefirst characterintheIFSenvironmentvariable,oraspaceifIFSisn’tdefined $@:Expandstomultipleargumentsconsistingofallthecommandlineparameters $#:Thenumberofpositionalparameters $?:Theexitstatusofthemostrecentcommand $-:Thecurrentoptionflags $$:TheprocessID(PID)ofthecurrentshell $!:TheprocessID(PID)ofthemostrecentbackgroundcommand Allthedashpositionalparametersmimicthesamepositionalparametersavailableinthe bashshell.Youcanuseeachofthepositionalparametersinyourshellscriptsjustasyou wouldinthebashshell. User-definedenvironmentvariables Thedashshellalsoallowsyoutosetyourownenvironmentvariables.Aswithbash,you can define a new environment variable on the command line by using the assignment statement: $testing=10;exporttesting $echo$testing 10 $ Withoutthe exportcommand,user-definedenvironmentvariablesarevisibleonlyinthe currentshellorprocess. Caution There’sonehugedifferencebetweendashvariablesandbashvariables.Thedash shelldoesn’tsupportvariablearrays.Thissmallfeaturecausesallsortsofproblems foradvancedshellscriptwriters. Thedashbuilt-incommands Just as with the bash shell, the dash shell contains a set of built-in commands that it recognizes.Youcanusethesecommandsdirectlyfromthecommandlineinterface,oryou can incorporate them in your shell scripts. Table 23.2 lists the dash shell built-in commands. Table23.2ThedashShellBuilt-InCommands Command Description alias Createsanaliasstringtorepresentatextstring bg Continuesspecifiedjobinbackgroundmode cd Switchestothespecifieddirectory echo Displaysatextstringandenvironmentvariables eval Concatenatesallargumentswithaspace exec Replacestheshellprocesswiththespecifiedcommand exit Terminatestheshellprocess export Exportsthespecifiedenvironmentvariableforuseinallchildshells fg Continuesspecifiedjobinforegroundmode getopts Obtainsoptionsandargumentsfromalistofparameters hash Maintainsandretrievesahashtableofrecentcommandsandtheirlocations pwd Displaysthevalueofthecurrentworkingdirectory read ReadsalinefromSTDINandassignthevaluetoavariable readonly ReadsalinefromSTDINtoavariablethatcan’tbechanged printf Displaystextandvariablesusingaformattedstring set Listsorsetsoptionflagsandenvironmentvariables shift Shiftsthepositionalparametersaspecifiednumberoftimes test Evaluatesanexpressionandreturns0iftrueor1iffalse Displaystheaccumulateduserandsystemtimesfortheshellandallshell times processes trap Parsesandexecutesanactionwhentheshellreceivesaspecifiedsignal Interpretsthespecifiednameanddisplaystheresolution(alias,built-in, type command,keyword) ulimit umask unalias unset wait Queriesorsetslimitsonprocesses Setsthevalueofthedefaultfileanddirectorypermissions Removesthespecifiedalias Removesthespecifiedvariableoroptionflagfromtheexportedvariables Waitsforthespecifiedjobtocompleteandreturnstheexitstatus Youprobablyrecognizeallthesebuilt-incommandsfromthebashshell.Thedashshell supportsmanyofthesamebuilt-incommandsasthebashshell.You’llnoticethatthere arenocommandsforthecommandhistoryfileorforthedirectorystack.Thedashshell doesn’tsupportthesefeatures. Scriptingindash Unfortunately,thedashshelldoesn’trecognizeallthescriptingfeaturesofthebashshell. Shell scripts written for the bash environment often fail when run in the dash shell, causing all sorts of grief for shell script programmers. This section describes the differencesyou’llneedtobeawareoftogetyourshellscriptstorunproperlyinadash shellenvironment. Creatingdashscripts Youprobablyguessedbynowthatcreatingshellscriptsforthedashshellisprettysimilar tocreatingshellscriptsforthebashshell.Youshouldalwaysspecifywhichshellyouwant touseinyourscripttoensurethatthescriptrunswiththepropershell. Youdothisonthefirstlineoftheshell: #!/bin/dash You can also specify a shell command line parameter on this line, as was documented earlierin“Thedashcommandlineparameters”section. Thingsthatdon’twork Unfortunately,becausethedashshellisonlyasubsetoftheBourneshellfeatures,some thingsinbashshellscriptsdon’tworkinthedashshell.Theseareoftencalledbashisms. Thissectionisaquicksummaryofbashshellfeaturesyoumaybeusedtousinginyour bashshellscriptsthatdon’tworkifyou’reinadashshellenvironment. Usingarithmetic Chapter11showedthreewaystoexpressamathematicaloperationinthebashshellscript: Usingtheexprcommand:exproperation Usingsquarebrackets:$[operation] Usingdoubleparentheses:$((operation)) Thedashshellsupportstheexprcommandandthedoubleparenthesesmethodbutdoesn’t supportthesquarebracketmethod.Thiscanbeaproblemifyouhavelotsofmathematical operationsthatusethesquarebrackets. Theproperformatforperformingmathematicaloperationsindashshellscriptsistouse thedoubleparenthesesmethod: $cattest5b #!/bin/dash #testingmathematicaloperations value1=10 value2=15 value3=$(($value1*$value2)) echo“Theansweris$value3” $./test5b Theansweris150 $ Nowtheshellcanperformthecalculationpr