OnWorks Linux and Windows Online WorkStations

Logo

Free Hosting Online for WorkStations

< Previous | Contents | Next >

Group Commands And Subshells

bash allows commands to be grouped together. This can be done in one of two ways; ei- ther with a group command or with a subshell. Here are examples of the syntax of each:

Group command:

{ command1; command2; [command3; ...] }

Subshell:

(command1; command2; [command3;...])

The two forms differ in that a group command surrounds its commands with braces and a subshell uses parentheses. It is important to note that, due to the way bash implements group commands, the braces must be separated from the commands by a space and the last command must be terminated with either a semicolon or a newline prior to the clos- ing brace.

So what are group commands and subshells good for? While they have an important dif- ference (which we will get to in a moment), they are both used to manage redirection. Let’s consider a script segment that performs redirections on multiple commands:



ls -l > output.txt

echo "Listing of foo.txt" >> output.txt cat foo.txt >> output.txt

ls -l > output.txt

echo "Listing of foo.txt" >> output.txt cat foo.txt >> output.txt


This is pretty straightforward. Three commands with their output redirected to a file named output.txt. Using a group command, we could code this as follows:


{ ls -l; echo "Listing of foo.txt"; cat foo.txt; } > output.txt

{ ls -l; echo "Listing of foo.txt"; cat foo.txt; } > output.txt


Using a subshell is similar:



(ls -l; echo "Listing of foo.txt"; cat foo.txt) > output.txt

(ls -l; echo "Listing of foo.txt"; cat foo.txt) > output.txt


Using this technique we have saved ourselves some typing, but where a group command or subshell really shines is with pipelines. When constructing a pipeline of commands, it is often useful to combine the results of several commands into a single stream. Group commands and subshells make this easy:



{ ls -l; echo "Listing of foo.txt"; cat foo.txt; } | lpr

{ ls -l; echo "Listing of foo.txt"; cat foo.txt; } | lpr


Here we have combined the output of our three commands and piped them into the input of lpr to produce a printed report.

In the script that follows, we will use groups commands and look at several programming techniques that can be employed in conjunction with associative arrays. This script, called array-2, when given the name of a directory, prints a listing of the files in the directory along with the names of the file's owner and group owner. At the end of the listing, the script prints a tally of the number of files belonging to each owner and group. Here we see the results (condensed for brevity) when the script is given the directory

/usr/bin:


[me@linuxbox ~]$ array-2 /usr/bin

/usr/bin/2to3-2.6

root

root

/usr/bin/2to3

root

root

/usr/bin/a2p

root

root

/usr/bin/abrowser

root

root

/usr/bin/aconnect

root

root

/usr/bin/acpi_fakekey

root

root

/usr/bin/acpi_listen

root

root

/usr/bin/add-apt-repository

root

root

.

.

.

/usr/bin/zipgrep

root

root

/usr/bin/zipinfo

root

root

/usr/bin/zipnote

root

root

/usr/bin/zip

root

root


/usr/bin/zipsplit

/usr/bin/zjsdecode

/usr/bin/zsoelim

root root root

root root root

File owners: daemon : 1


file(s)

root : 1394

file(s)

image

File group owners: crontab : 1 file(s) daemon : 1 file(s) lpadmin : 1 file(s) mail : 4 file(s) mlocate : 1 file(s) root : 1380 file(s) shadow : 2 file(s) ssh : 1 file(s)

tty : 2 file(s)

utmp : 2 file(s)


image

#!/bin/bash

#!/bin/bash

# array-2: Use arrays to tally file owners

declare -A files file_group file_owner groups owners if [[ ! -d "$1" ]]; then

echo "Usage: array-2 dir" >&2 exit 1

fi


for i in "$1"/*; do owner=$(stat -c %U "$i") group=$(stat -c %G "$i") files["$i"]="$i" file_owner["$i"]=$owner file_group["$i"]=$group ((++owners[$owner])) ((++groups[$group]))

done


# List the collected files

{ for i in "${files[@]}"; do printf "%-40s %-10s %-10s\n" \

"$i" ${file_owner["$i"]} ${file_group["$i"]} done } | sort

# array-2: Use arrays to tally file owners

declare -A files file_group file_owner groups owners if [[ ! -d "$1" ]]; then

echo "Usage: array-2 dir" >&2 exit 1

fi


for i in "$1"/*; do owner=$(stat -c %U "$i") group=$(stat -c %G "$i") files["$i"]="$i" file_owner["$i"]=$owner file_group["$i"]=$group ((++owners[$owner])) ((++groups[$group]))

done


# List the collected files

{ for i in "${files[@]}"; do printf "%-40s %-10s %-10s\n" \

"$i" ${file_owner["$i"]} ${file_group["$i"]} done } | sort

Here is a listing (with line numbers) of the script:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26


27

28

29

30

31

32

33

34

35

36

37

38

39

40

27

28

29

30

31

32

33

34

35

36

37

38

39

40


image

echo

echo

# List owners

echo "File owners:"

{ for i in "${!owners[@]}"; do

printf "%-10s: %5d file(s)\n" "$i" ${owners["$i"]} done } | sort

echo


# List groups

echo "File group owners:"

{ for i in "${!groups[@]}"; do

printf "%-10s: %5d file(s)\n" "$i" ${groups["$i"]} done } | sort

# List owners

echo "File owners:"

{ for i in "${!owners[@]}"; do

printf "%-10s: %5d file(s)\n" "$i" ${owners["$i"]} done } | sort

echo


# List groups

echo "File group owners:"

{ for i in "${!groups[@]}"; do

printf "%-10s: %5d file(s)\n" "$i" ${groups["$i"]} done } | sort

Let's take a look at the mechanics of this script:

Line 5: Associative arrays must be created with the declare command using the -A

option. In this script we create five arrays as follows:

files contains the names of the files in the directory, indexed by filename file_group contains the group owner of each file, indexed by filename file_owner contains the owner of each file, indexed by file name groups contains the number of files belonging to the indexed group owners contains the number of files belonging to the indexed owner

Lines 7-10: Checks to see that a valid directory name was passed as a positional parame- ter. If not, a usage message is displayed and the script exits with an exit status of 1.

Lines 12-20: Loop through the files in the directory. Using the stat command, lines 13 and 14 extract the names of the file owner and group owner and assign the values to their respective arrays (lines 16, 17) using the name of the file as the array index. Like- wise the file name itself is assigned to the files array (line 15).

Lines 18-19: The total number of files belonging to the file owner and group owner are incremented by one.

Lines 22-27: The list of files is output. This is done using the "${array[@]}" parameter expansion which expands into the entire list of array elements with each element treated as a separate word. This allows for the possibility that a file name may contain embedded spaces. Also note that the entire loop is enclosed in braces thus forming a group com- mand. This permits the entire output of the loop to be piped into the sort command. This is necessary because the expansion of the array elements is not sorted.

Lines 29-40: These two loops are similar to the file list loop except that they use the "${!


array[@]}" expansion which expands into the list of array indexes rather than the list of array elements.


 

Top OS Cloud Computing at OnWorks: