Bash's 24 traps

Source: Internet
Author: User
Tags first string truncated
1. For I in 'ls *. mp3'

Common Mistakes:

for i in `ls *.mp3`; do     # Wrong!

Why is it wrong? Because the for... in statement is segmented by space, the file name containing space is split into multiple words. If you encounter 01-Don't eat the yellow snow.mp3, the I values will be 01,-, don't, and so on.

Double quotation marks do not work either. It treats all the results of LS *. MP3 as one word.

for i in "`ls *.mp3`"; do   # Wrong!

Which of the following statements is true?

for i in *.mp3; do
2. CP $ File $ Target

This sentence is basically correct, but there is also a space tokenization problem. Therefore, double quotation marks should be used:

cp "$file" "$target"

However, if the file name starts with-, the file name will be processed by CP as a command line option, which is still a headache. You can try the following.

cp -- "$file" "$target"

If you are lucky enough to touch another system that does not support the option, you can only use the following method: Make every variable start with a directory.

for i in ./*.mp3; do  cp "$i" /target  ...
3. [$ Foo = "bar"]

When $ foo is empty, the above command becomes

[ = "bar" ]

Similarly, when the $ Foo package contains spaces:

[ multiple words here = "bar" ]

Both errors occur. So double quotation marks should be used to enclose variables:

["$ Foo" = bar] # It's almost perfect.

But! When $ Foo starts with "-", the problem persists. In the newer Bash, you can use the following method instead. [[the keyword can correctly handle blank spaces, spaces, strip, and other issues.

[[$ Foo = bar] # correct

This technique can be used in earlier versions of bash (though hard to understand ):

[X "$ foo" = xbar] # correct

Or simply put the variable on the right, because [the right side of the equal sign of the command can still work even if it is blank or starting with a horizontal line. (Similar practices are also available in the Java programming style, though for different purposes .)

[Bar = "$ foo"] # correct
4. CD 'dirname "$ F "'

There are also space issues. Add quotation marks.

cd "`dirname "$f"`"

The question is, is it wrong? Because of the nested double quotation marks, you will think that 'dirname is the first string, 'is the second string. It is a C language. In bash, the double quotation marks in the Command Replace (the content in the reverse quotation mark '') will be correctly matched together, so you do not need to convert them into meanings.

The $ () syntax is the same, and the following statements are correct.

cd "$(dirname "$f")"
5. ["$ foo" = Bar & "$ bar" = Foo]

[Cannot use the & symbol! Because [the essence is the test command, & will divide this line into two commands. Use the following statement.

[ bar = "$foo" -a foo = "$bar" ]       # Right![ bar = "$foo" ] && [ foo = "$bar" ]   # Also right![[ $foo = bar && $bar = foo ]]         # Also right!
6. [[$ Foo> 7]

Unfortunately, [this parameter is only applicable to strings and cannot be compared with numbers. The number comparison should be written as follows:

(( $foo > 7 ))

Or use the classic style:

[ $foo -gt 7 ]

However, there is a problem with using-GT, that is, an error occurs when $ foo is not a number. You must perform a type test.

This can also be written.

[[ $foo -gt 7 ]]
7. grep Foo bar | while read line; do (count ++); done

Due to format issues, I added a space to the title. The actual code should be like this:

Grep Foo bar | while read line; do (count ++); done # error!

This line of code counts the number of lines in the bar file that contain Foo, although very troublesome (equivalent to grep-C Foo bar or grep Foo bar | WC-l ). At first glance, there is no problem, but after execution, the Count variable has no value. Because every command in the pipeline is put into a new sub-shell for execution, the Count variable defined in the sub-shell cannot be passed out.

8. If [grep Foo myfile]

A common mistake for beginners is to treat the [after the if statement as part of the IF syntax. In fact, it is a command, equivalent to the test command, rather than the if syntax. C programmers should pay special attention to this.

If considers the return values of all commands between If and then as the judgment conditions. Therefore, the preceding statement should be written

if grep foo myfile > /dev/null; then
9. If [bar = "$ foo"]

Similarly, [is a command, not part of the IF statement, so pay attention to spaces.

if [ bar = "$foo" ]
10. If [[A = B] & [c = D]

The same problem is that [it is not part of the IF statement, and of course it is not the parentheses that change the logic judgment. It is a command. Maybe C programmers are more likely to make this mistake?

If [A = B] & [c = D] # correct
11. Cat file | sed S/Foo/BAR/> File

YouNoRead and Write a file in the same pipeline at the same time. According to the pipeline implementation method, the file is either truncated to 0 bytes, or will grow infinitely until the entire hard disk is filled up. If you want to change the content of the original file, you can only write the output to the temporary file before using the MV command.

sed ‘s/foo/bar/g‘ file > tmpfile && mv tmpfile file
12. Echo $ foo

What error codes does this sentence have? This is generally true, but the following example shows a problem.

MSG = "Please enter a file name of the form *. Zip" Echo $ MSG # error!

If a zip file exists in the current directory, it is displayed

Please enter a file name of the form freenfss.zip lw35nfss.zip

So even ECHO should not forget to quote the variable.

13. $ Foo = bar

You do not need to add the $ symbol to the variable value-this is not Perl or PHP.

14. Foo = bar

When a variable is assigned a value, no space is allowed on both sides of the equal sign-this is not a C language.

15. Echo <EOF

Here document is a good thing. It can output text in segments without quotation marks or line breaks. However, cat instead of ECHO should be used for output of here document.

# This is wrong:echo <<EOFHello worldEOF# This is right:cat <<EOFHello worldEOF
16. Su-C 'some command'

In the original article, this is basically correct, but the user's goal is to pass-C 'some command' to shell. Su has a-c parameter, so su will only pass 'some command' to shell. So it should be written as follows:

su root -c ‘some command‘

But on my platform, the result of Man Su's explanation of-C is:

-c, --commmand=COMMAND            pass a single COMMAND to the shell with -c

That is to say,-C 'some command' will also pass a string like-C 'some command' to shell, which does not match this one. In any case, write this article here first.

17. CD/Foo; Bar

CD may cause errors. After an error occurs, the bar command will be executed in a directory you cannot predict. Therefore, remember to judge the CD return value.

cd /foo && bar

If you want to execute multiple commands Based on the CD return value, you can use |.

cd /foo || exit 1;barbaz

For more information about directories, let's say that you want to frequently change the working directory in Shell programs, as shown in the following code:

find ... -type d | while read subdir; do  cd "$subdir" && whatever && ... && cd -done

It is better to write as follows:

find ... -type d | while read subdir; do  (cd "$subdir" && whatever && ...)done

Parentheses force a sub-shell, so that changing the working directory in this sub-shell will not affect the parent shell (the shell that executes this script), you can save the trouble of Cd.

You can also use commands such as pushd, popd, and dirs to control the working directory flexibly.

18. [bar = "$ foo"]

[= Cannot be used in the command. It should be written

[ bar = "$foo" ] && echo yes[[ bar == $foo ]] && echo yes
19. For I in {1... 10}; do./something &; done

& It should not be placed later; Because & has played the role of the statement separator and does not need to be used again ;.

for i in {1..10}; do ./something & done
20. cmd1 & cmd2 | cmd3

Some people like to use this format to replace the IF... then... else structure, but it is not exactly the same. If cmd2 returns a non-true value, cmd3 is executed. Therefore, it is better to honestly use if cmd1; then cmd2; else cmd3.

21. Bom (byte-order marks) Problem for UTF-8

UTF-8 encoding can contain several bytes at the beginning of a file to indicate the encoding sequence. These bytes are called Bom. However, Bom is not required for UTF-8 encoding in UNIX format. Excessive BOM may affect shell parsing, especially #! Commands such as/bin/sh cannot be identified.

MS-DOS format line feed (CRLF) also has the same problem. If you save the shell program in DOS format, the script cannot be executed.

$ ./dos-bash: ./dos: /bin/sh^M: bad interpreter: No such file or directory
22. Echo "Hello world! "

Interactive execution of this command produces the following errors:

-bash: !": event not found

Because! "Is processed as a replacement symbol of the command line history. But there is no such problem in shell scripts.

Unfortunately, you cannot use escape characters to escape! :

$ echo "hi\!"hi\!

Solution: use single quotes, that is

$ echo ‘Hello, world!‘

If you must use double quotation marks, you can use set + H to cancel the replacement of command line history.

set +Hecho "Hello, world!"
23. For ARG in $ *

$ * Indicates all command line parameters, so you may want to write them one by one to process the parameters one by one, but it will fail if the parameters contain spaces. For example:

#!/bin/bash# Incorrect versionfor x in $*; do  echo "parameter: ‘$x‘"done$ ./myscript ‘arg 1‘ arg2 arg3parameter: ‘arg‘parameter: ‘1‘parameter: ‘arg2‘parameter: ‘arg3‘

The correct method is to use "[email protected]".

#!/bin/bash# Correct versionfor x in "[email protected]"; do  echo "parameter: ‘$x‘"done$ ./myscript ‘arg 1‘ arg2 arg3parameter: ‘arg 1‘parameter: ‘arg2‘parameter: ‘arg3‘

$ * And [email protected] are described in the bash manual as follows:

*    Expands to the positional parameters, starting from one.       When the expansion occurs within double quotes, it      expands to a single word with the value of each parameter      separated by the first character of the IFS special variable.       That is, "$*" is equivalent to "$1c$2c...",@    Expands to the positional parameters, starting from one.      When the expansion occurs within double quotes, each      parameter expands to a separate word.  That  is,  "[email protected]"       is equivalent to "$1" "$2" ...

It can be seen that $ * is the same as [email protected] without quotation marks, but "$ *" is extended into a string, "[email protected]" is extended to every parameter.

24. Function Foo ()

There is no problem in bash, but other shells may have errors. Do not use the function with parentheses. The safest way is to use parentheses, that is

foo() {  ...}

Original post:

Http://tech.idv2.com/2008/01/09/bash-pitfalls/

Thanks to fcicq, his new 30 days series have brought us many good articles.

What I want to analyze today is Bash pitfalls, which introduces some typical errors in bash programming. Fcicq may not be suitable for beginners, but I think it is just for beginners of bash programming to read this article well.

Analyze the errors mentioned in this article one by one. If it is not completely translated, it will be skipped if it is useless, and some comments will be added in some places.

1. For I in 'ls *. mp3'

Common Mistakes:

for i in `ls *.mp3`; do     # Wrong!

Why? Because the for... in statement is segmented by space, the file name containing space will be split into multiple words. If you encounter 01-Don't eat the yellow snow.mp3, the I values will be 01,-, don't, and so on.

Double quotation marks do not work either. It treats all the results of LS *. MP3 as one word.

for i in "`ls *.mp3`"; do   # Wrong!

The correct statement is:

for i in *.mp3; do
2. CP $ File $ Target

This sentence is basically correct, but there is also a space tokenization problem. Therefore, double quotation marks should be used:

cp "$file" "$target"

However, if the file name starts with-, the file name will be processed by CP as a command line option, which is still a headache. You can try the following.

cp -- "$file" "$target"

If you encounter a system that does not support the -- Option option, you can only use the following method: Make each variable start with a directory.

for i in ./*.mp3; do  cp "$i" /target  ...
3. [$ Foo = "bar"]

When $ foo is empty, the above command becomes

[ = "bar" ]

Class. When $ Foo contains spaces:

[ multiple words here = "bar" ]

Both errors occur. So double quotation marks should be used to enclose variables:

["$ Foo" = bar] # It's almost perfect.

But! When $ Foo starts with "-", the problem persists. In the newer Bash, you can use the following method instead. [[the keyword can correctly handle blank spaces, spaces, strip, and other issues.

[[$ Foo = bar] # correct

This technique can be used in the old version of bash (though hard to understand ):

[X "$ foo" = xbar] # correct

Or simply put the variable on the right, because [the right side of the equal sign of the command can still work even if it is blank or starting with a horizontal line. (Similar practices are also available in the Java programming style, though for different purposes .)

[Bar = "$ foo"] # correct
4. CD 'dirname "$ F "'

There are also space issues. Add quotation marks.

cd "`dirname "$f"`"

Question: Is it wrong? Because of the nested double quotation marks, you will think that 'dirname is the first string, 'is the second string. It is a C language. In bash, the double quotation marks in the Command Replace (the content in the reverse quotation mark '') will be correctly matched together, so you do not need to convert them into meanings.

The $ () syntax is the same, and the following statements are correct.

cd "$(dirname "$f")"
5. ["$ foo" = Bar & "$ bar" = Foo]

[Cannot use the & symbol! Because [the essence is the test command, & will divide this line into two commands. Use the following statement.

[ bar = "$foo" -a foo = "$bar" ]       # Right![ bar = "$foo" ] && [ foo = "$bar" ]   # Also right![[ $foo = bar && $bar = foo ]]         # Also right!
6. [[$ Foo> 7]

Unfortunately, [this parameter is only applicable to strings and cannot be compared with numbers. The number comparison should be written as follows:

(( $foo > 7 ))

Or, in the classic format:

[ $foo -gt 7 ]

However, there is a problem with using-GT, that is, an error occurs when $ foo is not a number. You must perform a type test.

This can also be written.

[[ $foo -gt 7 ]]
7. grep Foo bar | while read line; do (count ++); done

Due to format issues, I added a space to the title. The actual code should be like this:

Grep Foo bar | while read line; do (count ++); done # error!

This line of code counts the number of lines in the bar file that contain Foo, although very troublesome (equivalent to grep-C Foo bar or grep Foo bar | WC-l ). At first glance, there is no problem, but after execution, the Count variable has no value. Because every command in the pipeline is put into a new sub-shell for execution, the Count variable defined in the sub-shell cannot be passed out.

8. If [grep Foo myfile]

A common mistake for beginners is to treat the [after the if statement as part of the IF syntax. In fact, it is a command, equivalent to the test command, rather than the if syntax. C programmers should pay special attention to this.

If considers the return values of all commands between If and then as the judgment conditions. Therefore, the preceding statement should be written

if grep foo myfile > /dev/null; then
9. If [bar = "$ foo"]

Similarly, [is a command, not part of the IF statement, so pay attention to spaces.

if [ bar = "$foo" ]
10. If [[A = B] & [c = D]

The same problem is that [it is not part of the IF statement, and of course it is not the parentheses that change the logic judgment. It is a command. Maybe C programmers are more likely to make this mistake?

If [A = B] & [c = D] # correct
11. Cat file | sed S/Foo/BAR/> File

YouNoRead and Write a file simultaneously in the same MPs queue. According to the pipeline implementation method, the file is either truncated to 0 bytes, or will grow infinitely until the entire hard disk is filled up. If you want to change the content of the original file, you can only write the output to the temporary file before using the MV command.

sed ‘s/foo/bar/g‘ file > tmpfile && mv tmpfile file
12. Echo $ foo

What error codes does this sentence have? This is generally true, but the following example shows a problem.

MSG = "Please enter a file name of the form *. Zip" Echo $ MSG # error!

If the current directory contains a zip file, it will be displayed

Please enter a file name of the form freenfss.zip lw35nfss.zip

Do not enclose variables in quotation marks even for ECHO.

13. $ Foo = bar

You do not need to add the $ symbol when assigning values to variables-this is not Perl or PHP.

14. Foo = bar

When a variable is assigned a value, no space is allowed on both sides of the equal sign-this is not a C language.

15. Echo <EOF

Here document is a good thing. It can output text in segments without quotation marks or line breaks. However, cat instead of ECHO should be used for output of here document.

# This is wrong:echo <<EOFHello worldEOF# This is right:cat <<EOFHello worldEOF
16. Su-C 'some command'

In the original article, this is basically correct, but the user's goal is to pass-C 'some command' to shell. Su has a-c parameter, so su will only pass 'some command' to shell. So it should be written as follows:

su root -c ‘some command‘

But on my platform, the result of Man Su's explanation of-C is:

-c, --commmand=COMMAND            pass a single COMMAND to the shell with -c

That is to say,-C 'some command' will also pass a string like-C 'some command' to shell, which does not match this one. In any case, write this article here first.

17. CD/Foo; Bar

CD may cause errors. After an error occurs, the bar command will be executed in a directory you cannot predict. Therefore, remember to judge the CD return value.

cd /foo && bar

If you want to execute multiple commands Based on the CD return value, you can use |.

cd /foo || exit 1;barbaz

For more information about directories, let's say that you want to frequently change the working directory in Shell programs, as shown in the following code:

find ... -type d | while read subdir; do  cd "$subdir" && whatever && ... && cd -done

Do not write as follows:

find ... -type d | while read subdir; do  (cd "$subdir" && whatever && ...)done

A subshell is forced to start, so that changing the working directory in this subshell will not affect the parent shell (the shell that executes this script), saving the trouble of Cd.

You can also use commands such as pushd, popd, and dirs to control the working directory flexibly.

18. [bar = "$ foo"]

[= Cannot be used in the command. It should be written

[ bar = "$foo" ] && echo yes[[ bar == $foo ]] && echo yes
19. For I in {1... 10}; do./something &; done

& It should not be placed later; Because & has played the role of the statement separator and does not need to be used again ;.

for i in {1..10}; do ./something & done
20. cmd1 & cmd2 | cmd3

Some people like to use this format to replace the IF... then... else structure, but it is not exactly the same. If cmd2 returns a non-true value, cmd3 is executed. Therefore, it is better to honestly use if cmd1; then cmd2; else cmd3.

21. Bom (byte-order marks) Problem for UTF-8

UTF-8 encoding can start with a file with several bytes to represent the encoding byte order, these bytes are called Bom. However, Bom is not required for UTF-8 encoding in UNIX format. Excessive BOM may affect shell parsing, especially starting #! Commands such as/bin/sh cannot be identified.

MS-DOS format line feed (CRLF) also has the same problem. If you save the shell program in DOS format, the script cannot be executed.

$ ./dos-bash: ./dos: /bin/sh^M: bad interpreter: No such file or directory
22. Echo "Hello world! "

Interactive execution of this command produces the following errors:

-bash: !": event not found

Because! "Is processed as a replacement symbol of the command line history. But there is no such problem in shell scripts.

Unfortunately, you cannot use escape characters to escape! :

$ echo "hi\!"hi\!

One of the solutions is to use single quotes, that is

$ echo ‘Hello, world!‘

If you must use double quotation marks, you can use set + H to cancel the replacement of command line history.

set +Hecho "Hello, world!"
23. For ARG in $ *

$ * Indicates all command line parameters, so you may want to write them one by one to process the parameters one by one, but it will fail if the parameters contain spaces. For example:

#!/bin/bash# Incorrect versionfor x in $*; do  echo "parameter: ‘$x‘"done$ ./myscript ‘arg 1‘ arg2 arg3parameter: ‘arg‘parameter: ‘1‘parameter: ‘arg2‘parameter: ‘arg3‘

The correct method is to use "[email protected]".

#!/bin/bash# Correct versionfor x in "[email protected]"; do  echo "parameter: ‘$x‘"done$ ./myscript ‘arg 1‘ arg2 arg3parameter: ‘arg 1‘parameter: ‘arg2‘parameter: ‘arg3‘

$ * And [email protected] are described in the bash manual as follows:

*    Expands to the positional parameters, starting from one.       When the expansion occurs within double quotes, it      expands to a single word with the value of each parameter      separated by the first character of the IFS special variable.       That is, "$*" is equivalent to "$1c$2c...",@    Expands to the positional parameters, starting from one.      When the expansion occurs within double quotes, each      parameter expands to a separate word.  That  is,  "[email protected]"       is equivalent to "$1" "$2" ...

As you can see, when no quotation marks are added, $ * is the same as [email protected], but "$ *" is extended into a string, "[email protected]" is extended to every parameter.

24. Function Foo ()

There is no problem in bash, but other shells may have errors. Do not use the function with parentheses. The safest way is to use parentheses, that is

foo() {  ...}

Bash's 24 traps

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.