User Tools

Site Tools


unix_101

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
unix_101 [2016/05/25 13:44]
peek [Combining Commands On A Single Line]
unix_101 [2016/05/25 15:24] (current)
peek
Line 392: Line 392:
  
 **CAUTION: There is NO undelete command for unix.  Once a file or directory has been deleted THERE IS NO WAY to bring that file or directory back.  The only way to recover a lost file or directory is to restore it from backup.** **CAUTION: There is NO undelete command for unix.  Once a file or directory has been deleted THERE IS NO WAY to bring that file or directory back.  The only way to recover a lost file or directory is to restore it from backup.**
- 
-====== Dotfiles ====== 
- 
-A mention should be made to the existence of dotfiles, or files/directories whose names begin with a '.' If you take a close look at the output of the ls command earlier in this document, you will notice that some invocations of ls listed these files and some did not.  In particular, the command ''ls'' by itself did not list dotfiles.  This is because dotfiles are "hidden files" under Unix.  They are shown if you pass special command line arguments to ls, but otherwise ls will not show them to you.  Dotfiles are typically used to hold configuration information used by the shell and other programs to store your user-specific preferences and data. 
- 
-Dotfiles are almost always plain text files.  This means that one way to change the behavior of a program is to edit the dotfiles that the program uses, and you can do this with any editor you choose.  A word of warning however: incorrect content in a dotfile may result in unexpected behavior.  You should always make a backup of the file you are about to change so that, if needed, you can restore the original version should something go terribly wrong. 
- 
-The easiest way to find out what dotfiles are used by a program is to check the program's manual, help, or info pages. 
- 
-====== Parents, Children, Orphans, and Zombies ====== 
- 
-It is often the case that a program will execute another program to either replace itself or to run along side itself.  In fact, this is so common, that it's the first thing that happens on a UNIX machine on bootup.  The very first program run by the system is usually called ''init'', and it takes care of starting up all of the other programs that make your system run. 
- 
-  * Every program has an associated process identification number, called PID.  You can see a list of all of the processes running on a computer with the ''ps -ef'' command.  The ''ps'' command shows you the **process table** -- a special table kept by the kernel to keep up with all of the processes currently running on the computer.  If you only want to see the processes running in your shell, just type ''ps'' without the ''-ef'' command line arguments. 
-  * When a program executes another program, the executor is called the **parent**, and the executed the **child**.  The parent process owns the child process, and the child process inherits it's execution environment from it's parent.  (Don't worry if you don't know what "execution environment" means.) 
-  * The ''init'' program is the great ancestor -- every program has ''init'' somewhere in it's linage.  If a parent program dies, the child becomes owned by ''init''. 
-  * If a program exits, but it still has an entry in the process table, it's called a **zombie**.  This will have almost no effect on you or your UNIX experience.  Ever.  I just wanted a reason to bring zombies into the conversation. 
- 
-====== Managing Running Commands ====== 
- 
-When you run a command, that command takes over the terminal.  It expects to read input from the keyboard and write output to the screen.  In the meantime, your shell is sitting there waiting for the command to exit.  But how do you regain control if a program goes haywire?  Or what if your command is just taking a very long time to complete, and you want to get something else done while you wait? 
- 
-  * The behavior where a command takes over the keyboard and screen is called "running a command in the **foreground**" This is the default behavior of commands unless told otherwise. 
-  * If you want to interrupt a command, you can hold down the control key and press the "C" key (''control-c'').  This will kill the command.  (NOTE: If the command is held up by some kernel function, like performing I/O for instance, then the program cannot quit until the kernel first returns control to the program so that it can.) 
-  * If you expect a program to take a long time, then you can run it as a **background** process.  Background processes should not expect keyboard input.  They may be fed input from a file (see the section on redirection and pipes below).  A process can be run in the background by appending a space and a ''&'' symbol at the end of the command line. 
-  * If you are running a program in the foreground, but it's taking too long, and you want to switch it to the background, you can do so by holding down the control key and pressing "Z" (''ontrol-Z'').  This will interrupt the program without killing it.  You can then type the command ''bg'' to tell the shell to resume running the program in the background.  If you type control-Z and decide that you made a mistake, you can continue running the program in the foreground by typing ''fg''. 
-  * If you have one or more programs running in the background, you can view a list of background processes by typing ''jobs''. 
- 
-For example, I'm going to use the ''sleep'' command to simulate a long-running process: 
-<code> 
-peek@catus:~$ # I can type something here and the shell will ignore it because 
-peek@catus:~$ # the line begins with a '#' In shell scripting, a line beginning 
-peek@catus:~$ # with a '#' is called a comment line. 
-peek@catus:~$ # This lets me tell you what I'm doing without confusing the shell 
-peek@catus:~$ # First, I'm going to start a program that's going to take a long time: 
-peek@catus:~$ sleep 300 
-^Z 
-[1]+  Stopped                 sleep 300 
-peek@catus:~$ # I just pressed control-Z to interrupt the sleep command. 
-peek@catus:~$ # The sleep command just sits and does nothing for the number of  
-peek@catus:~$ # seconds I tell it to. 
-peek@catus:~$ # In this case, the sleep command is going to sit there for 5 minutes 
-peek@catus:~$ # (300 seconds). I didn't want to wait 5 minutes, so I'm going to move 
-peek@catus:~$ # the command into the background with the 'bg' command 
-peek@catus:~$ bg 
-[1]+ sleep 300 & 
-peek@catus:~$ # Now the sleep command is running again, but it's running in the  
-peek@catus:~$ # background, which frees up the shell for me to run other commands. 
-peek@catus:~$ # Now I'll run another one: 
-peek@catus:~$ sleep 600 
-^Z 
-[2]+  Stopped                 sleep 600 
-peek@catus:~$ # This command will run for 10 minutes (600 seconds). 
-peek@catus:~$ # I can see a list of background processes with 'jobs' 
-peek@catus:~$ jobs 
-[1]-  Running                 sleep 300 & 
-[2]+  Stopped                 sleep 600 
-peek@catus:~$ # Notice that the 'sleep 300' command is "running", but the 'sleep 600' 
-peek@catus:~$ # command is "stopped" I'll start the 'sleep 600' command running again: 
-peek@catus:~$ bg 
-[2]+ sleep 600 & 
-peek@catus:~$ # Now I can do other things. 
-peek@catus:~$ # But what if I want to connect with one of my background processes? 
-peek@catus:~$ # I can bring a background process to the foreground with the 'fg' command. 
-peek@catus:~$ # Using 'fg' by itself will bring to the foreground the last command 
-peek@catus:~$ # I put in the background. 
-peek@catus:~$ # If I want to bring some other process to the foreground, then I have to 
-peek@catus:~$ # use the numerical identifier displayed on the left in square brackets. 
-[1]-  Done                    sleep 300 
-peek@catus:~$ # Ah!  Now I see that the 'sleep 300' command has finished! 
-peek@catus:~$ # Well, now there's no point in bringing it to the foreground -- it' 
-peek@catus:~$ # done and gone. 
-peek@catus:~$ # I'll create another 10-minute sleep... 
-peek@catus:~$ sleep 600 & 
-[3] 15157 
-peek@catus:~$ # I started this command of in the background right away.  Notice that 
-peek@catus:~$ # it was given a new, unique job number, 3, even though there are now only 
-peek@catus:~$ # two processes running in the background. 
-peek@catus:~$ jobs 
-[2]-  Running                 sleep 600 & 
-[3]+  Running                 sleep 600 & 
-peek@catus:~$ # I'll bring the first process to the foreground. 
-peek@catus:~$ fg 2 
-sleep 600 
-^Z 
-[2]+  Stopped                 sleep 600 
-peek@catus:~$ # There.  I did it.  But then I got bored again, so I put it back in 
-peek@catus:~$ # the background.  When these processes end, they will tell you so with  
-peek@catus:~$ # a "Done", just like the 'sleep 300' above.  However, even after the  
-peek@catus:~$ # program exits, you won't see the "Done" line until you press RETURN  
-peek@catus:~$ # or enter another command. 
-peek@catus:~$ # Oh yeah, the job number is local the shell.  It's not the same thing 
-peek@catus:~$ # as the process identification number (PID).  The PID is something the 
-peek@catus:~$ # kernel uses.  The difference is like "I'm in apartment B" versus 
-peek@catus:~$ # "I live at 1234 Winston Way Apartments" That is, if you view a shell 
-peek@catus:~$ # as an apartment building, processes as residents, and the kernel as 
-peek@catus:~$ # the city the apartment building is in.  But maybe that's just further 
-peek@catus:~$ # confounding an already confusing concept...? 
-[3]-  Done                    sleep 600 
-peek@catus:~$ # Ah, well done.  I see that in the amount of time that it took for me 
-peek@catus:~$ # to type, the last sleep command finished. 
-peek@catus:~$ # Wait a second, wasn't there another sleep command that should have 
-peek@catus:~$ # finished before that one? 
-peek@catus:~$ jobs 
-[2]+  Stopped                 sleep 600 
-peek@catus:~$ bg 
-[2]+ sleep 600 & 
-peek@catus:~$ # Did you catch that mistake I made?  I said earlier that I had put job 
-peek@catus:~$ # number 2 back into the background, but I forgot to actually type 'bg'. 
-peek@catus:~$ # I've just wasted valuable time that I could have been using for  
-peek@catus:~$ # something else. 
-[2]+  Done                    sleep 600 
-peek@catus:~$ 
-</code> 
- 
-====== Input, Output, and Redirection ====== 
- 
-In computer programming, standard streams are input and output communications channels between a computer program and it's environment.  These streams are preconnected when the program begins it's execution.  There are three standard I/O channels that are available to every program: **standard input** (**stdin**), **standard output** (**stdout**), and **standard error** (**stderr**). 
- 
-Unless told otherwise, the operating system will assume that a program's stdin comes from the keyboard, and a program's stdout and stderr will go to the screen.  However, it's often useful to redirect input and output, or to connect the output of one program to the input of another.  This is called **redirection**. 
- 
-^ Command Line Argument ^ Redirection Type ^ 
-^ <code>></code> | Standard output redirection -- Output from the program is sent to the given file, pipe, or specified destination. <code> 
-$ ls -ald /etc/passwd > /tmp/ls-output.txt 
-$ cat /tmp/ls-output.txt 
--rw-r--r-- 1 root root 2803 May 10 16:40 /etc/passwd 
-</code> | 
-^ <code><</code> | Standard input redirection -- Input to the program is read from the given file, pipe, or specified source. <code> 
-$ cat < /etc/passwd 
-root:x:0:0:root:/root:/bin/bash 
-daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin 
-bin:x:2:2:bin:/bin:/usr/sbin/nologin 
-sys:x:3:3:sys:/dev:/usr/sbin/nologin 
-[**> The rest of this output removed for brevity <**] 
-</code> | 
-^ <code>2></code> | Standard error redirection -- Output written to standard error is instead written to the given file, pipe, or specified destination. <code> 
-$ ls -ald /this-file-does-not-exist 2> /tmp/ls-output.txt 
-$ cat /tmp/ls-output.txt 
-ls: cannot access '/this-file-does-not-exist': No such file or directory 
-</code> | 
-^ <code>|</code> | Pipe -- This is used to shuttle output from one command to another command's input. <code> 
-$ cat /etc/passwd | wc -c 
-2803 
-$ cat /etc/passwd | wc -l 
-51 
-$ cat /etc/passwd | wc --max-line-length 
-87 
-</code> (This shows that my ''/etc/passwd'' file contains 2,803 bytes, and 51 lines.  The longest line is 87 characters.  These command line arguments and more can be found in the ''wc'' man page, or by typing ''wc --help'' (two dashes before "help").) | 
-^ <code>2>&1</code> | Standard error redirection -- Stderr is written to wherever stdout goes.  For example, if writing output to a file, then this: <code><command> > logfile.txt 2> logfile.txt</code> Is functionally equivalent to this: <code><command> > logfile.txt 2>&1</code> | 
-^ <code>>&2</code> | Standard output redirection -- Stdout is written to wherever stderr goes. | 
- 
-====== Combining Commands and Subshells ====== 
- 
-===== Combining Commands On A Single Line ===== 
- 
-Commands can be combined on the same line by separating each command with a semicolon, like so: 
- 
-^ This ^ = ^ This ^ 
-| <code>ls -1 
-cd ~/Desktop 
-df . 
-</code> | | <code>ls -1 ; cd ~/Desktop ; df .</code> | 
- 
-===== Splitting (A) Command(s) Across Multiple Lines ===== 
- 
-Just like it's possible to combine commands, it's also possible to split commands.  Any line that ends with a ''\'' character is taken by the shell to mean that the command is incomplete, and that there will be more coming on the next line.  Usually you wouldn't do this for commands that you type yourself, but it's handy to use when writing shell scripts as it makes your script easier to read and understand.  For an example, see the command substitution section below. 
-===== Executing Commands In A Subshell ===== 
- 
-Commands can also be run in a subshell.  This means that the shell runs a copy of itself, and the copy executes your command.  Why would you want to do this?  Well, here's an example.  Say you want to time how long it takes to log into a set of remote machines with ssh and run a command: 
- 
-<code>peek@catus:~$ time ssh peek@alces01 "uptime" ; time ssh peek@alces02 "uptime" ; time ssh peek@alces03 "uptime" 
- 09:09:03 up 4 days, 18:11,  0 users,  load average: 0.06, 0.07, 0.06 
- 
-real 0m0.591s 
-user 0m0.020s 
-sys 0m0.000s 
- 09:09:04 up 4 days, 18:31,  0 users,  load average: 0.01, 0.02, 0.05 
- 
-real 0m0.667s 
-user 0m0.020s 
-sys 0m0.000s 
- 09:09:04 up 4 days, 19:19,  0 users,  load average: 0.00, 0.01, 0.05 
- 
-real 0m0.673s 
-user 0m0.020s 
-sys 0m0.000s 
-</code> 
- 
-That's all nice and fine, but if you want to know the total time to execute all three commands then you have to do some math.  Another method would be to run the three commands in a subshell, and the time the subshell: 
- 
-<code> 
-peek@catus:~$ time (ssh peek@alces01 "uptime" ; ssh peek@alces02 "uptime" ; ssh peek@alces03 "uptime") 
- 09:10:27 up 4 days, 18:12,  0 users,  load average: 0.12, 0.08, 0.06 
- 09:10:27 up 4 days, 18:32,  0 users,  load average: 0.00, 0.01, 0.05 
- 09:10:28 up 4 days, 19:20,  0 users,  load average: 0.04, 0.04, 0.05 
- 
-real 0m1.788s 
-user 0m0.056s 
-sys 0m0.004s 
-</code> 
- 
-===== Command Substitution ===== 
- 
-Command substitution allows the output of a command to replace the command name.  There are two forms: 
- 
-^ Old Form ^ New Form ^ 
-| <code> 
-`<commands>` 
-</code> 
-| <code> 
-$(<commands>) 
-</code> 
-| 
- 
-Why would you want to do this?  Earlier you saw how to send the output of one command to the input of another command with pipes.  But what if what you need is to take the output of one command and use it as a command line argument of another command? 
- 
-For example: 
-<code> 
-<command1> $(<command2> $(<command3>) ) 
-</code> 
- 
-This may not seem like much right now, but it becomes very powerful when you get into shell scripting.  Here's an example.  Don't worry if you don't understand what the code does, just bask in it as a glorious example: 
- 
-<code> 
-peek@catus:~$ list_of_user_shells=$(for uid in $(seq 108 110); do grep "^[^:]*:[^:]*:${uid}:" /etc/passwd ; done | awk -F: '{print $7}' | sort | uniq) 
-</code> 
- 
-What does this command do?  It searches through ''/etc/passwd'' searching for any user with a user ID number between 108 and 110 inclusively, then pulls from their user record what their login shell is, puts the login shells into a list, sorts the list, and then removes duplicate entries.  Here's a breakdown: 
- 
-^ Command ^ Description ^ 
-| <code>seq 108 110</code> | This command prints out all integers between the two integers listed on it's command line arguments, inclusively.  Ex: <code>$ seq 108 110 
-108 
-109 
-110 
-</code> | 
-| <code>for uid in $(seq 108 110); do ... ; done</code> | This command reads in the integers output by the ''seq'' command and loops over each one, assigning each number to the variable ''uid'' and then executing the commands between ''do'' and ''done'' For Ex: <code>$ for uid in $(seq 108 110); do echo "PROCESSING UID: ${uid}" ; done 
-PROCESSING UID: 108 
-PROCESSING UID: 109 
-PROCESSING UID: 110 
-</code> | 
-====== Exit Codes ====== 
- 
-Whenever a program exits it returns an exit code to the shell.  An exit code of 0 means that the program exited normally.  A non-zero exit code means that an error occurred.  This is useful information for building conditional commands that may change behavior depending on what errors arise.  For instance, the ''make'' command will execute a list of commands in a file named ''Makefile'', and exit the first time it encounters an error.  Makefiles are often used to generate programs and content.  But for now, it's sufficient for you to know that exit codes exist and that they are useful. 
  
 ====== File Names, Directory Names, and Wildcards ====== ====== File Names, Directory Names, and Wildcards ======
