This article is about scripting in Unix-like systems. For batch programming in DOS, OS/2 and Windows, see Batch file. For batch programming in Windows PowerShell shell, see Windows PowerShell § Scripting. For programming in the shells (cmd.exe) in operating systems of the Windows NT/2000 series, see cmd.exe. For shell programming, by means of files called Command Scripts or Procedures on Vax/VMS machines, see DIGITAL Command Language.
A shell script is a computer program designed to be run by a Unix shell, a command-line interpreter.[1] The various dialects of shell scripts are considered to be scripting languages. Typical operations performed by shell scripts include file manipulation, program execution, and printing text. A script which sets up the environment, runs the program, and does any necessary cleanup or logging, is called a wrapper.
The term is also used more generally to mean the automated mode of running an operating system shell; each operating system uses a particular name for these functions including batch files (MSDos-Win95 stream, OS/2), command procedures (VMS), and shell scripts (Windows NT stream and third-party derivatives like 4NT—article is at cmd.exe), and mainframe operating systems are associated with a number of terms.
Shells commonly present in Unix and Unix-like systems include the Korn shell, the Bourne shell, and GNU Bash. While a Unix operating system may have a different default shell, such as Zsh on macOS, these shells are typically present for backwards compatibility.
Capabilities[edit]
[edit]
Comments are ignored by the shell. They typically begin with the hash symbol (#
), and continue until the end of the line.[2]
Configurable choice of scripting language[edit]
The shebang, or hash-bang, is a special kind of comment which the system uses to determine what interpreter to use to execute the file. The shebang must be the first line of the file, and start with «#!
«.[2] In Unix-like operating systems, the characters following the «#!
» prefix are interpreted as a path to an executable program that will interpret the script.[3]
Shortcuts[edit]
A shell script can provide a convenient variation of a system command where special environment settings, command options, or post-processing apply automatically, but in a way that allows the new script to still act as a fully normal Unix command.
One example would be to create a version of ls, the command to list files, giving it a shorter command name of l
, which would be normally saved in a user’s bin
directory as /home/username/bin/l
, and a default set of command options pre-supplied.
#!/bin/sh LC_COLLATE=C ls -FCas "$@"
Here, the first line uses a shebang to indicate which interpreter should execute the rest of the script, and the second line makes a listing with options for file format indicators, columns, all files (none omitted), and a size in blocks. The LC_COLLATE=C
sets the default collation order to not fold upper and lower case together, not intermix dotfiles with normal filenames as a side effect of ignoring punctuation in the names (dotfiles are usually only shown if an option like -a
is used), and the "$@"
causes any parameters given to l
to pass through as parameters to ls, so that all of the normal options and other syntax known to ls can still be used.
The user could then simply use l
for the most commonly used short listing.
Another example of a shell script that could be used as a shortcut would be to print a list of all the files and directories within a given directory.
In this case, the shell script would start with its normal starting line of #!/bin/sh. Following this, the script executes the command clear which clears the terminal of all text before going to the next line. The following line provides the main function of the script. The ls -al command lists the files and directories that are in the directory from which the script is being run. The ls command attributes could be changed to reflect the needs of the user.
Note: If an implementation does not have the clear command, try using the clr command instead.
Batch jobs[edit]
Shell scripts allow several commands that would be entered manually at a command-line interface to be executed automatically, and without having to wait for a user to trigger each stage of the sequence. For example, in a directory with three C source code files, rather than manually running the four commands required to build the final program from them, one could instead create a script for POSIX-compliant shells, here named build
and kept in the directory with them, which would compile them automatically:
#!/bin/sh printf 'compiling...n' cc -c foo.c cc -c bar.c cc -c qux.c cc -o myprog foo.o bar.o qux.o printf 'done.n'
The script would allow a user to save the file being edited, pause the editor, and then just run ./build
to create the updated program, test it, and then return to the editor. Since the 1980s or so, however, scripts of this type have been replaced with utilities like make which are specialized for building programs.
Generalization[edit]
Simple batch jobs are not unusual for isolated tasks, but using shell loops, tests, and variables provides much more flexibility to users. A POSIX sh script to convert JPEG images to PNG images, where the image names are provided on the command-line—possibly via wildcards—instead of each being listed within the script, can be created with this file, typically saved in a file like /home/username/bin/jpg2png
#!/bin/sh for jpg; do # use $jpg in place of each filename given, in turn png=${jpg%.jpg}.png # construct the PNG version of the filename by replacing .jpg with .png printf 'converting "%s" ...n' "$jpg" # output status info to the user running the script if convert "$jpg" jpg.to.png; then # use convert (provided by ImageMagick) to create the PNG in a temp file mv jpg.to.png "$png" # if it worked, rename the temporary PNG image to the correct name else # ...otherwise complain and exit from the script printf >&2 'jpg2png: error: failed output saved in "jpg.to.png".n' exit 1 fi # the end of the "if" test construct done # the end of the "for" loop printf 'all conversions successfuln' # tell the user the good news
The jpg2png
command can then be run on an entire directory full of JPEG images with just /home/username/bin/jpg2png *.jpg
Programming[edit]
Many modern shells also supply various features usually found only in more sophisticated general-purpose programming languages, such as control-flow constructs, variables, comments, arrays, subroutines and so on. With these sorts of features available, it is possible to write reasonably sophisticated applications as shell scripts. However, they are still limited by the fact that most shell languages have little or no support for data typing systems, classes, threading, complex math, and other common full language features, and are also generally much slower than compiled code or interpreted languages written with speed as a performance goal.
The standard Unix tools sed and awk provide extra capabilities for shell programming; Perl can also be embedded in shell scripts as can other scripting languages like Tcl. Perl and Tcl come with graphics toolkits as well.
Typical POSIX scripting languages[edit]
Scripting languages commonly found on UNIX, Linux, and POSIX-compliant operating system installations include:
- KornShell (
ksh
) in several possible versions such as ksh88, Korn Shell ’93 and others. - The Bourne shell (
sh
), one of the oldest shells still common in use - The C shell (
csh
) - GNU Bash (
bash
) tclsh
, a shell which is a main component of the Tcl/Tk programming language.- The wish is a GUI-based Tcl/Tk shell.
The C and Tcl shells have syntax quite similar to that of said programming languages, and the Korn shells and Bash are developments of the Bourne shell, which is based on the ALGOL language with elements of a number of others added as well.[4] On the other hand, the various shells plus tools like awk, sed, grep, and BASIC, Lisp, C and so forth contributed to the Perl programming language.[5]
Other shells that may be available on a machine or for download and/or purchase include:
- Almquist shell (
ash
) - PowerShell (
msh
) - Z shell (
zsh
, a particularly common enhanced KornShell) - The Tenex C Shell (
tcsh
).
Related programs such as shells based on Python, Ruby, C, Java, Perl, Pascal, Rexx etc. in various forms are also widely available. Another somewhat common shell is Old shell (osh
), whose manual page states it «is an enhanced, backward-compatible port of the standard command interpreter from Sixth Edition UNIX.»[6]
So called remote shells such as
- a Remote Shell (
rsh
) - a Secure Shell (
ssh
)
are really just tools to run a more complex shell on a remote system and have no ‘shell’ like characteristics themselves.
Other scripting languages[edit]
Many powerful scripting languages have been introduced for tasks that are too large or complex to be comfortably handled with ordinary shell scripts, but for which the advantages of a script are desirable and the development overhead of a full-blown, compiled programming language would be disadvantageous. The specifics of what separates scripting languages from high-level programming languages is a frequent source of debate, but, generally speaking, a scripting language is one which requires an interpreter.
Life cycle[edit]
Shell scripts often serve as an initial stage in software development, and are often subject to conversion later to a different underlying implementation, most commonly being converted to Perl, Python, or C. The interpreter directive allows the implementation detail to be fully hidden inside the script, rather than being exposed as a filename extension, and provides for seamless reimplementation in different languages with no impact on end users.
While files with the «.sh» file extension are usually a shell script of some kind, most shell scripts do not have any filename extension.[7][8][9][10]
Advantages and disadvantages[edit]
Perhaps the biggest advantage of writing a shell script is that the commands and syntax are exactly the same as those directly entered at the command-line. The programmer does not have to switch to a totally different syntax, as they would if the script were written in a different language, or if a compiled language were used.
Often, writing a shell script is much quicker than writing the equivalent code in other programming languages. The many advantages include easy program or file selection, quick start, and interactive debugging. A shell script can be used to provide a sequencing and decision-making linkage around existing programs, and for moderately sized scripts the absence of a compilation step is an advantage. Interpretive running makes it easy to write debugging code into a script and re-run it to detect and fix bugs. Non-expert users can use scripting to tailor the behavior of programs, and shell scripting provides some limited scope for multiprocessing.
On the other hand, shell scripting is prone to costly errors. Inadvertent typing errors such as rm -rf * /
(instead of the intended rm -rf */
) are folklore in the Unix community; a single extra space converts the command from one that deletes all subdirectories contained in the current directory, to one which deletes everything from the file system’s root directory. Similar problems can transform cp
and mv
into dangerous weapons, and misuse of the >
redirect can delete the contents of a file. This is made more problematic by the fact that many UNIX commands differ in name by only one letter: cp
, cd
, dd
, df
, etc.
Another significant disadvantage is the slow execution speed and the need to launch a new process for almost every shell command executed. When a script’s job can be accomplished by setting up a pipeline in which efficient filter commands perform most of the work, the slowdown is mitigated, but a complex script is typically several orders of magnitude slower than a conventional compiled program that performs an equivalent task.
There are also compatibility problems between different platforms. Larry Wall, creator of Perl, famously wrote that «It is easier to port a shell than a shell script.»[11]
Similarly, more complex scripts can run into the limitations of the shell scripting language itself; the limits make it difficult to write quality code, and extensions by various shells to ameliorate problems with the original shell language can make problems worse.[12]
Many disadvantages of using some script languages are caused by design flaws within the language syntax or implementation, and are not necessarily imposed by the use of a text-based command-line; there are a number of shells which use other shell programming languages or even full-fledged languages like Scsh (which uses Scheme).
Interoperability among scripting languages[edit]
Different scripting languages may share many common elements, largely due to being POSIX based, and some shells offer modes to emulate different shells. This allows a shell script written in one scripting language to be adapted into another.
One example of this is Bash, which offers the same grammar and syntax as the Bourne shell, and which also provides a POSIX-compliant mode.[13] As such, most shell scripts written for the Bourne shell can be run in BASH, but the reverse may not be true since BASH has extensions which are not present in the Bourne shell. As such, these features are known as bashisms.[14]
Shell scripting on other operating systems[edit]
Interoperability software such as Cygwin, the MKS Toolkit, Interix (which is available in the Microsoft Windows Services for UNIX), Hamilton C shell, UWIN (AT&T Unix for Windows) and others allow Unix shell programs to be run on machines running Windows NT and its successors, with some loss of functionality on the MS-DOS-Windows 95 branch, as well as earlier MKS Toolkit versions for OS/2. At least three DCL implementations for Windows type operating systems—in addition to XLNT, a multiple-use scripting language package which is used with the command shell, Windows Script Host and CGI programming—are available for these systems as well. Mac OS X and subsequent are Unix-like as well.[15]
In addition to the aforementioned tools, some POSIX and OS/2 functionality can be used with the corresponding environmental subsystems of the Windows NT operating system series up to Windows 2000 as well. A third, 16-bit subsystem often called the MS-DOS subsystem uses the Command.com provided with these operating systems to run the aforementioned MS-DOS batch files.[16]
The console alternatives 4DOS, 4OS2, FreeDOS, Peter Norton’s NDOS and 4NT / Take Command which add functionality to the Windows NT-style cmd.exe, MS-DOS/Windows 95 batch files (run by Command.com), OS/2’s cmd.exe, and 4NT respectively are similar to the shells that they enhance and are more integrated with the Windows Script Host, which comes with three pre-installed engines, VBScript, JScript, and VBA and to which numerous third-party engines can be added, with Rexx, Perl, Python, Ruby, and Tcl having pre-defined functions in 4NT and related programs. PC DOS is quite similar to MS-DOS, whilst DR DOS is more different. Earlier versions of Windows NT are able to run contemporary versions of 4OS2 by the OS/2 subsystem.
Scripting languages are, by definition, able to be extended; for example, a MS-DOS/Windows 95/98 and Windows NT type systems allows for shell/batch programs to call tools like KiXtart, QBasic, various BASIC, Rexx, Perl, and Python implementations, the Windows Script Host and its installed engines. On Unix and other POSIX-compliant systems, awk and sed are used to extend the string and numeric processing ability of shell scripts. Tcl, Perl, Rexx, and Python have graphics toolkits and can be used to code functions and procedures for shell scripts which pose a speed bottleneck (C, Fortran, assembly language &c are much faster still) and to add functionality not available in the shell language such as sockets and other connectivity functions, heavy-duty text processing, working with numbers if the calling script does not have those abilities, self-writing and self-modifying code, techniques like recursion, direct memory access, various types of sorting and more, which are difficult or impossible in the main script, and so on. Visual Basic for Applications and VBScript can be used to control and communicate with such things as spreadsheets, databases, scriptable programs of all types, telecommunications software, development tools, graphics tools and other software which can be accessed through the Component Object Model.
See also[edit]
- Glue code
- Interpreter directive
- Shebang symbol (#!)
- Unix shells
- PowerShell
- Windows Script Host
References[edit]
- ^ Kernighan, Brian W.; Pike, Rob (1984), «3. Using the Shell», The UNIX Programming Environment, Prentice Hall, Inc., p. 94, ISBN 0-13-937699-2,
The shell is actually a programming language: it has variables, loops, decision-making, and so on.
- ^ a b Johnson, Chris (2009).
[1] Pro Bash Programming: Scripting the Linux Shell, Apress, Retrieved on September 27, 2019. ISBN 9781430219989 - ^ «exec(3p) – POSIX Programmer’s Manual». Retrieved 2020-07-24.
- ^ Unix Shells By Example, pp 7-10,
- ^ Programming Perl, 5th Edition, preface
- ^ «osh — manned.org». manned.org. Retrieved 2019-01-16.
- ^ Robbins, Arnold; Hannah, Elbert; Lamb, Linda (2008). Learning the vi and Vim Editors. p. 205. ISBN 9781449313258.
- ^ Easttom, Chuck (2012). Essential Linux Administration:: A Comprehensive Guide for Beginners. p. 228. ISBN 978-1435459571.
- ^ Kumari, Sinny (November 23, 2015). Linux Shell Scripting Essentials. Packt Publishing Ltd. ISBN 9781783552375. Retrieved May 7, 2017.
Rather than using a file extension for shell scripts, it’s preferred to keep a filename without extension and let an interpreter identify the type by looking into shebang(#!).
- ^ Taylor, Dave; Perry, Brandon (December 16, 2016). Wicked Cool Shell Scripts, 2nd Edition: 101 Scripts for Linux, OS X and UNIX Systems. No Starch Press. ISBN 9781593276027. Retrieved May 7, 2017.
Shell scripts don’t need a special file extension, so leave the extension blank (or you can add the extension .sh if you prefer, but this isn’t required.
- ^ Larry Wall (January 4, 1991). «Finding the last arg». Newsgroup: comp.unix.shell. Retrieved January 5, 2023.
- ^ Christiansen, Tom. «Csh Programming Considered Harmful».
- ^ «Major Differences From The Bourne Shell».
- ^ «24 Bashism To Avoid for POSIX-Compliant Shell Scripts». 18 May 2022.
- ^ MSDN[not specific enough to verify]
- ^ Windows NT 4 Workstation Resource Kit
External links[edit]
- An Introduction To Shell Programming by Greg Goebel
- UNIX / Linux shell scripting tutorial by Steve Parker
- Shell Scripting Primer (Apple)
- What to watch out for when writing portable shell scripts by Peter Seebach
- Free Unix Shell scripting books
- Beginners/BashScripting, Ubuntu Linux
This article is about scripting in Unix-like systems. For batch programming in DOS, OS/2 and Windows, see Batch file. For batch programming in Windows PowerShell shell, see Windows PowerShell § Scripting. For programming in the shells (cmd.exe) in operating systems of the Windows NT/2000 series, see cmd.exe. For shell programming, by means of files called Command Scripts or Procedures on Vax/VMS machines, see DIGITAL Command Language.
A shell script is a computer program designed to be run by a Unix shell, a command-line interpreter.[1] The various dialects of shell scripts are considered to be scripting languages. Typical operations performed by shell scripts include file manipulation, program execution, and printing text. A script which sets up the environment, runs the program, and does any necessary cleanup or logging, is called a wrapper.
The term is also used more generally to mean the automated mode of running an operating system shell; each operating system uses a particular name for these functions including batch files (MSDos-Win95 stream, OS/2), command procedures (VMS), and shell scripts (Windows NT stream and third-party derivatives like 4NT—article is at cmd.exe), and mainframe operating systems are associated with a number of terms.
Shells commonly present in Unix and Unix-like systems include the Korn shell, the Bourne shell, and GNU Bash. While a Unix operating system may have a different default shell, such as Zsh on macOS, these shells are typically present for backwards compatibility.
Capabilities[edit]
[edit]
Comments are ignored by the shell. They typically begin with the hash symbol (#
), and continue until the end of the line.[2]
Configurable choice of scripting language[edit]
The shebang, or hash-bang, is a special kind of comment which the system uses to determine what interpreter to use to execute the file. The shebang must be the first line of the file, and start with «#!
«.[2] In Unix-like operating systems, the characters following the «#!
» prefix are interpreted as a path to an executable program that will interpret the script.[3]
Shortcuts[edit]
A shell script can provide a convenient variation of a system command where special environment settings, command options, or post-processing apply automatically, but in a way that allows the new script to still act as a fully normal Unix command.
One example would be to create a version of ls, the command to list files, giving it a shorter command name of l
, which would be normally saved in a user’s bin
directory as /home/username/bin/l
, and a default set of command options pre-supplied.
#!/bin/sh LC_COLLATE=C ls -FCas "$@"
Here, the first line uses a shebang to indicate which interpreter should execute the rest of the script, and the second line makes a listing with options for file format indicators, columns, all files (none omitted), and a size in blocks. The LC_COLLATE=C
sets the default collation order to not fold upper and lower case together, not intermix dotfiles with normal filenames as a side effect of ignoring punctuation in the names (dotfiles are usually only shown if an option like -a
is used), and the "$@"
causes any parameters given to l
to pass through as parameters to ls, so that all of the normal options and other syntax known to ls can still be used.
The user could then simply use l
for the most commonly used short listing.
Another example of a shell script that could be used as a shortcut would be to print a list of all the files and directories within a given directory.
In this case, the shell script would start with its normal starting line of #!/bin/sh. Following this, the script executes the command clear which clears the terminal of all text before going to the next line. The following line provides the main function of the script. The ls -al command lists the files and directories that are in the directory from which the script is being run. The ls command attributes could be changed to reflect the needs of the user.
Note: If an implementation does not have the clear command, try using the clr command instead.
Batch jobs[edit]
Shell scripts allow several commands that would be entered manually at a command-line interface to be executed automatically, and without having to wait for a user to trigger each stage of the sequence. For example, in a directory with three C source code files, rather than manually running the four commands required to build the final program from them, one could instead create a script for POSIX-compliant shells, here named build
and kept in the directory with them, which would compile them automatically:
#!/bin/sh printf 'compiling...n' cc -c foo.c cc -c bar.c cc -c qux.c cc -o myprog foo.o bar.o qux.o printf 'done.n'
The script would allow a user to save the file being edited, pause the editor, and then just run ./build
to create the updated program, test it, and then return to the editor. Since the 1980s or so, however, scripts of this type have been replaced with utilities like make which are specialized for building programs.
Generalization[edit]
Simple batch jobs are not unusual for isolated tasks, but using shell loops, tests, and variables provides much more flexibility to users. A POSIX sh script to convert JPEG images to PNG images, where the image names are provided on the command-line—possibly via wildcards—instead of each being listed within the script, can be created with this file, typically saved in a file like /home/username/bin/jpg2png
#!/bin/sh for jpg; do # use $jpg in place of each filename given, in turn png=${jpg%.jpg}.png # construct the PNG version of the filename by replacing .jpg with .png printf 'converting "%s" ...n' "$jpg" # output status info to the user running the script if convert "$jpg" jpg.to.png; then # use convert (provided by ImageMagick) to create the PNG in a temp file mv jpg.to.png "$png" # if it worked, rename the temporary PNG image to the correct name else # ...otherwise complain and exit from the script printf >&2 'jpg2png: error: failed output saved in "jpg.to.png".n' exit 1 fi # the end of the "if" test construct done # the end of the "for" loop printf 'all conversions successfuln' # tell the user the good news
The jpg2png
command can then be run on an entire directory full of JPEG images with just /home/username/bin/jpg2png *.jpg
Programming[edit]
Many modern shells also supply various features usually found only in more sophisticated general-purpose programming languages, such as control-flow constructs, variables, comments, arrays, subroutines and so on. With these sorts of features available, it is possible to write reasonably sophisticated applications as shell scripts. However, they are still limited by the fact that most shell languages have little or no support for data typing systems, classes, threading, complex math, and other common full language features, and are also generally much slower than compiled code or interpreted languages written with speed as a performance goal.
The standard Unix tools sed and awk provide extra capabilities for shell programming; Perl can also be embedded in shell scripts as can other scripting languages like Tcl. Perl and Tcl come with graphics toolkits as well.
Typical POSIX scripting languages[edit]
Scripting languages commonly found on UNIX, Linux, and POSIX-compliant operating system installations include:
- KornShell (
ksh
) in several possible versions such as ksh88, Korn Shell ’93 and others. - The Bourne shell (
sh
), one of the oldest shells still common in use - The C shell (
csh
) - GNU Bash (
bash
) tclsh
, a shell which is a main component of the Tcl/Tk programming language.- The wish is a GUI-based Tcl/Tk shell.
The C and Tcl shells have syntax quite similar to that of said programming languages, and the Korn shells and Bash are developments of the Bourne shell, which is based on the ALGOL language with elements of a number of others added as well.[4] On the other hand, the various shells plus tools like awk, sed, grep, and BASIC, Lisp, C and so forth contributed to the Perl programming language.[5]
Other shells that may be available on a machine or for download and/or purchase include:
- Almquist shell (
ash
) - PowerShell (
msh
) - Z shell (
zsh
, a particularly common enhanced KornShell) - The Tenex C Shell (
tcsh
).
Related programs such as shells based on Python, Ruby, C, Java, Perl, Pascal, Rexx etc. in various forms are also widely available. Another somewhat common shell is Old shell (osh
), whose manual page states it «is an enhanced, backward-compatible port of the standard command interpreter from Sixth Edition UNIX.»[6]
So called remote shells such as
- a Remote Shell (
rsh
) - a Secure Shell (
ssh
)
are really just tools to run a more complex shell on a remote system and have no ‘shell’ like characteristics themselves.
Other scripting languages[edit]
Many powerful scripting languages have been introduced for tasks that are too large or complex to be comfortably handled with ordinary shell scripts, but for which the advantages of a script are desirable and the development overhead of a full-blown, compiled programming language would be disadvantageous. The specifics of what separates scripting languages from high-level programming languages is a frequent source of debate, but, generally speaking, a scripting language is one which requires an interpreter.
Life cycle[edit]
Shell scripts often serve as an initial stage in software development, and are often subject to conversion later to a different underlying implementation, most commonly being converted to Perl, Python, or C. The interpreter directive allows the implementation detail to be fully hidden inside the script, rather than being exposed as a filename extension, and provides for seamless reimplementation in different languages with no impact on end users.
While files with the «.sh» file extension are usually a shell script of some kind, most shell scripts do not have any filename extension.[7][8][9][10]
Advantages and disadvantages[edit]
Perhaps the biggest advantage of writing a shell script is that the commands and syntax are exactly the same as those directly entered at the command-line. The programmer does not have to switch to a totally different syntax, as they would if the script were written in a different language, or if a compiled language were used.
Often, writing a shell script is much quicker than writing the equivalent code in other programming languages. The many advantages include easy program or file selection, quick start, and interactive debugging. A shell script can be used to provide a sequencing and decision-making linkage around existing programs, and for moderately sized scripts the absence of a compilation step is an advantage. Interpretive running makes it easy to write debugging code into a script and re-run it to detect and fix bugs. Non-expert users can use scripting to tailor the behavior of programs, and shell scripting provides some limited scope for multiprocessing.
On the other hand, shell scripting is prone to costly errors. Inadvertent typing errors such as rm -rf * /
(instead of the intended rm -rf */
) are folklore in the Unix community; a single extra space converts the command from one that deletes all subdirectories contained in the current directory, to one which deletes everything from the file system’s root directory. Similar problems can transform cp
and mv
into dangerous weapons, and misuse of the >
redirect can delete the contents of a file. This is made more problematic by the fact that many UNIX commands differ in name by only one letter: cp
, cd
, dd
, df
, etc.
Another significant disadvantage is the slow execution speed and the need to launch a new process for almost every shell command executed. When a script’s job can be accomplished by setting up a pipeline in which efficient filter commands perform most of the work, the slowdown is mitigated, but a complex script is typically several orders of magnitude slower than a conventional compiled program that performs an equivalent task.
There are also compatibility problems between different platforms. Larry Wall, creator of Perl, famously wrote that «It is easier to port a shell than a shell script.»[11]
Similarly, more complex scripts can run into the limitations of the shell scripting language itself; the limits make it difficult to write quality code, and extensions by various shells to ameliorate problems with the original shell language can make problems worse.[12]
Many disadvantages of using some script languages are caused by design flaws within the language syntax or implementation, and are not necessarily imposed by the use of a text-based command-line; there are a number of shells which use other shell programming languages or even full-fledged languages like Scsh (which uses Scheme).
Interoperability among scripting languages[edit]
Different scripting languages may share many common elements, largely due to being POSIX based, and some shells offer modes to emulate different shells. This allows a shell script written in one scripting language to be adapted into another.
One example of this is Bash, which offers the same grammar and syntax as the Bourne shell, and which also provides a POSIX-compliant mode.[13] As such, most shell scripts written for the Bourne shell can be run in BASH, but the reverse may not be true since BASH has extensions which are not present in the Bourne shell. As such, these features are known as bashisms.[14]
Shell scripting on other operating systems[edit]
Interoperability software such as Cygwin, the MKS Toolkit, Interix (which is available in the Microsoft Windows Services for UNIX), Hamilton C shell, UWIN (AT&T Unix for Windows) and others allow Unix shell programs to be run on machines running Windows NT and its successors, with some loss of functionality on the MS-DOS-Windows 95 branch, as well as earlier MKS Toolkit versions for OS/2. At least three DCL implementations for Windows type operating systems—in addition to XLNT, a multiple-use scripting language package which is used with the command shell, Windows Script Host and CGI programming—are available for these systems as well. Mac OS X and subsequent are Unix-like as well.[15]
In addition to the aforementioned tools, some POSIX and OS/2 functionality can be used with the corresponding environmental subsystems of the Windows NT operating system series up to Windows 2000 as well. A third, 16-bit subsystem often called the MS-DOS subsystem uses the Command.com provided with these operating systems to run the aforementioned MS-DOS batch files.[16]
The console alternatives 4DOS, 4OS2, FreeDOS, Peter Norton’s NDOS and 4NT / Take Command which add functionality to the Windows NT-style cmd.exe, MS-DOS/Windows 95 batch files (run by Command.com), OS/2’s cmd.exe, and 4NT respectively are similar to the shells that they enhance and are more integrated with the Windows Script Host, which comes with three pre-installed engines, VBScript, JScript, and VBA and to which numerous third-party engines can be added, with Rexx, Perl, Python, Ruby, and Tcl having pre-defined functions in 4NT and related programs. PC DOS is quite similar to MS-DOS, whilst DR DOS is more different. Earlier versions of Windows NT are able to run contemporary versions of 4OS2 by the OS/2 subsystem.
Scripting languages are, by definition, able to be extended; for example, a MS-DOS/Windows 95/98 and Windows NT type systems allows for shell/batch programs to call tools like KiXtart, QBasic, various BASIC, Rexx, Perl, and Python implementations, the Windows Script Host and its installed engines. On Unix and other POSIX-compliant systems, awk and sed are used to extend the string and numeric processing ability of shell scripts. Tcl, Perl, Rexx, and Python have graphics toolkits and can be used to code functions and procedures for shell scripts which pose a speed bottleneck (C, Fortran, assembly language &c are much faster still) and to add functionality not available in the shell language such as sockets and other connectivity functions, heavy-duty text processing, working with numbers if the calling script does not have those abilities, self-writing and self-modifying code, techniques like recursion, direct memory access, various types of sorting and more, which are difficult or impossible in the main script, and so on. Visual Basic for Applications and VBScript can be used to control and communicate with such things as spreadsheets, databases, scriptable programs of all types, telecommunications software, development tools, graphics tools and other software which can be accessed through the Component Object Model.
See also[edit]
- Glue code
- Interpreter directive
- Shebang symbol (#!)
- Unix shells
- PowerShell
- Windows Script Host
References[edit]
- ^ Kernighan, Brian W.; Pike, Rob (1984), «3. Using the Shell», The UNIX Programming Environment, Prentice Hall, Inc., p. 94, ISBN 0-13-937699-2,
The shell is actually a programming language: it has variables, loops, decision-making, and so on.
- ^ a b Johnson, Chris (2009).
[1] Pro Bash Programming: Scripting the Linux Shell, Apress, Retrieved on September 27, 2019. ISBN 9781430219989 - ^ «exec(3p) – POSIX Programmer’s Manual». Retrieved 2020-07-24.
- ^ Unix Shells By Example, pp 7-10,
- ^ Programming Perl, 5th Edition, preface
- ^ «osh — manned.org». manned.org. Retrieved 2019-01-16.
- ^ Robbins, Arnold; Hannah, Elbert; Lamb, Linda (2008). Learning the vi and Vim Editors. p. 205. ISBN 9781449313258.
- ^ Easttom, Chuck (2012). Essential Linux Administration:: A Comprehensive Guide for Beginners. p. 228. ISBN 978-1435459571.
- ^ Kumari, Sinny (November 23, 2015). Linux Shell Scripting Essentials. Packt Publishing Ltd. ISBN 9781783552375. Retrieved May 7, 2017.
Rather than using a file extension for shell scripts, it’s preferred to keep a filename without extension and let an interpreter identify the type by looking into shebang(#!).
- ^ Taylor, Dave; Perry, Brandon (December 16, 2016). Wicked Cool Shell Scripts, 2nd Edition: 101 Scripts for Linux, OS X and UNIX Systems. No Starch Press. ISBN 9781593276027. Retrieved May 7, 2017.
Shell scripts don’t need a special file extension, so leave the extension blank (or you can add the extension .sh if you prefer, but this isn’t required.
- ^ Larry Wall (January 4, 1991). «Finding the last arg». Newsgroup: comp.unix.shell. Retrieved January 5, 2023.
- ^ Christiansen, Tom. «Csh Programming Considered Harmful».
- ^ «Major Differences From The Bourne Shell».
- ^ «24 Bashism To Avoid for POSIX-Compliant Shell Scripts». 18 May 2022.
- ^ MSDN[not specific enough to verify]
- ^ Windows NT 4 Workstation Resource Kit
External links[edit]
- An Introduction To Shell Programming by Greg Goebel
- UNIX / Linux shell scripting tutorial by Steve Parker
- Shell Scripting Primer (Apple)
- What to watch out for when writing portable shell scripts by Peter Seebach
- Free Unix Shell scripting books
- Beginners/BashScripting, Ubuntu Linux
This article is about scripting in Unix-like systems. For batch programming in DOS, OS/2 and Windows, see Batch file. For batch programming in Windows PowerShell shell, see Windows PowerShell § Scripting. For programming in the shells (cmd.exe) in operating systems of the Windows NT/2000 series, see cmd.exe. For shell programming, by means of files called Command Scripts or Procedures on Vax/VMS machines, see DIGITAL Command Language.
A shell script is a computer program designed to be run by a Unix shell, a command-line interpreter.[1] The various dialects of shell scripts are considered to be scripting languages. Typical operations performed by shell scripts include file manipulation, program execution, and printing text. A script which sets up the environment, runs the program, and does any necessary cleanup or logging, is called a wrapper.
The term is also used more generally to mean the automated mode of running an operating system shell; each operating system uses a particular name for these functions including batch files (MSDos-Win95 stream, OS/2), command procedures (VMS), and shell scripts (Windows NT stream and third-party derivatives like 4NT—article is at cmd.exe), and mainframe operating systems are associated with a number of terms.
Shells commonly present in Unix and Unix-like systems include the Korn shell, the Bourne shell, and GNU Bash. While a Unix operating system may have a different default shell, such as Zsh on macOS, these shells are typically present for backwards compatibility.
Capabilities[edit]
[edit]
Comments are ignored by the shell. They typically begin with the hash symbol (#
), and continue until the end of the line.[2]
Configurable choice of scripting language[edit]
The shebang, or hash-bang, is a special kind of comment which the system uses to determine what interpreter to use to execute the file. The shebang must be the first line of the file, and start with «#!
«.[2] In Unix-like operating systems, the characters following the «#!
» prefix are interpreted as a path to an executable program that will interpret the script.[3]
Shortcuts[edit]
A shell script can provide a convenient variation of a system command where special environment settings, command options, or post-processing apply automatically, but in a way that allows the new script to still act as a fully normal Unix command.
One example would be to create a version of ls, the command to list files, giving it a shorter command name of l
, which would be normally saved in a user’s bin
directory as /home/username/bin/l
, and a default set of command options pre-supplied.
#!/bin/sh LC_COLLATE=C ls -FCas "$@"
Here, the first line uses a shebang to indicate which interpreter should execute the rest of the script, and the second line makes a listing with options for file format indicators, columns, all files (none omitted), and a size in blocks. The LC_COLLATE=C
sets the default collation order to not fold upper and lower case together, not intermix dotfiles with normal filenames as a side effect of ignoring punctuation in the names (dotfiles are usually only shown if an option like -a
is used), and the "$@"
causes any parameters given to l
to pass through as parameters to ls, so that all of the normal options and other syntax known to ls can still be used.
The user could then simply use l
for the most commonly used short listing.
Another example of a shell script that could be used as a shortcut would be to print a list of all the files and directories within a given directory.
In this case, the shell script would start with its normal starting line of #!/bin/sh. Following this, the script executes the command clear which clears the terminal of all text before going to the next line. The following line provides the main function of the script. The ls -al command lists the files and directories that are in the directory from which the script is being run. The ls command attributes could be changed to reflect the needs of the user.
Note: If an implementation does not have the clear command, try using the clr command instead.
Batch jobs[edit]
Shell scripts allow several commands that would be entered manually at a command-line interface to be executed automatically, and without having to wait for a user to trigger each stage of the sequence. For example, in a directory with three C source code files, rather than manually running the four commands required to build the final program from them, one could instead create a script for POSIX-compliant shells, here named build
and kept in the directory with them, which would compile them automatically:
#!/bin/sh printf 'compiling...n' cc -c foo.c cc -c bar.c cc -c qux.c cc -o myprog foo.o bar.o qux.o printf 'done.n'
The script would allow a user to save the file being edited, pause the editor, and then just run ./build
to create the updated program, test it, and then return to the editor. Since the 1980s or so, however, scripts of this type have been replaced with utilities like make which are specialized for building programs.
Generalization[edit]
Simple batch jobs are not unusual for isolated tasks, but using shell loops, tests, and variables provides much more flexibility to users. A POSIX sh script to convert JPEG images to PNG images, where the image names are provided on the command-line—possibly via wildcards—instead of each being listed within the script, can be created with this file, typically saved in a file like /home/username/bin/jpg2png
#!/bin/sh for jpg; do # use $jpg in place of each filename given, in turn png=${jpg%.jpg}.png # construct the PNG version of the filename by replacing .jpg with .png printf 'converting "%s" ...n' "$jpg" # output status info to the user running the script if convert "$jpg" jpg.to.png; then # use convert (provided by ImageMagick) to create the PNG in a temp file mv jpg.to.png "$png" # if it worked, rename the temporary PNG image to the correct name else # ...otherwise complain and exit from the script printf >&2 'jpg2png: error: failed output saved in "jpg.to.png".n' exit 1 fi # the end of the "if" test construct done # the end of the "for" loop printf 'all conversions successfuln' # tell the user the good news
The jpg2png
command can then be run on an entire directory full of JPEG images with just /home/username/bin/jpg2png *.jpg
Programming[edit]
Many modern shells also supply various features usually found only in more sophisticated general-purpose programming languages, such as control-flow constructs, variables, comments, arrays, subroutines and so on. With these sorts of features available, it is possible to write reasonably sophisticated applications as shell scripts. However, they are still limited by the fact that most shell languages have little or no support for data typing systems, classes, threading, complex math, and other common full language features, and are also generally much slower than compiled code or interpreted languages written with speed as a performance goal.
The standard Unix tools sed and awk provide extra capabilities for shell programming; Perl can also be embedded in shell scripts as can other scripting languages like Tcl. Perl and Tcl come with graphics toolkits as well.
Typical POSIX scripting languages[edit]
Scripting languages commonly found on UNIX, Linux, and POSIX-compliant operating system installations include:
- KornShell (
ksh
) in several possible versions such as ksh88, Korn Shell ’93 and others. - The Bourne shell (
sh
), one of the oldest shells still common in use - The C shell (
csh
) - GNU Bash (
bash
) tclsh
, a shell which is a main component of the Tcl/Tk programming language.- The wish is a GUI-based Tcl/Tk shell.
The C and Tcl shells have syntax quite similar to that of said programming languages, and the Korn shells and Bash are developments of the Bourne shell, which is based on the ALGOL language with elements of a number of others added as well.[4] On the other hand, the various shells plus tools like awk, sed, grep, and BASIC, Lisp, C and so forth contributed to the Perl programming language.[5]
Other shells that may be available on a machine or for download and/or purchase include:
- Almquist shell (
ash
) - PowerShell (
msh
) - Z shell (
zsh
, a particularly common enhanced KornShell) - The Tenex C Shell (
tcsh
).
Related programs such as shells based on Python, Ruby, C, Java, Perl, Pascal, Rexx etc. in various forms are also widely available. Another somewhat common shell is Old shell (osh
), whose manual page states it «is an enhanced, backward-compatible port of the standard command interpreter from Sixth Edition UNIX.»[6]
So called remote shells such as
- a Remote Shell (
rsh
) - a Secure Shell (
ssh
)
are really just tools to run a more complex shell on a remote system and have no ‘shell’ like characteristics themselves.
Other scripting languages[edit]
Many powerful scripting languages have been introduced for tasks that are too large or complex to be comfortably handled with ordinary shell scripts, but for which the advantages of a script are desirable and the development overhead of a full-blown, compiled programming language would be disadvantageous. The specifics of what separates scripting languages from high-level programming languages is a frequent source of debate, but, generally speaking, a scripting language is one which requires an interpreter.
Life cycle[edit]
Shell scripts often serve as an initial stage in software development, and are often subject to conversion later to a different underlying implementation, most commonly being converted to Perl, Python, or C. The interpreter directive allows the implementation detail to be fully hidden inside the script, rather than being exposed as a filename extension, and provides for seamless reimplementation in different languages with no impact on end users.
While files with the «.sh» file extension are usually a shell script of some kind, most shell scripts do not have any filename extension.[7][8][9][10]
Advantages and disadvantages[edit]
Perhaps the biggest advantage of writing a shell script is that the commands and syntax are exactly the same as those directly entered at the command-line. The programmer does not have to switch to a totally different syntax, as they would if the script were written in a different language, or if a compiled language were used.
Often, writing a shell script is much quicker than writing the equivalent code in other programming languages. The many advantages include easy program or file selection, quick start, and interactive debugging. A shell script can be used to provide a sequencing and decision-making linkage around existing programs, and for moderately sized scripts the absence of a compilation step is an advantage. Interpretive running makes it easy to write debugging code into a script and re-run it to detect and fix bugs. Non-expert users can use scripting to tailor the behavior of programs, and shell scripting provides some limited scope for multiprocessing.
On the other hand, shell scripting is prone to costly errors. Inadvertent typing errors such as rm -rf * /
(instead of the intended rm -rf */
) are folklore in the Unix community; a single extra space converts the command from one that deletes all subdirectories contained in the current directory, to one which deletes everything from the file system’s root directory. Similar problems can transform cp
and mv
into dangerous weapons, and misuse of the >
redirect can delete the contents of a file. This is made more problematic by the fact that many UNIX commands differ in name by only one letter: cp
, cd
, dd
, df
, etc.
Another significant disadvantage is the slow execution speed and the need to launch a new process for almost every shell command executed. When a script’s job can be accomplished by setting up a pipeline in which efficient filter commands perform most of the work, the slowdown is mitigated, but a complex script is typically several orders of magnitude slower than a conventional compiled program that performs an equivalent task.
There are also compatibility problems between different platforms. Larry Wall, creator of Perl, famously wrote that «It is easier to port a shell than a shell script.»[11]
Similarly, more complex scripts can run into the limitations of the shell scripting language itself; the limits make it difficult to write quality code, and extensions by various shells to ameliorate problems with the original shell language can make problems worse.[12]
Many disadvantages of using some script languages are caused by design flaws within the language syntax or implementation, and are not necessarily imposed by the use of a text-based command-line; there are a number of shells which use other shell programming languages or even full-fledged languages like Scsh (which uses Scheme).
Interoperability among scripting languages[edit]
Different scripting languages may share many common elements, largely due to being POSIX based, and some shells offer modes to emulate different shells. This allows a shell script written in one scripting language to be adapted into another.
One example of this is Bash, which offers the same grammar and syntax as the Bourne shell, and which also provides a POSIX-compliant mode.[13] As such, most shell scripts written for the Bourne shell can be run in BASH, but the reverse may not be true since BASH has extensions which are not present in the Bourne shell. As such, these features are known as bashisms.[14]
Shell scripting on other operating systems[edit]
Interoperability software such as Cygwin, the MKS Toolkit, Interix (which is available in the Microsoft Windows Services for UNIX), Hamilton C shell, UWIN (AT&T Unix for Windows) and others allow Unix shell programs to be run on machines running Windows NT and its successors, with some loss of functionality on the MS-DOS-Windows 95 branch, as well as earlier MKS Toolkit versions for OS/2. At least three DCL implementations for Windows type operating systems—in addition to XLNT, a multiple-use scripting language package which is used with the command shell, Windows Script Host and CGI programming—are available for these systems as well. Mac OS X and subsequent are Unix-like as well.[15]
In addition to the aforementioned tools, some POSIX and OS/2 functionality can be used with the corresponding environmental subsystems of the Windows NT operating system series up to Windows 2000 as well. A third, 16-bit subsystem often called the MS-DOS subsystem uses the Command.com provided with these operating systems to run the aforementioned MS-DOS batch files.[16]
The console alternatives 4DOS, 4OS2, FreeDOS, Peter Norton’s NDOS and 4NT / Take Command which add functionality to the Windows NT-style cmd.exe, MS-DOS/Windows 95 batch files (run by Command.com), OS/2’s cmd.exe, and 4NT respectively are similar to the shells that they enhance and are more integrated with the Windows Script Host, which comes with three pre-installed engines, VBScript, JScript, and VBA and to which numerous third-party engines can be added, with Rexx, Perl, Python, Ruby, and Tcl having pre-defined functions in 4NT and related programs. PC DOS is quite similar to MS-DOS, whilst DR DOS is more different. Earlier versions of Windows NT are able to run contemporary versions of 4OS2 by the OS/2 subsystem.
Scripting languages are, by definition, able to be extended; for example, a MS-DOS/Windows 95/98 and Windows NT type systems allows for shell/batch programs to call tools like KiXtart, QBasic, various BASIC, Rexx, Perl, and Python implementations, the Windows Script Host and its installed engines. On Unix and other POSIX-compliant systems, awk and sed are used to extend the string and numeric processing ability of shell scripts. Tcl, Perl, Rexx, and Python have graphics toolkits and can be used to code functions and procedures for shell scripts which pose a speed bottleneck (C, Fortran, assembly language &c are much faster still) and to add functionality not available in the shell language such as sockets and other connectivity functions, heavy-duty text processing, working with numbers if the calling script does not have those abilities, self-writing and self-modifying code, techniques like recursion, direct memory access, various types of sorting and more, which are difficult or impossible in the main script, and so on. Visual Basic for Applications and VBScript can be used to control and communicate with such things as spreadsheets, databases, scriptable programs of all types, telecommunications software, development tools, graphics tools and other software which can be accessed through the Component Object Model.
See also[edit]
- Glue code
- Interpreter directive
- Shebang symbol (#!)
- Unix shells
- PowerShell
- Windows Script Host
References[edit]
- ^ Kernighan, Brian W.; Pike, Rob (1984), «3. Using the Shell», The UNIX Programming Environment, Prentice Hall, Inc., p. 94, ISBN 0-13-937699-2,
The shell is actually a programming language: it has variables, loops, decision-making, and so on.
- ^ a b Johnson, Chris (2009).
[1] Pro Bash Programming: Scripting the Linux Shell, Apress, Retrieved on September 27, 2019. ISBN 9781430219989 - ^ «exec(3p) – POSIX Programmer’s Manual». Retrieved 2020-07-24.
- ^ Unix Shells By Example, pp 7-10,
- ^ Programming Perl, 5th Edition, preface
- ^ «osh — manned.org». manned.org. Retrieved 2019-01-16.
- ^ Robbins, Arnold; Hannah, Elbert; Lamb, Linda (2008). Learning the vi and Vim Editors. p. 205. ISBN 9781449313258.
- ^ Easttom, Chuck (2012). Essential Linux Administration:: A Comprehensive Guide for Beginners. p. 228. ISBN 978-1435459571.
- ^ Kumari, Sinny (November 23, 2015). Linux Shell Scripting Essentials. Packt Publishing Ltd. ISBN 9781783552375. Retrieved May 7, 2017.
Rather than using a file extension for shell scripts, it’s preferred to keep a filename without extension and let an interpreter identify the type by looking into shebang(#!).
- ^ Taylor, Dave; Perry, Brandon (December 16, 2016). Wicked Cool Shell Scripts, 2nd Edition: 101 Scripts for Linux, OS X and UNIX Systems. No Starch Press. ISBN 9781593276027. Retrieved May 7, 2017.
Shell scripts don’t need a special file extension, so leave the extension blank (or you can add the extension .sh if you prefer, but this isn’t required.
- ^ Larry Wall (January 4, 1991). «Finding the last arg». Newsgroup: comp.unix.shell. Retrieved January 5, 2023.
- ^ Christiansen, Tom. «Csh Programming Considered Harmful».
- ^ «Major Differences From The Bourne Shell».
- ^ «24 Bashism To Avoid for POSIX-Compliant Shell Scripts». 18 May 2022.
- ^ MSDN[not specific enough to verify]
- ^ Windows NT 4 Workstation Resource Kit
External links[edit]
- An Introduction To Shell Programming by Greg Goebel
- UNIX / Linux shell scripting tutorial by Steve Parker
- Shell Scripting Primer (Apple)
- What to watch out for when writing portable shell scripts by Peter Seebach
- Free Unix Shell scripting books
- Beginners/BashScripting, Ubuntu Linux
Введение
Набор встроенных команд bash (и его аналогов sh, zsh, etc) совместим с любым POSIX-совместимым приложением в Linux, что позволяет встроить в ваш bash-скрипт любое совместимое приложение. Это дает очень большой набор возможностей в сфере автоматизации рутинных задач администрирования систем Linux, деплоя и сборки приложений, различных пакетных обработок, в том числе аудио и видео.
Командная строка — самый мощный пользовательский интерфейс из существующих на данный момент. Базовый уровень знаний получить достаточно просто. Рекомендуется изучить руководство bash. Это можно сделать, выполнив команду man bash.
Суть bash-скриптов — записать все ваши действия в один файл и выполнять их по необходимости.
В этой статье расскажем про написание bash-скриптов с нуля и получим представление, какую пользу можно из них извлечь. Рекомендуем держать под рукой bash-справочник, если вы планируете заняться этим всерьез.
Развертывание среды
Для выполнения скриптов, которые мы будем учиться писать, нужна среда. Если вы используете на своем компьютере систему Linux, вы можете делать все локально. Если Windows, — можете установить WSL/WSL2. Кроме того, вы можете создать виртуальный сервер и подключиться к нему по SSH. Так вы не навредите своему компьютеру если что-то пойдет не так.
Мы выбрали вариант создать виртуальную машину. Залогинимся в личном кабинете https://my.selectel.ru/, нажав на вкладку «Облачная платформа». Там вы сможете создать виртуальный сервер.
Необходимо выбрать зону размещения сервера исходя из его близости к пользователям. Чем дальше сервер, тем выше пинг.
Нажмем «Создать сервер».
В разделе «Источник» убеждаемся, что выбран образ Ubuntu 20.04.
Конфигурацию можно настроить по своим потребностям.
В разделе «Сеть» стоит выбрать «Подсеть — Плавающий IP-адрес».
В разделе «Доступ» загрузите SSH-ключ и не забудьте сохранить root-пароль. Подробнее об этом рассказано в этой статье
Теперь можно создать сервер кнопкой «Создать» в самом низу.
Будет отображена страница статуса сервера, надо дождаться индикации ACTIVE вверху справа.
Теперь на вкладке «Порты» можно посмотреть IP-адрес, присвоенный серверу.
Не копируйте чужой код
Копирование чужого кода на свой компьютер/сервер опасно. Ранее существовал «патч Бармина», представляющий из себя команду rm -rf /*. Ее очень любили давать новичкам Linux на некоторых конференциях в качестве универсального средства от всех проблем. Суть команды — рекурсивное удаление всех каталогов внутри корневого каталога, т. е. всех системных и пользовательских файлов. Сейчас эта команда не сработает во всех актуальных версиях Linux, но раньше она служила злой шуткой и наказанием тем, кто копировал чужие скрипты на свои серверы и выполнял их. Способов навредить серверу/компьютеру все еще достаточно, но они не столь очевидны.
Выбор редактора
Вам потребуется удобный текстовый редактор. Если вы подключаетесь по SSH, то лучшим выбором будут 3 варианта:
- * vim (если умеете из него выходить)
- * nano (прост, удобен и надежен)
- * mcedit (входит в пакет mc, классический двухпанельный консольный файловый менеджер)
Если вы делаете все локально, выбор полностью на вас. Обычный выбор под Linux — gedit. В этой инструкции мы пользовались nano через SSH на удаленном сервере.
Запуск “Hello, World!”
Первая программа, которую обычно пишут программисты это «Hello, World!» — простой вывод этой строки. Мы тоже с этого начнем. За вывод строки в консоль отвечает команда echo. Прямо в консоли вы можете напечатать echo «Hello, World!» и получить соответствующий вывод:
root@geneviev:~ # echo "Hello, World!"
Hello, World!
Сделаем это программой. Команда touch helloworld.sh создаст файл helloworld.sh. Команда nano helloworld.sh откроет этот файл для редактирования. Заполним файл нашей программой:
#!/bin/bash
echo "Hello, World!"
Для выхода с сохранением из nano надо нажать CTRL + O для сохранения (после чего нажать enter для перезаписи текущего открытого файла), а потом CTRL + X для выхода. Можно выходить без сохранения, при этом он спросит, точно ли вы хотите выйти без сохранения. Если да, надо нажать N для выхода без сохранения. Если вы нажмете Y, он спросит куда сохранить измененный файл, можно нажать enter для перезаписи редактируемого файла.
Разберем, что мы написали.
Первой строкой идет #!/bin/bash — фактически это указание на местоположение интерпретатора. Чтобы при запуске скрипта не требовалось указывать отдельно интерпретатор. Убедиться, что ваш bash интерпретатор лежит по этому пути можно через команду which bash:
root@geneviev:~ # which bash
/usr/bin/bash
Второй строкой идет непосредственно вся наша программа. Как она работает, мы разобрали выше, перейдем к выполнению.
Запустить ваш скрипт/команду можно двумя способами.
Способ №1: bash helloworld.sh. Вы вызываете интерпретатор и в аргументе передаете ему имя файла для исполнения.
root@geneviev:~ # bash helloworld.sh
Hello, World!
Способ №2: Сначала надо разрешить системе исполнять скрипт: chmod +x helloworld.sh. Эта команда сделает файл исполняемым. Теперь вы можете запустить его как любой бинарный файл в linux: ./helloworld.sh.
root@geneviev:~ # ./helloworld.sh
Hello, World!
Первая программа готова, она просто выводит строку в консоль.
Аргументы
Это параметры, которые вы можете передать программе при ее вызове. Например, программа ping принимает в качестве обязательного аргумента IP-адрес или DNS-имя, которое требуется пропинговать: ping selectel.ru. Это простой и удобный способ общения пользователя с программой.
Давайте научим нашу программу принимать аргументы и работать с ними. Доступ к аргументам осуществляется через служебную команду $X где X это число. $0 — всегда имя исполняемого скрипта. $1 — первый аргумент, $2 — второй и так далее. Конечно, если вы планируете передавать пару десятков аргументов вашему приложению, это может быть несколько утомительно, так что вам понадобится что-то вроде этого цикла, чтобы перебрать все поступившие аргументы:
for var in "$@"; do
echo "$var"
done
Подробнее про циклы будет рассказано в следующих разделах.
Пример, создадим новый файл: touch hellousername.sh. Выдаем права на исполнение chmod +x hellousername.sh.
Открываем nano hellousername.sh
Код примера следующий:
#!/bin/bash
echo "Hello, $1!"
Сохраняем, закрываем. Смотрим, что получилось.
root@geneviev:~ # ./hellousername.sh Vasya
Hello, Vasya!
Программа получилась маленькая, но она учит пользоваться (на самом базовом уровне) аргументами, которые мы в нее можем передать. В данном случае аргумент передается один, Vasya, мы сразу его используем, не делая никаких проверок.
root@geneviev:~ # ./hellousername.sh
Hello, !
При таком сценарии в нашей программе баг: она ни с кем не поздоровается. Чтобы исправить ситуацию, есть 2 способа: проверить число аргументов или проверить содержимое аргумента.
Способ №1
#!/bin/bash
if [ "$#" -lt 1 ]; then
echo "Недостаточно аргументов. Пожалуйста, передайте в качестве аргумента имя. Пример: $0 Vasya"
exit 1
fi
echo "Hello, $1!"
Более подробно конструкцию if then [else] fi мы рассмотрим далее, пока не будем на ней останавливаться. Важно понимать, что тут проверяется. $# Это число аргументов без учета имени скрипта, который всегда $0.
Способ №2
#!/bin/bash
if [ -z "$1" ]; then
echo "Имя пустое или не передано. Пожалуйста, передайте в качестве аргумента имя. Пример: $0 Vasya"
exit 1
fi
echo "Hello, $1!"
Здесь тоже используется конструкция if then [else] fi. Ключ -z в if используется для проверки переменной на пустую строку. Есть противоположный ключ -n, он проверяет что строка не пустая. Конечно, этот способ некорректно использовать для проверки входящих аргументов, но в теле самой программы он будет полезен. Например, чтобы проверить что выполненное в самой программе приложение что-то вернуло.
Управляющие конструкции
if-else
Написание программ на любом из языков длиннее нескольких строчек сложно представить без ветвления. В разных языках бывают разные варианты ветвления, но в большинстве случаев используется синтаксис if else. В bash это также присутствует.
Возьмем один из примеров выше.
#!/bin/bash
if [ "$#" -lt 1 ]; then
echo "Недостаточно аргументов. Пожалуйста, передайте в качестве аргумента имя. Пример: $0 Vasya"
exit 1
fi
echo "Hello, $1!"
Происходит проверка системной переменной $# на то, что она меньше, чем (lower than, -lt) 1. Если это выражение истинно, мы переходим в блок команд, открывающийся ключевым словом then. Весь блок, начинающийся с if, должен завершаться ключевым словом fi. Более сложная структура ветвления может выглядеть примерно так:
if TEST-COMMAND1
then
STATEMENTS1
elif TEST-COMMAND2
then
STATEMENTS2
else
STATEMENTS3
fi
Реальный пример:
#!/bin/bash
if [ "$#" -lt 1 ];
then
echo "Недостаточно аргументов. Пожалуйста, передайте в качестве аргумента имя. Пример: $0 Vasya"
exit 1
fi
if [ "$1" = "Vasya" ]; then
echo "Whatsupp, Vasiliy?"
elif [ "$1" = "Masha" ];
then
echo "Hey, Masha"
elif [ "$1" = "Michael" ];
then
echo "Shalom, Michael"
else
echo "Hi, $1"
fi
Вывод программы:
root@geneviev:~ # ./hellousername.sh Vasya
Whatsupp, Vasiliy?
root@geneviev:~ # ./hellousername.sh Masha
Hey, Masha
root@geneviev:~ # ./hellousername.sh Michael
Shalom, Michael
root@geneviev:~ # ./hellousername.sh Andrew
Hi, Andrew
root@geneviev:~ # ./hellousername.sh
Недостаточно аргументов. Пожалуйста, передайте в качестве аргумента имя. Пример: ./hellousername.sh Vasya
Выражение «$1» = «Vasya» проверяет строки на идентичность. Блок после else выполняется только если выше не найден более подходящий блок.
&& и ||
В предыдущей главе вы могли заметить, что я использовал exit 1 для завершения работы программы в случае неуспешной проверки аргумента. Это означает, что программа завершилась с ошибкой. В bash есть операторы && и ||, которые используются для создания цепочек команд. Каждая цепочка зависит от результата выполнения предыдущей программы.
Пример 1: command1 && command2. В этом случае command2 выполнится, только если command1 завершится с кодом 0 (exit 0, по умолчанию).
Пример 2: command1 || command2. В этом случае command2 выполнится, только если command1 завершится с кодом отличным от 0.
Пример 3: command1 && command2 || command3. Если command1 завершится с кодом 0, то будет выполнен command2, иначе command3.
Переменные
Как гласит один из основных принципов программирования — Do Not Repeat Yourself (DRY). Вот и мы не будем повторять себя и перепишем предыдущий пример с использованием переменных, чтобы не вызывать echo каждый раз.
Переменные в bash создаются присваиванием: x=»foo bar» или z=$1. Переменной x мы присвоили строку @foo bar«, а переменной z мы присвоили значение первого аргумента. Использовать именованные переменные гораздо удобнее, чем использовать $1, особенно когда надо использовать его значение во многих местах.
К тому же, аргументы могут идти в разном порядке. Осмысленные названия переменных очень важны, при разрастании программы это снизит неизбежную путаницу. Избегайте имен переменных (и функций) вроде «a», «b», «zzzz», etc.
Чтобы не вызывать echo в каждом варианте с разными строками, разобьем строку на части. Первая часть будет приветствием. Вторая — именем. Третья — завершающим знаком препинания. Его можно не выносить в переменную.
#!/bin/bash
greetString="Hello"
nameString="stranger"
if [ "$#" -lt 1 ];
then
echo "Недостаточно аргументов. Пожалуйста, передайте в качестве аргумента имя. Пример: $0 Vasya"
exit 1
fi
if [ "$1" = "Vasya" ];
then
nameString="Vasiliy"
greetString="Whatsup"
elif [ "$1" = "Masha" ];
then
nameString="Maria"
elif [ "$1" = "Michael" ];
then
greetString="Shalom"
nameString="Michael"
fi
echo "$greetString, $nameString!"
В этом примере мы создаем переменные greetString и nameString, которым присваиваем значения по умолчанию. В конце программа выводит значения этих двух переменных с помощью echo и форматированной строки (в двойных кавычках переменные раскрываются). Между этими действиями программа определяет, надо ли присваивать переменным другие значения.
Switch case
Использование if-else конструкции в нашем примере не является оптимальным вариантом. Мы всего лишь сверяем значение переменной с определенным набором значений. В такой ситуации лучшим выбором будет switch-case-конструкция.
case "$variable" in
"$condition1" )
command...
;;
"$condition2" )
command...
;;
esac
Перепишем нашу программу приветствий с использованием switch-case:
#!/bin/bash
name=$1
case "$name" in
"Vasya" )
nameString="Vasiliy"
greetString="Whatsup"
;;
"Masha" )
greetString="Hey"
nameString="Maria"
;;
* )
greetString="Hello"
nameString="stranger"
;;
esac
echo "$greetString, $nameString!"
Циклы
Как и любой полноценный язык программирования, bash поддерживает циклы. Цикл for и цикл while. Циклы нужны, чтобы выполнять какой-то код заданное число раз. Например, при парсинге CSV перебирать построчно и каждую строку рассматривать отдельно.
Цикл for
Вот пример структуры цикла for:
for var in list
do
команды
done
Простой реальный пример:
#!/bin/bash
for name in Maria Vasya Michael stranger
do
echo "Hello, $name!"
done
Вывод:
root@geneviev:~ # ./cycle.sh
Hello, Maria!
Hello, Vasya!
Hello, Michael!
Hello, stranger!
Программа просто перебирает все имена, разделенные пробелом, и выводит их с помощью echo.
Попробуем немного усложнить пример:
#!/bin/bash
file=$1
for name in $(cat $file)
do
echo "Hello, $name!"
done
Создадим файл с именами touch names и запишем в него список имен для приветствия:
Maria
Vasiliy
Ivan
Nikolai
Innokentiy
Вывод:
root@geneviev:~ # ./cycle.sh
^C
root@geneviev:~ # ./cycle.sh names
Hello, Maria!
Hello, Vasiliy!
Hello, Ivan!
Hello, Nikolai!
Hello, Innokentiy!
Обратите внимание на ^C. Это символ прерывания выполнения программы. В нашем случае мы вызвали программу без аргумента, и она вошла в вечный цикл. Можно сказать, зависла. Пришлось завершить ее принудительно. Не забывайте делать проверки входных данных в реальных программах. Как это делать, можете посмотреть в главах if-else и switch case, например.
В нашей программе есть небольшой баг. Модифицируем файл имен:
Erich Maria Remarque
Vasiliy
Ivan
Nikolai
Innokentiy
Запустим программу, получим вывод:
root@geneviev:~ # ./cycle.sh names
Hello, Erich!
Hello, Maria!
Hello, Remarque!
Hello, Vasiliy!
Hello, Ivan!
Hello, Nikolai!
Hello, Innokentiy!
Как говорится, «Кто все эти люди?». Так получается, потому что у нас не задана переменная окружения IFS (Internal Field Separator), указывающая на разделители полей. Наш цикл использует пробелы и переносы строки как разделители. В начале скрипта (после #!/bin/bash) укажите использовать перенос строки как разделитель полей: IFS=$’n’.
root@geneviev:~ # ./cycle.sh names
Hello, Erich Maria Remarque!
Hello, Vasiliy!
Hello, Ivan!
Hello, Nikolai!
Hello, Innokentiy!
В итоге мы получим возможность работать со строками целиком. Это пригодится для парсинга CSV.
Обычно цикл for используется со счетчиком. В C-like стиле. Что-то вроде for (i=0;i<10;i++){}. В bash тоже так можно.
#!/bin/bash
for (( i=1; i <= 10; i++ ))
do
echo "number is $i"
done
Вывод:
root@geneviev:~ # ./cycle.sh
number is 1
number is 2
number is 3
number is 4
number is 5
number is 6
number is 7
number is 8
number is 9
number is 10
Цикл while
Схема организации цикла while:
while команда проверки условия
do
другие команды
done
Простой способ сделать бесконечную петлю (бесконечный цикл):
while true
do
echo "this is infinity loop"
done
Это может пригодится, например, когда вам нужно вызывать что-то чаще, чем позволяет cron (например, раз в минуту). Или когда вам просто надо проверять какое-то значение постоянно. Областей применения у бесконечных циклов много.
Здесь используются те же самые выражения, что и в if:
#!/bin/bash
count=0
while [ $count -lt 10 ]
do
(( count++ ))
echo "count: $count"
done
Вывод:
root@geneviev:~ # ./cycle.sh
count: 1
count: 2
count: 3
count: 4
count: 5
count: 6
count: 7
count: 8
count: 9
count: 10
Из цикла можно выйти с помощью команды break (работает также и для for):
#!/bin/bash
count=0
while [ $count -lt 10 ]
do
(( count++ ))
echo "count: $count"
if [ "$count" -gt 5 ]
then
break
fi
done
Вывод:
root@geneviev:~ # ./cycle.sh
count: 1
count: 2
count: 3
count: 4
count: 5
count: 6
Заключение
Несмотря на огромную конкуренцию в сфере автоматизации рутины со стороны python, ruby, perl bash не сдает позиции. Он прост в освоении и использовании, гибок и так или иначе присутствует в абсолютном большинстве дистрибутивов Linux.
В этой статье были приведены только основы написания программ на bash. Надеемся, вы нашли их полезными для себя.
Бесплатная книга-сайт на русском, полный гайд
Advanced Bash-Scripting Guide
Введение
BASH — Bourne-Again SHell (что может переводится как «перерожденный шел», или «Снова шел Борна(создатель sh)»), самый популярный командный интерпретатор в юниксоподобных системах, в особенности в GNU/Linux. Ниже приведу ряд встроенных команд, которые мы будем использовать для создания своих скриптов.
break
выход из цикла for, while или until
continue
выполнение следующей итерации цикла for, while или until
echo
вывод аргументов, разделенных пробелами, на стандартное устройство вывода
exit
выход из оболочки
export
отмечает аргументы как переменные для передачи в дочерние процессы в среде
hash
запоминает полные имена путей команд, указанных в качестве аргументов, чтобы не искать их при следующем обращении
kill
посылает сигнал завершения процессу
pwd
выводит текущий рабочий каталог
read
читает строку из ввода оболочки и использует ее для присвоения значений указанным переменным.
return
заставляет функцию оболочки выйти с указанным значением
shift
перемещает позиционные параметры налево
test
вычисляет условное выражение
times
выводит имя пользователя и системное время, использованное оболочкой и ее потомками
trap
указывает команды, которые должны выполняться при получении оболочкой сигнала
unset
вызывает уничтожение переменных оболочки
wait
ждет выхода из дочернего процесса и сообщает выходное состояние.
И конечно же кроме встроенных команд мы будем использовать целую кучу внешних, отдельных команд-программ, с которыми мы познакомимся уже в процессе
Что необходимо знать с самого начала
- Любой bash-скрипт должен начинаться со строки:
#!/bin/bash
в этой строке после #! указывается путь к bash-интерпретатору, поэтому если он у вас установлен в другом месте(где, вы можете узнать набрав whereis bash
) поменяйте её на ваш путь.
- Коментарии начинаются с символа # (кроме первой строки).
- В bash переменные не имеют типа(о них речь пойдет ниже)
Переменные и параметры скрипта
Приведу как пример небольшой пример, который мы разберем:
#!/bin/bash
#указываем где у нас хранится bash-интерпретатор
#присваиваем переменной parametr1 значение первого параметра скрипта
parametr1=$1
#присваиваем переменной script_name значение имени скрипта
script_name=$0
# команда echo выводит определенную строку, обращение к переменным осуществляется через $имя_переменной.
echo "Вы запустили скрипт с именем $script_name и параметром $parametr1"
# здесь мы видим другие кавычки, разница в том, что в одинарных кавычках не происходит подстановки переменных.
echo 'Вы запустили скрипт с именем $script_name и параметром $parametr1'
#Выход с кодом 0 (удачное завершение работы скрипта)
exit 0
Результат выполнения скрипта:
ite@ite-desktop:~$ ./test.sh qwerty
Вы запустили скрипт с именем ./test.sh и параметром qwerty
Вы запустили скрипт с именем $script_name и параметром $parametr1
После того как мы познакомились как использовать переменные и передавать скрипту параметры, время познакомиться с зарезервированными переменными:
$DIRSTACK
— содержимое вершины стека каталогов
$EDITOR
— текстовый редактор по умолчанию
$EUID
— Эффективный UID. Если вы использовали программу su для выполнения команд от другого пользователя, то эта переменная содержит UID этого пользователя, в то время как…
$UID
— …содержит реальный идентификатор, который устанавливается только при логине.
$FUNCNAME
— имя текущей функции в скрипте.
$GROUPS
— массив групп к которым принадлежит текущий пользователь
$HOME
— домашний каталог пользователя
$HOSTNAME
— ваш hostname
$HOSTTYPE
— архитектура машины.
$LC_CTYPE
— внутренняя переменная, котороя определяет кодировку символов
$OLDPWD
— прежний рабочий каталог
$OSTYPE
— тип ОС
$PATH
— путь поиска программ
$PPID
— идентификатор родительского процесса
$SECONDS
— время работы скрипта(в сек.)
$#
— общее количество параметров переданных скрипту
$*
— все аргументы переданыне скрипту(выводятся в строку)
$@
— тоже самое, что и предыдущий, но параметры выводятся в столбик
$!
— PID последнего запущенного в фоне процесса
$$
— PID самого скрипта
Условия
Условные операторы, думаю, знакомы практически каждому, кто хоть раз пытался на чем-то писать программы. В bash условия пишутся след. образом (как обычно на примере):
#!/bin/bash
#в переменную source засовываем первый параметр скрипта
source=$1
#в переменную dest засовываем второй параметр скрипта
dest=$2
# в ковычках указываем имена переменных для сравнения. -eq — логическое сравнение обозначающие «равны»
if [[ "$source" -eq "$dest" ]]
# если они действительно равны, то
then
#выводим сообщение об ошибке, т.к. $source и $dest у нас равны
echo "Применик $dest и источник $source один и тот же файл!"
# выходим с ошибкой (1 — код ошибки)
exit 1
# если же они не равны
else
# то выполняем команду cp: копируем источник в приемник
cp $source $dest
echo "Удачное копирование!"
fi
#обозначаем окончание условия.
Результат выполнения скрипта:
ite@ite-desktop:~$ ./primer2.sh 1 1
Применик 1 и источник 1 один и тот же файл!
ite@ite-desktop:~$ ./primer2.sh 1 2
Удачное копирование!
Структура if-then-else
используется следующим образом:
if
<команда или набор команд возвращающих код возврата(0 или 1)>
then
<если выражение после if истино, то выполняется этот блок>
else
<если выражение после if ложно, тот этот>
В качестве команд возвращающих код возврата могут выступать структуры [[ , [ , test, (( ))
или любая другая(или несколько) linux-команда.
test
— используется для логического сравнения. после выражения, неоьбходима закрывающая скобка «]»
[
— синоним команды test
[[
— расширенная версия «[» (начиная с версии 2.02)(как в примере), внутри которой могут быть использованы || (или), & (и). Долна иметь закрывающуб скобку «]]»
(( ))
— математическое сравнение.
для построения многоярусных условий вида:
if ...
then ....
else
if ....
then....
else ....
для краткости и читаемости кода, можно использовать структуру:
if ..
then ...
elif ...
then ...
elif ...
Условия. Множественный выбор
Если необходимо сравнивать какоую-то одну переменную с большим количеством параметров, то целесообразней использовать оператор case.
#!/bin/bash
echo "Выберите редатор для запуска:"
echo "1 Запуск программы nano"
echo "2 Запуск программы vi"
echo "3 Запуск программы emacs"
echo "4 Выход"
#здесь мы читаем в переменную $doing со стандартного ввода
read doing
case $doing in
1)
/usr/bin/nano
# если $doing содержит 1, то запустить nano
;;
2)
/usr/bin/vi
# если $doing содержит 2, то запустить vi
;;
3)
/usr/bin/emacs
# если $doing содержит 3, то запустить emacs
;;
4)
exit 0
;;
*)
#если введено с клавиатуры то, что в case не описывается, выполнять следующее:
echo "Введено неправильное действие"
esac
#окончание оператора case.
Результат работы:
ite@ite-desktop:~$ ./menu2.sh
Выберите редатор для запуска:
1 Запуск программы nano
2 Запуск программы vi
3 Запуск программы emacs
4 Выход
После выбор цифры и нажатия Enter запуститься тот редактор, который вы выбрали(если конечно все пути указаны правильно, и у вас установлены эти редакторы )
Прведу список логических операторв, которые используются для конструкции if-then-else-fi:
-z
# строка пуста
-n
# строка не пуста
=, (==)
# строки равны
!=
# строки неравны
-eq
# равно
-ne
# неравно
-lt,(< )
# меньше
-le,(<=)
# меньше или равно
-gt,(>)
#больше
-ge,(>=)
#больше или равно
!
#отрицание логического выражения
-a,(&&)
#логическое «И»
-o,(||)
# логическое «ИЛИ»
С основами языка и условиями мы разобрались, чтобы не перегружать статью, разобью её на несколько частей(допустим на 3). Во второй части разберем операторы цикла и выполнение математических операций.
Основы BASH. Часть 2
Циклы. Цикл for-in.
Оператор for-in
предназначен для поочередного обращения к значениям перечисленным в списке. Каждое значение поочередно в списке присваивается переменной.
Синтаксис следующий:
for переменная in список_значений
do
команды
done
Рассмотрим небольшой пример:
#!/bin/bash
for i in 0 1 2 3 4 #переменной $i будем поочередно присваивать значения от 0 до 4 включительно
do
echo "Console number is $i" >> /dev/pts/$i #Пишем в файл /dev/pts/$i(файл виртуального терминала) строку "Console number is $i"
done #цикл окончен
exit 0
После выполнения примера в первых 5 виртуальных консолях(терминалах) появится строка с её номером. В переменную $i
поочередно подставляются значения из списка и в цикле идет работа со значением этой переменной
Циклы. Цикл while.
Цикл while
сложнее цикла for-in
и используется для повторения команд, пока какое-то выражение истинно( код возврата = 0).
Синтаксис оператора следующий:
while выражение или команда возвращающая код возврата
do
команды
done
Пример работы цикла рассмотрим на следующем примере:
#!/bin/bash
again=yes #присваиваем значение "yes" переменной again
while [ "$again" = "yes" ] #Будем выполнять цикл, пока $again будет равно "yes"
do
echo "Please enter a name:"
read name
echo "The name you entered is $name"
echo "Do you wish to continue?"
read again
done
echo "Bye-Bye"
А теперь результат работы скрипта:
ite@ite-desktop:~$ ./bash2_primer1.sh
Please enter a name:
ite
The name you entered is ite
Do you wish to continue?
yes
Please enter a name:
mihail
The name you entered is mihail
Do you wish to continue?
no
Bye-Bye
Как видим цикл выполняется до тех пор, пока мы не введем что-то отличное от «yes». Между do
и done
можно описывать любые структуры, операторы и т.п., все они будут выполнятся в цикле.Но следует быть осторожным с этим циклом, если вы запустите на выполнение в нём какую-либо команду, без изменения переменной выражения, вы можете попасть в бесконечный цикл.
Теперь об условии истинности. После while
, как и в условном операторе if-then-else
можно вставлять любое выражение или команду, которая возвращает код возврата, и цикл будет исполнятся до тех пор, пока код возврата = 0! Оператор [
аналог команды test
, которая проверяет истинность условия, которое ей передали.
Рассмотрим еще один пример, я взял его из книги Advanced Bash Scripting. Уж очень он мне понравился :), но я его немного упростил. В этом примере мы познакомимся с еще одним типом циклов UNTIL-DO. Эта практически полный аналог цикла WHILE-DO, только выполняется пока какое-то выражение ложно.
Вот пример:
#!/bin/bash
echo "Введите числитель: "
read dividend
echo "Введите знаменатель: "
read divisor
dnd=$dividend #мы будем изменять переменные dividend и divisor,
#сохраним их знания в других переменных, т.к. они нам
#понадобятся
dvs=$divisor
remainder=1
until [ "$remainder" -eq 0 ]
do
let "remainder = dividend % divisor"
dividend=$divisor
divisor=$remainder
done
echo "НОД чисел $dnd и $dvs = $dividend"
Результат выполнения скрипта:
ite@ite-desktop:~$ ./bash2_primer3.sh
Введите числитель:
100
Введите знаменатель:
90
НОД чисел 100 и 90 = 10
Математические операции
Команда let
.
Команда let
производит арифметические операции над числами и переменными.
Рассмотрим небольшой пример, в котором мы производим некоторые вычисления над введенными числами:
#!/bin/bash
echo "Введите a: "
read a
echo "Введите b: "
read b
let "c = a + b" #сложение
echo "a+b= $c"
let "c = a / b" #деление
echo "a/b= $c"
let "c <<= 2" #сдвигает c на 2 разряда влево
echo "c после сдвига на 2 разряда: $c"
let "c = a % b" # находит остаток от деления a на b
echo "$a / $b. остаток: $c "
Результат выполнения:
ite@ite-desktop:~$ ./bash2_primer2.sh
Введите a:
123
Введите b:
12
a+b= 135
a/b= 10
c после сдвига на 2 разряда: 40
123 / 12. остаток: 3
Ну вот, как видите ничего сложного, список математических операций стандартный:
+
— сложение
—
— вычитание
*
— умножение
/
— деление
**
— возведение в степень
%
— модуль(деление по модулю), остаток от деления
let
позволяет использовать сокращения арифметических команд, тем самым сокращая кол-во используемых переменных. Например:a = a+b
эквивалентноa +=b
и т.д
Работа с внешними программами при написании shell-скриптов
Для начала немного полезной теории.
Перенаправление потоков.
В bash (как и многих других оболочках) есть встроенные файловые дескрипторы: 0 (stdin)
, 1 (stdout)
, 2 (stderr)
.
stdout
— Стандартный вывод. Сюда попадает все что выводят программы
stdin
— Стандартный ввод. Это все что набирает юзер в консоли
stderr
— Стандартный вывод ошибок.
Для операций с этими дескрипторами, существуют специальные символы: >
(перенаправление вывода), <
(перенаправление ввода). Оперировать ими не сложно. Например:
cat /dev/random > /dev/null #перенаправить вывод команды cat /dev/random в /dev/null (абсолютно бесполезная операция :)) )
или
ls -la > listing #записать в файл listing содержание текущего каталога (уже полезней)
Если есть необходимость дописывать в файл(при использовании «>
» он заменятеся), необходимо вместо «>
» использовать «>>
«
sudo < my_password
после просьбы sudo ввести пароль, он возьмется из файла my_password, как будто вы его ввели с клавиатуры.
Если необходимо записать в файл только ошибки, которые могли возникнуть при работе программы, то можно использовать:
./program_with_error 2> error_file
цифра 2 перед «>
» означает что нужно перенаправлять все что попадет в дескриптор 2(stderr).
Если необходимо заставить stderr
писать в stdout
, то это можно след. образом:
./program_with_error 2>&1
символ «&
» означает указатель на дескриптор 1(stdout)
(Поумолчанию stderr
пишет на ту консоль, в котрой работает пользователь(вренее пишет на дисплей)).
2. Конвееры.
Конвеер — очень мощный инструмент для работы с консолью Bash. Синтаксис простой:
команда1 | команда 2
— означает, что вывод команды 1 передастся на ввод команде 2
Конвееры можно группировать в цепочки и выводить с помощью перенаправления в файл, например:
ls -la | grep «hash» |sort > sortilg_list
вывод команды ls -la
передается команде grep
, которая отбирает все строки, в которых встретится слово hash, и передает команде сортировке sort
, которая пишет результат в файл sorting_list. Все довольно понятно и просто.
Чаще всего скрипты на Bash используются в качестве автоматизации каких-то рутинных операций в консоли, отсюда иногда возникает необходимость в обработке stdout
одной команды и передача на stdin
другой команде, при этом результат выполнения одной команды должен быть неким образом обработан. В этом разделе я постораюсь объяснить основные принципы работы с внешними командами внутри скрипта. Думаю что примеров я привел достаточно и можно теперь писать только основные моменты.
1. Передача вывода в переменную.
Для того чтобы записать в переменную вывод какой-либо команды, достаточно заключить команду в ``
ковычки, например
a = ` echo «qwerty» `
echo $a
Результат работы: qwerty
Однако если вы захотите записать в переменную список директорий, то необходимо, должным образом обработать результат для помещения данных в переменную. Рассмотрим небольшой, пример:
LIST=`find /svn/ -type d 2>/dev/null| awk '{FS="/"} {print $4}'| sort|uniq | tr 'n' ' '`
for ONE_OF_LIST in $LIST
do
svnadmin hotcopy /svn/$ONE_OF_LIST /svn/temp4backup/$ONE_OF_LIST
done
Здесь мы используем цикл for-do-done
для архивирование всех директорий в папке /svn/ с помощью команды svnadmin hotcopy
(что в нашем случае не имеет никого значения, просто как пример). Наибольшй интерес вызывает строка: LIST=find /svn/ -type d 2>/dev/null| awk '{FS="/"} {print $4}'| sort|uniq | tr 'n' ' '
В ней переменной LIST присваивается выполнение команды find, обработанной командами awk, sort, uniq,tr(все эти команды мы рассматривать не будем, ибо это отдельная статья). В переменной LIST будут имена всех каталогов в папке /svn/ пгомещенных в одну строку(для того чтобы её стравить циклу.