Process Input Line by Line
1#!/bin/bash
2input="path/to/file"
3while IFS= read -r line
4do
5 # process each read-in line
6 echo "$line"
7done < "$input" # NORICE HERE
Pay attention to the < part following done on the last line. It redirects the $input file to stdin of read process, making read reads the file line by line and then do the processing in the loop body. As for the preceding IFS=, there is a section
explains it in this post.
References: Linux/UNIX: Bash Read a File Line By Line 1 on nixCraft .
shift Built-in
1if [ $# != 0 ]; then
2 while true; do
3 case "$1" in
4 --first-option)
5 $ARG="$1"
6 shift
7 ;;
8 --second-option)
9 $NEXT_ARG="$1"
10 shift
11 ;;
12 # --more-option)
13 # ...
14 esac
15 done
16fi
shift NUM shifts the positional parameters
($1, $2 etc) to the left by NUM. If unset, NUM is 1. Check the Bash manual
for more details.
Parameter Substitution
${var#Pattern},${var##Pattern}From the front,#matches the shortest part,##matches the longest part${var%Pattern},${var%%Pattern}From the back,%matches the shortest part,%%matches the longest part
Conditional Expression
For strings:
[ STRING == STRING ]True if two strings are equal. **"=" may be used instead of “==” for strict POSIX compliance.**[ -z STRING ]True if the string is empty[ -n STRING ]or[ STRING ]True if the string is non-empty For files:[ -e FILE ]Exists.[ -f FILE ]Regular file.
: Built-in
It’s a Bourne Shell’s built-in and works as true (with some minor differences stated here
). Example:
1:> file # simply create a file and nothing else
References:
${BASH_SOURCE[0]} Variable
BASH_SOURCE is a built-in array variable that stores the script path of functions on the call stack, and ${BASH_SOURCE[0]} is the script path of the top of the stack, i.e. the script of the current executing code.
It’s similar to $0 but with the following difference. For a script called foo:
1#!/bin/bash
2echo "[$0] v.s. [$BASH_SOURCE]"
Run the following commands:
1$ bash ./foo
2[./foo] v.s. [./foo]
3$ ./foo
4[./foo] v.s. [./foo]
5$ . ./foo
6[bash] v.s. [./foo]
In other words, it works in both sourcing and executing scenarios. Last but not least, it’s a Bash variable and not POSIX-compliant.
References:
Sourcing or Executing a script?
As this answer states:
Sourcing a script will run the commands in the current shell process.
Executing a script will run the commands in a new shell process.
1# Executing
2./my_script
3my_script
4# Sourcing
5source my_script
6. my_script
IFS Variable
$IFS, which stands for Internal Field Separator, is a special shell variable that specifies delimiters to split strings into words in loops2:
1IFS=","
2INPUT="a,b,c,d"
3for field in ${INPUT}
4do
5 # ...
6done
Or with the read built-in command:
1cat > file << "EOF"
2big_machine|202.54.1.1|/home/alice|Alice
3small_machine|202.54.1.2|/home/bob|Bob
4EOF
5
6IFS='|'
7while read -r hostname ip homedir username
8do
9 # ...
10done < file
More information on:
- this example of RIP Tutorial
- this wiki page of Linux Shell Scripting Tutorial
set Built-in
As mentioned in the Bash Manual :
This built-in is so complicated that it deserves its own section.
set is a versatile Bash built-in command having multiple functions. Therefore we only list those that are encountered before.
set -/+<opts>turns on/off shell options-emake Bash stop executing the script if a command in the script returns an error.
set -- [args]sets the positional parameters toargs. More example here .
Copy All (including hidden) Files in a Directory
As this answer suggests:
1mkdir /home/<new_user>
2cp -r /etc/skel/. /home/<new_user>
3# ^ the dot here makes things work
Change Password in Script
Use chpasswd. In its manual:
NAME
chpasswd- update passwords in batch mode……
DESCRIPTION The
chpasswdcommand reads a list of user name and password pairs from standard input and uses this information to update a group of existing users. Each line is of the format:1user_name:password
Example of using it:
1echo 'user_name:password' | chpasswd
List Files with Full Path
As recommended by this post :
1ls -d $PWD/*
And another option by this answer :
1find -type f -maxdepth 1 "$(PWD)"
I personally prefer the first one, which I think is simpler and more straightforward.
“find, and then -exec”
There is a option -exec of the find command, which specifies the action to take for those files that are found out by the command. Examples:
1find ./ -type f -exec chmod 644 {} \;
2
3find . -exec /bin/rm {} \; # useful when you can't do `rm *` because there are too many files and Bash can't manage their names
The semicolon ; following -exec COMMAND is to separate the command body with other arguments of find3. Note that Bash takes ; as a list operator
, so in order to keep it as a literal and pass it to find, we need to escape it to remove its special meaning to Bash (example provided by this answer
):
1find . -exec echo {} \;
2find . -exec echo {} ';'
3find . -exec echo {} ";"
4find . -exec echo {} \+
5find . -exec echo {} +
Check this post for more examples.
Bash Print with Color
1function __print() {
2 #color
3 local COLOR_NONE="\e[0m"
4 local COLOR_RED="\e[0;31m"
5 local COLOR_GREEN="\e[0;32m"
6 local COLOR_YELLOW="\e[0;33m"
7
8 #debug level
9 local INFO=1
10 local ERR=2
11 local STEP=3
12
13 color=$COLOR_NONE
14 if [ $1 == $INFO ];then
15 color=$COLOR_GREEN
16 elif [ $1 == $ERR ];then
17 color=$COLOR_RED
18 elif [ $1 == $STEP ];then
19 color=$COLOR_YELLOW
20 fi
21
22 echo -e $color"$2"$COLOR_NONE
23}
-
In the article they mention some bash-scripting concepts like process substitution (Linux Journal ), here strings and here documents . Check out those links for detailed information. ↩︎
-
More precisely, in all scenarios involving word splitting . Check this for more examples. ↩︎
-
Character
+could share the similar function with;. Seefind’s manual to see their difference. ↩︎