Line 678: Line 435:
   * There is a special command called ''nohup'' that will intercede on your program's behalf and stop the HUP signal from reaching the child process.  If your command would be ''<command> <options>...'', then you would use nohup by putting it at the beginning of your command line: ''nohup <command> <options>...'' The child program should not expect any keyboard input from the user.  If it does, then your program won't get any input once you log out.  The nohup program will create a file in the current directory called ''nohup.out'' that contains the output from your program.  You can look at this file later to see if your program looks like it's working correctly or not.   * There is a special command called ''nohup'' that will intercede on your program's behalf and stop the HUP signal from reaching the child process.  If your command would be ''<command> <options>...'', then you would use nohup by putting it at the beginning of your command line: ''nohup <command> <options>...'' The child program should not expect any keyboard input from the user.  If it does, then your program won't get any input once you log out.  The nohup program will create a file in the current directory called ''nohup.out'' that contains the output from your program.  You can look at this file later to see if your program looks like it's working correctly or not.
   * There is another, more complex program called ''screen'' that creates a fake terminal in which your program runs.  The nice thing about screen is that (a) your program can be interactive and expect input from the user, and (b) you can disconnect and reconnect to screen as many times as you want.  The ability to reconnect to screen makes it a favorite of mine, as it allows me to start a command, leave for the day, and then later log in remotely from home to check on how things are going.  Screen can also be used to create more fake terminals on the fly, and allow the user to switch between them at will.   * There is another, more complex program called ''screen'' that creates a fake terminal in which your program runs.  The nice thing about screen is that (a) your program can be interactive and expect input from the user, and (b) you can disconnect and reconnect to screen as many times as you want.  The ability to reconnect to screen makes it a favorite of mine, as it allows me to start a command, leave for the day, and then later log in remotely from home to check on how things are going.  Screen can also be used to create more fake terminals on the fly, and allow the user to switch between them at will.
 +
 +====== Playing Around ======
 +
 +Now that you have a few commands to play with, let's play with them:
 +
 +^ Type: | <code>$ mkdir /tmp/playground</code> |
 +^ Type: | <code>$ find /tmp/playground
 +/tmp/playground</code> |
 +^ Type: | <code>$ cd /tmp/playground</code> |
 +^ Type: | <code>pwd
 +/tmp/playground</code> |
 +^ Type: | <code>$ mkdir dir1 dir2</code> |
 +^ Type: | <code>$ cp /etc/passwd .</code> |
 +^ Type: | <code>$ find
 +.
 +./passwd
 +./dir1
 +./dir2
 +</code> |
 +^ Type: | <code>$ mv passwd fun</code> |
 +^ Type: | <code>$ find
 +.
 +./dir1
 +./dir2
 +./fun
 +</code> |
 +^ Type: | <code>$ mv fun dir1</code> |
 +^ Type: | <code>$ find
 +.
 +./dir1
 +./dir1/fun
 +./dir2
 +</code> |
 +^ Type: | <code>$ mv dir1 dir2</code> |
 +^ Type: | <code>$ find
 +.
 +./dir2
 +./dir2/dir1
 +./dir2/dir1/fun
 +</code> |
 +^ Type: | <code>$ cd</code> |
 +^ Type: | <code>$ rmdir /tmp/playground
 +rmdir: failed to remove '/tmp/playground': Directory not empty
 +</code> |
 +^ Type: | <code>$ rm /tmp/playground/dir2/dir1/fun</code> |
 +^ Type: | <code>$ find /tmp/playground
 +/tmp/playground
 +/tmp/playground/dir2
 +/tmp/playground/dir2/dir1
 +</code> |
 +^ Type: | <code>$ rmdir /tmp/playground/dir2/dir1</code> |
 +^ Type: | <code>$ rmdir /tmp/playground/dir2</code> |
 +^ Type: | <code>$ rmdir /tmp/playground</code> |
 +
unix_101.1464183892.txt.gz ยท Last modified: 2016/05/25 13:44 by peek